diff --git a/flecs.c b/flecs.c index 21bd09dac4..5ca203a1d9 100644 --- a/flecs.c +++ b/flecs.c @@ -35078,7 +35078,7 @@ int flecs_json_serialize_children_alerts( ecs_filter_t f = ECS_FILTER_INIT; ecs_filter(ECS_CONST_CAST(ecs_world_t*, world), { .storage = &f, - .terms = {{ ecs_pair(EcsChildOf, entity) }} + .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} }); ecs_iter_t it = ecs_filter_iter(world, &f); @@ -35138,6 +35138,63 @@ int flecs_json_serialize_alerts( return 0; } +static +int flecs_json_serialize_refs_idr( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_id_record_t *idr) +{ + char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); + + flecs_json_member(buf, id_str); + ecs_os_free(id_str); + + flecs_json_array_push(buf); + + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + int32_t i, count = ecs_table_count(table); + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + flecs_json_next(buf); + flecs_json_path(buf, world, e); + } + } + } + + flecs_json_array_pop(buf); + + return 0; +} + +static +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(relationship, entity)); + + if (idr) { + if (relationship == EcsWildcard) { + ecs_id_record_t *cur = idr; + while ((cur = cur->second.next)) { + flecs_json_serialize_refs_idr(world, buf, cur); + } + } else { + flecs_json_serialize_refs_idr(world, buf, idr); + } + } + + return 0; +} + int ecs_entity_to_json_buf( const ecs_world_t *world, ecs_entity_t entity, @@ -35231,6 +35288,15 @@ int ecs_entity_to_json_buf( } } + if (desc && desc->serialize_refs) { + flecs_json_memberl(buf, "refs"); + flecs_json_object_push(buf); + if (flecs_json_serialize_refs(world, buf, entity, desc->serialize_refs)) { + goto error; + } + flecs_json_object_pop(buf); + } + flecs_json_object_pop(buf); return 0; @@ -37725,6 +37791,7 @@ void flecs_rest_string_param( static void flecs_rest_parse_json_ser_entity_params( + ecs_world_t *world, ecs_entity_to_json_desc_t *desc, const ecs_http_request_t *req) { @@ -37739,6 +37806,12 @@ void flecs_rest_parse_json_ser_entity_params( flecs_rest_bool_param(req, "private", &desc->serialize_private); flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts); + + char *rel = NULL; + flecs_rest_string_param(req, "refs", &rel); + if (rel) { + desc->serialize_refs = ecs_lookup_fullpath(world, rel); + } } static @@ -37784,7 +37857,7 @@ bool flecs_rest_reply_entity( } ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - flecs_rest_parse_json_ser_entity_params(&desc, req); + flecs_rest_parse_json_ser_entity_params(world, &desc, req); if (ecs_entity_to_json_buf(world, e, &reply->body, &desc) != 0) { ecs_strbuf_reset(&reply->body); reply->code = 500; diff --git a/flecs.h b/flecs.h index db0e36c191..70c50eb603 100644 --- a/flecs.h +++ b/flecs.h @@ -12352,10 +12352,11 @@ typedef struct ecs_entity_to_json_desc_t { bool serialize_values; /**< Serialize component values */ bool serialize_type_info; /**< Serialize type info (requires serialize_values) */ bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ } ecs_entity_to_json_desc_t; #define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\ - false, false, false, false, false, true, false, false, false, false, false } + false, false, false, false, false, true, false, false, false, false, false, false } /** Serialize entity into JSON string. * This creates a JSON object with the entity's (path) name, which components diff --git a/include/flecs/addons/json.h b/include/flecs/addons/json.h index 0c4103c913..7052c93eb8 100644 --- a/include/flecs/addons/json.h +++ b/include/flecs/addons/json.h @@ -204,10 +204,11 @@ typedef struct ecs_entity_to_json_desc_t { bool serialize_values; /**< Serialize component values */ bool serialize_type_info; /**< Serialize type info (requires serialize_values) */ bool serialize_alerts; /**< Serialize active alerts for entity */ + ecs_entity_t serialize_refs; /**< Serialize references (incoming edges) for relationship */ } ecs_entity_to_json_desc_t; #define ECS_ENTITY_TO_JSON_INIT (ecs_entity_to_json_desc_t){true, false,\ - false, false, false, false, false, true, false, false, false, false, false } + false, false, false, false, false, true, false, false, false, false, false, false } /** Serialize entity into JSON string. * This creates a JSON object with the entity's (path) name, which components diff --git a/src/addons/json/serialize.c b/src/addons/json/serialize.c index 8dbd22c8a0..31d8057404 100644 --- a/src/addons/json/serialize.c +++ b/src/addons/json/serialize.c @@ -1019,7 +1019,7 @@ int flecs_json_serialize_children_alerts( ecs_filter_t f = ECS_FILTER_INIT; ecs_filter(ECS_CONST_CAST(ecs_world_t*, world), { .storage = &f, - .terms = {{ ecs_pair(EcsChildOf, entity) }} + .terms = {{ .id = ecs_pair(EcsChildOf, entity) }} }); ecs_iter_t it = ecs_filter_iter(world, &f); @@ -1079,6 +1079,63 @@ int flecs_json_serialize_alerts( return 0; } +static +int flecs_json_serialize_refs_idr( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_id_record_t *idr) +{ + char *id_str = ecs_id_str(world, ecs_pair_first(world, idr->id)); + + flecs_json_member(buf, id_str); + ecs_os_free(id_str); + + flecs_json_array_push(buf); + + ecs_table_cache_iter_t it; + if (idr && flecs_table_cache_all_iter((ecs_table_cache_t*)idr, &it)) { + const ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + int32_t i, count = ecs_table_count(table); + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + flecs_json_next(buf); + flecs_json_path(buf, world, e); + } + } + } + + flecs_json_array_pop(buf); + + return 0; +} + +static +int flecs_json_serialize_refs( + const ecs_world_t *world, + ecs_strbuf_t *buf, + ecs_entity_t entity, + ecs_entity_t relationship) +{ + ecs_id_record_t *idr = flecs_id_record_get(world, + ecs_pair(relationship, entity)); + + if (idr) { + if (relationship == EcsWildcard) { + ecs_id_record_t *cur = idr; + while ((cur = cur->second.next)) { + flecs_json_serialize_refs_idr(world, buf, cur); + } + } else { + flecs_json_serialize_refs_idr(world, buf, idr); + } + } + + return 0; +} + int ecs_entity_to_json_buf( const ecs_world_t *world, ecs_entity_t entity, @@ -1172,6 +1229,15 @@ int ecs_entity_to_json_buf( } } + if (desc && desc->serialize_refs) { + flecs_json_memberl(buf, "refs"); + flecs_json_object_push(buf); + if (flecs_json_serialize_refs(world, buf, entity, desc->serialize_refs)) { + goto error; + } + flecs_json_object_pop(buf); + } + flecs_json_object_pop(buf); return 0; diff --git a/src/addons/rest.c b/src/addons/rest.c index 2397131acb..2b79d927db 100644 --- a/src/addons/rest.c +++ b/src/addons/rest.c @@ -159,6 +159,7 @@ void flecs_rest_string_param( static void flecs_rest_parse_json_ser_entity_params( + ecs_world_t *world, ecs_entity_to_json_desc_t *desc, const ecs_http_request_t *req) { @@ -173,6 +174,12 @@ void flecs_rest_parse_json_ser_entity_params( flecs_rest_bool_param(req, "private", &desc->serialize_private); flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); flecs_rest_bool_param(req, "alerts", &desc->serialize_alerts); + + char *rel = NULL; + flecs_rest_string_param(req, "refs", &rel); + if (rel) { + desc->serialize_refs = ecs_lookup_fullpath(world, rel); + } } static @@ -218,7 +225,7 @@ bool flecs_rest_reply_entity( } ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; - flecs_rest_parse_json_ser_entity_params(&desc, req); + flecs_rest_parse_json_ser_entity_params(world, &desc, req); if (ecs_entity_to_json_buf(world, e, &reply->body, &desc) != 0) { ecs_strbuf_reset(&reply->body); reply->code = 500; diff --git a/test/meta/project.json b/test/meta/project.json index ec7cff94b5..b392ede1f1 100644 --- a/test/meta/project.json +++ b/test/meta/project.json @@ -711,6 +711,9 @@ "serialize_entity_w_2_alerts", "serialize_entity_w_child_alerts", "serialize_entity_w_severity_filter_alert", + "serialize_entity_refs_childof", + "serialize_entity_refs_custom", + "serialize_entity_refs_wildcard", "serialize_iterator_1_comps_empty", "serialize_iterator_1_comps_2_ents_same_table", "serialize_iterator_1_tag_2_ents_same_table", diff --git a/test/meta/src/SerializeToJson.c b/test/meta/src/SerializeToJson.c index e4a6fa1149..db472788f4 100644 --- a/test/meta/src/SerializeToJson.c +++ b/test/meta/src/SerializeToJson.c @@ -2504,6 +2504,117 @@ void SerializeToJson_serialize_entity_w_severity_filter_alert() { ecs_fini(world); } +void SerializeToJson_serialize_entity_refs_childof() { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + ecs_entity_t e1 = ecs_new_entity(world, "e1"); + + ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_add_pair(world, child_1, EcsChildOf, e1); + + ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_add_pair(world, child_2, EcsChildOf, e1); + + ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_add_pair(world, child_3, Rel, e1); + + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_refs = EcsChildOf; + char *json = ecs_entity_to_json(world, e1, &desc); + test_assert(json != NULL); + + test_str(json, "{" + "\"path\":\"e1\", " + "\"ids\":[], " + "\"refs\":{" + "\"ChildOf\":[" + "\"e1.child1\", " + "\"e1.child2\"" + "]" + "}" + "}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeToJson_serialize_entity_refs_custom() { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + ecs_entity_t e1 = ecs_new_entity(world, "e1"); + + ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_add_pair(world, child_1, Rel, e1); + + ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_add_pair(world, child_2, Rel, e1); + + ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_add_pair(world, child_3, EcsChildOf, e1); + + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_refs = Rel; + char *json = ecs_entity_to_json(world, e1, &desc); + test_assert(json != NULL); + + test_str(json, "{" + "\"path\":\"e1\", " + "\"ids\":[], " + "\"refs\":{" + "\"Rel\":[" + "\"child1\", " + "\"child2\"" + "]" + "}" + "}"); + ecs_os_free(json); + + ecs_fini(world); +} + +void SerializeToJson_serialize_entity_refs_wildcard() { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Rel); + + ecs_entity_t e1 = ecs_new_entity(world, "e1"); + + ecs_entity_t child_1 = ecs_new_entity(world, "child1"); + ecs_add_pair(world, child_1, Rel, e1); + + ecs_entity_t child_2 = ecs_new_entity(world, "child2"); + ecs_add_pair(world, child_2, Rel, e1); + + ecs_entity_t child_3 = ecs_new_entity(world, "child3"); + ecs_add_pair(world, child_3, EcsChildOf, e1); + + ecs_entity_to_json_desc_t desc = ECS_ENTITY_TO_JSON_INIT; + desc.serialize_refs = EcsWildcard; + char *json = ecs_entity_to_json(world, e1, &desc); + test_assert(json != NULL); + + test_str(json, "{" + "\"path\":\"e1\", " + "\"ids\":[], " + "\"refs\":{" + "\"ChildOf\":[" + "\"e1.child3\"" + "], " + "\"Rel\":[" + "\"child1\", " + "\"child2\"" + "]" + "}" + "}"); + ecs_os_free(json); + + ecs_fini(world); +} + void SerializeToJson_serialize_iterator_1_comps_empty() { ecs_world_t *world = ecs_init(); diff --git a/test/meta/src/main.c b/test/meta/src/main.c index e919cfdb1c..43700d81e8 100644 --- a/test/meta/src/main.c +++ b/test/meta/src/main.c @@ -680,6 +680,9 @@ void SerializeToJson_serialize_entity_w_1_alert(void); void SerializeToJson_serialize_entity_w_2_alerts(void); void SerializeToJson_serialize_entity_w_child_alerts(void); void SerializeToJson_serialize_entity_w_severity_filter_alert(void); +void SerializeToJson_serialize_entity_refs_childof(void); +void SerializeToJson_serialize_entity_refs_custom(void); +void SerializeToJson_serialize_entity_refs_wildcard(void); void SerializeToJson_serialize_iterator_1_comps_empty(void); void SerializeToJson_serialize_iterator_1_comps_2_ents_same_table(void); void SerializeToJson_serialize_iterator_1_tag_2_ents_same_table(void); @@ -3573,6 +3576,18 @@ bake_test_case SerializeToJson_testcases[] = { "serialize_entity_w_severity_filter_alert", SerializeToJson_serialize_entity_w_severity_filter_alert }, + { + "serialize_entity_refs_childof", + SerializeToJson_serialize_entity_refs_childof + }, + { + "serialize_entity_refs_custom", + SerializeToJson_serialize_entity_refs_custom + }, + { + "serialize_entity_refs_wildcard", + SerializeToJson_serialize_entity_refs_wildcard + }, { "serialize_iterator_1_comps_empty", SerializeToJson_serialize_iterator_1_comps_empty @@ -4734,7 +4749,7 @@ static bake_test_suite suites[] = { "SerializeToJson", NULL, NULL, - 136, + 139, SerializeToJson_testcases }, {