From 32e812cdd933b55357a107c1ad14c61e1df7bd20 Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Sun, 11 Jun 2023 17:36:56 -0700 Subject: [PATCH] Add Info, Warning and Error severity kinds to alerts --- flecs.c | 270 ++---------------- flecs.h | 47 ++- include/flecs/addons/alerts.h | 12 + .../addons/cpp/mixins/alerts/builder_i.hpp | 18 ++ .../flecs/addons/cpp/mixins/alerts/decl.hpp | 12 +- .../flecs/addons/cpp/mixins/alerts/impl.hpp | 5 + src/addons/alerts.c | 16 +- src/addons/json/serialize.c | 7 + src/world.c | 4 + test/addons/project.json | 5 +- test/addons/src/Alerts.c | 57 ++++ test/addons/src/main.c | 17 +- test/cpp_api/project.json | 6 +- test/cpp_api/src/Misc.cpp | 81 +++++- test/cpp_api/src/main.cpp | 22 +- test/meta/src/SerializeToJson.c | 13 +- 16 files changed, 319 insertions(+), 273 deletions(-) diff --git a/flecs.c b/flecs.c index 2a4407905b..31b3e9ddd2 100644 --- a/flecs.c +++ b/flecs.c @@ -2040,210 +2040,6 @@ void flecs_resume_readonly( #endif -/** - * @file datastructures/qsort.h - * @brief Quicksort implementation. - */ - -/* From: https://github.com/svpv/qsort/blob/master/qsort.h - * Use custom qsort implementation rather than relying on the version in libc to - * ensure that results are consistent across platforms. - */ - -/* - * Copyright (c) 2013, 2017 Alexey Tourbin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * This is a traditional Quicksort implementation which mostly follows - * [Sedgewick 1978]. Sorting is performed entirely on array indices, - * while actual access to the array elements is abstracted out with the - * user-defined `LESS` and `SWAP` primitives. - * - * Synopsis: - * QSORT(N, LESS, SWAP); - * where - * N - the number of elements in A[]; - * LESS(i, j) - compares A[i] to A[j]; - * SWAP(i, j) - exchanges A[i] with A[j]. - */ - -#ifndef QSORT_H -#define QSORT_H - -/* Sort 3 elements. */ -#define Q_SORT3(q_a1, q_a2, q_a3, Q_LESS, Q_SWAP) \ -do { \ - if (Q_LESS(q_a2, q_a1)) { \ - if (Q_LESS(q_a3, q_a2)) \ - Q_SWAP(q_a1, q_a3); \ - else { \ - Q_SWAP(q_a1, q_a2); \ - if (Q_LESS(q_a3, q_a2)) \ - Q_SWAP(q_a2, q_a3); \ - } \ - } \ - else if (Q_LESS(q_a3, q_a2)) { \ - Q_SWAP(q_a2, q_a3); \ - if (Q_LESS(q_a2, q_a1)) \ - Q_SWAP(q_a1, q_a2); \ - } \ -} while (0) - -/* Partition [q_l,q_r] around a pivot. After partitioning, - * [q_l,q_j] are the elements that are less than or equal to the pivot, - * while [q_i,q_r] are the elements greater than or equal to the pivot. */ -#define Q_PARTITION(q_l, q_r, q_i, q_j, Q_UINT, Q_LESS, Q_SWAP) \ -do { \ - /* The middle element, not to be confused with the median. */ \ - Q_UINT q_m = q_l + ((q_r - q_l) >> 1); \ - /* Reorder the second, the middle, and the last items. \ - * As [Edelkamp Weiss 2016] explain, using the second element \ - * instead of the first one helps avoid bad behaviour for \ - * decreasingly sorted arrays. This method is used in recent \ - * versions of gcc's std::sort, see gcc bug 58437#c13, although \ - * the details are somewhat different (cf. #c14). */ \ - Q_SORT3(q_l + 1, q_m, q_r, Q_LESS, Q_SWAP); \ - /* Place the median at the beginning. */ \ - Q_SWAP(q_l, q_m); \ - /* Partition [q_l+2, q_r-1] around the median which is in q_l. \ - * q_i and q_j are initially off by one, they get decremented \ - * in the do-while loops. */ \ - q_i = q_l + 1; q_j = q_r; \ - while (1) { \ - do q_i++; while (Q_LESS(q_i, q_l)); \ - do q_j--; while (Q_LESS(q_l, q_j)); \ - if (q_i >= q_j) break; /* Sedgewick says "until j < i" */ \ - Q_SWAP(q_i, q_j); \ - } \ - /* Compensate for the i==j case. */ \ - q_i = q_j + 1; \ - /* Put the median to its final place. */ \ - Q_SWAP(q_l, q_j); \ - /* The median is not part of the left subfile. */ \ - q_j--; \ -} while (0) - -/* Insertion sort is applied to small subfiles - this is contrary to - * Sedgewick's suggestion to run a separate insertion sort pass after - * the partitioning is done. The reason I don't like a separate pass - * is that it triggers extra comparisons, because it can't see that the - * medians are already in their final positions and need not be rechecked. - * Since I do not assume that comparisons are cheap, I also do not try - * to eliminate the (q_j > q_l) boundary check. */ -#define Q_INSERTION_SORT(q_l, q_r, Q_UINT, Q_LESS, Q_SWAP) \ -do { \ - Q_UINT q_i, q_j; \ - /* For each item starting with the second... */ \ - for (q_i = q_l + 1; q_i <= q_r; q_i++) \ - /* move it down the array so that the first part is sorted. */ \ - for (q_j = q_i; q_j > q_l && (Q_LESS(q_j, q_j - 1)); q_j--) \ - Q_SWAP(q_j, q_j - 1); \ -} while (0) - -/* When the size of [q_l,q_r], i.e. q_r-q_l+1, is greater than or equal to - * Q_THRESH, the algorithm performs recursive partitioning. When the size - * drops below Q_THRESH, the algorithm switches to insertion sort. - * The minimum valid value is probably 5 (with 5 items, the second and - * the middle items, the middle itself being rounded down, are distinct). */ -#define Q_THRESH 16 - -/* The main loop. */ -#define Q_LOOP(Q_UINT, Q_N, Q_LESS, Q_SWAP) \ -do { \ - Q_UINT q_l = 0; \ - Q_UINT q_r = (Q_N) - 1; \ - Q_UINT q_sp = 0; /* the number of frames pushed to the stack */ \ - struct { Q_UINT q_l, q_r; } \ - /* On 32-bit platforms, to sort a "char[3GB+]" array, \ - * it may take full 32 stack frames. On 64-bit CPUs, \ - * though, the address space is limited to 48 bits. \ - * The usage is further reduced if Q_N has a 32-bit type. */ \ - q_st[sizeof(Q_UINT) > 4 && sizeof(Q_N) > 4 ? 48 : 32]; \ - while (1) { \ - if (q_r - q_l + 1 >= Q_THRESH) { \ - Q_UINT q_i, q_j; \ - Q_PARTITION(q_l, q_r, q_i, q_j, Q_UINT, Q_LESS, Q_SWAP); \ - /* Now have two subfiles: [q_l,q_j] and [q_i,q_r]. \ - * Dealing with them depends on which one is bigger. */ \ - if (q_j - q_l >= q_r - q_i) \ - Q_SUBFILES(q_l, q_j, q_i, q_r); \ - else \ - Q_SUBFILES(q_i, q_r, q_l, q_j); \ - } \ - else { \ - Q_INSERTION_SORT(q_l, q_r, Q_UINT, Q_LESS, Q_SWAP); \ - /* Pop subfiles from the stack, until it gets empty. */ \ - if (q_sp == 0) break; \ - q_sp--; \ - q_l = q_st[q_sp].q_l; \ - q_r = q_st[q_sp].q_r; \ - } \ - } \ -} while (0) - -/* The missing part: dealing with subfiles. - * Assumes that the first subfile is not smaller than the second. */ -#define Q_SUBFILES(q_l1, q_r1, q_l2, q_r2) \ -do { \ - /* If the second subfile is only a single element, it needs \ - * no further processing. The first subfile will be processed \ - * on the next iteration (both subfiles cannot be only a single \ - * element, due to Q_THRESH). */ \ - if (q_l2 == q_r2) { \ - q_l = q_l1; \ - q_r = q_r1; \ - } \ - else { \ - /* Otherwise, both subfiles need processing. \ - * Push the larger subfile onto the stack. */ \ - q_st[q_sp].q_l = q_l1; \ - q_st[q_sp].q_r = q_r1; \ - q_sp++; \ - /* Process the smaller subfile on the next iteration. */ \ - q_l = q_l2; \ - q_r = q_r2; \ - } \ -} while (0) - -/* And now, ladies and gentlemen, may I proudly present to you... */ -#define QSORT(Q_N, Q_LESS, Q_SWAP) \ -do { \ - if ((Q_N) > 1) \ - /* We could check sizeof(Q_N) and use "unsigned", but at least \ - * on x86_64, this has the performance penalty of up to 5%. */ \ - Q_LOOP(ecs_size_t, Q_N, Q_LESS, Q_SWAP); \ -} while (0) - -void ecs_qsort( - void *base, - ecs_size_t nitems, - ecs_size_t size, - int (*compar)(const void *, const void*)); - -#define ecs_qsort_t(base, nitems, T, compar) \ - ecs_qsort(base, nitems, ECS_SIZEOF(T), compar) - -#endif - /** * @file datastructures/name_index.h * @brief Data structure for resolving 64bit keys by string (name). @@ -2573,11 +2369,6 @@ int flecs_entity_compare( ecs_entity_t e2, const void *ptr2); -/* Compare function for entity ids which can be used with qsort */ -int flecs_entity_compare_qsort( - const void *e1, - const void *e2); - bool flecs_name_is_id( const char *name); @@ -13385,31 +13176,6 @@ uint64_t flecs_hash( return wyhash(data, flecs_ito(size_t, length), 0, _wyp); } -/** - * @file datastructures/qsort.c - * @brief Quicksort implementation. - */ - - -void ecs_qsort( - void *base, - ecs_size_t nitems, - ecs_size_t size, - int (*compar)(const void *, const void*)) -{ - void *tmp = ecs_os_alloca(size); /* For swap */ - - #define LESS(i, j) \ - compar(ECS_ELEM(base, size, i), ECS_ELEM(base, size, j)) < 0 - - #define SWAP(i, j) \ - ecs_os_memcpy(tmp, ECS_ELEM(base, size, i), size),\ - ecs_os_memcpy(ECS_ELEM(base, size, i), ECS_ELEM(base, size, j), size),\ - ecs_os_memcpy(ECS_ELEM(base, size, j), tmp, size) - - QSORT(nitems, LESS, SWAP); -} - /** * @file datastructures/bitset.c * @brief Bitset data structure. @@ -19680,7 +19446,7 @@ void MonitorAlerts(ecs_iter_t *it) { ecs_set(world, ai, EcsAlertInstance, { .message = NULL }); ecs_set(world, ai, EcsMetricSource, { .entity = e }); ecs_set(world, ai, EcsMetricValue, { .value = 0 }); - ecs_doc_set_color(world, ai, "#b5494b"); + // ecs_doc_set_color(world, ai, "#b5494b"); ecs_defer_suspend(it->world); flecs_alerts_add_alert_to_src(world, e, a, ai); ecs_defer_resume(it->world); @@ -19806,6 +19572,14 @@ ecs_entity_t ecs_alert_init( ecs_add(world, result, EcsMetric); ecs_add_pair(world, result, EcsMetric, EcsCounter); + /* Add severity to alert */ + ecs_entity_t severity = desc->severity; + if (!severity) { + severity = EcsAlertError; + } + + ecs_add_pair(world, result, ecs_id(EcsAlert), severity); + if (desc->brief) { #ifdef FLECS_DOC ecs_doc_set_brief(world, result, desc->brief); @@ -19879,11 +19653,17 @@ void FlecsAlertsImport(ecs_world_t *world) { ecs_set_name_prefix(world, "Ecs"); ECS_COMPONENT_DEFINE(world, EcsAlert); + ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); ecs_set_name_prefix(world, "EcsAlert"); ECS_COMPONENT_DEFINE(world, EcsAlertInstance); ECS_COMPONENT_DEFINE(world, EcsAlertsActive); + ECS_TAG_DEFINE(world, EcsAlertInfo); + ECS_TAG_DEFINE(world, EcsAlertWarning); + ECS_TAG_DEFINE(world, EcsAlertError); + ECS_TAG_DEFINE(world, EcsAlertCritical); + ecs_add_id(world, ecs_id(EcsAlertsActive), EcsPrivate); ecs_struct(world, { @@ -34206,17 +33986,24 @@ int ecs_entity_to_json_buf( while (ecs_map_next(&it)) { flecs_json_next(buf); flecs_json_object_push(buf); + ecs_entity_t a = ecs_map_key(&it); ecs_entity_t ai = ecs_map_value(&it); char *alert_name = ecs_get_fullpath(world, ai); flecs_json_memberl(buf, "alert"); flecs_json_string(buf, alert_name); ecs_os_free(alert_name); + ecs_entity_t severity_id = ecs_get_target( + world, a, ecs_id(EcsAlert), 0); + const char *severity = ecs_get_name(world, severity_id); + const EcsAlertInstance *alert = ecs_get( world, ai, EcsAlertInstance); if (alert) { flecs_json_memberl(buf, "message"); flecs_json_string(buf, alert->message); + flecs_json_memberl(buf, "severity"); + flecs_json_string(buf, severity); } flecs_json_object_pop(buf); } @@ -46154,6 +45941,10 @@ static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; ECS_COMPONENT_DECLARE(EcsAlert); ECS_COMPONENT_DECLARE(EcsAlertInstance); ECS_COMPONENT_DECLARE(EcsAlertsActive); +ECS_TAG_DECLARE(EcsAlertInfo); +ECS_TAG_DECLARE(EcsAlertWarning); +ECS_TAG_DECLARE(EcsAlertError); +ECS_TAG_DECLARE(EcsAlertCritical); #endif /* -- Private functions -- */ @@ -59707,15 +59498,6 @@ int flecs_entity_compare( return (e1 > e2) - (e1 < e2); } -int flecs_entity_compare_qsort( - const void *e1, - const void *e2) -{ - ecs_entity_t v1 = *(ecs_entity_t*)e1; - ecs_entity_t v2 = *(ecs_entity_t*)e2; - return flecs_entity_compare(v1, NULL, v2, NULL); -} - uint64_t flecs_string_hash( const void *ptr) { diff --git a/flecs.h b/flecs.h index 61c06ad049..89e4defc70 100644 --- a/flecs.h +++ b/flecs.h @@ -11566,10 +11566,18 @@ extern "C" { FLECS_API extern ECS_COMPONENT_DECLARE(FlecsAlerts); /* Module components */ + +/** Tag added to alert, and used as first element of alert severity pair */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); +/* Alert severity tags */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); + /** Alert information. Added to each alert instance */ typedef struct EcsAlertInstance { char *message; @@ -11603,6 +11611,10 @@ typedef struct ecs_alert_desc_t { /* Description of metric. Will only be set if FLECS_DOC addon is enabled */ const char *brief; + + /* Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or + * EcsAlertCritical. Defaults to EcsAlertError. */ + ecs_entity_t severity; } ecs_alert_desc_t; /** Create a new alert. @@ -17818,12 +17830,16 @@ namespace flecs { * @{ */ -static const flecs::entity_t Alert = ecs_id(EcsAlert); -using AlertInstance = EcsAlertInstance; -using AlertsActive = EcsAlertsActive; - /** Module */ struct alerts { + using AlertsActive = EcsAlertsActive; + using Instance = EcsAlertInstance; + + struct Alert { }; + struct Info { }; + struct Warning { }; + struct Error { }; + alerts(flecs::world& world); }; @@ -29252,6 +29268,24 @@ struct alert_builder_i : filter_builder_i { return *this; } + /** Set severity of alert (default is Error) + * + * @see ecs_alert_desc_t::severity + */ + Base& severity(flecs::entity_t kind) { + m_desc->severity = kind; + return *this; + } + + /** Set severity of alert (default is Error) + * + * @see ecs_alert_desc_t::severity + */ + template + Base& severity() { + return severity(_::cpp_type::id(world_v())); + } + protected: virtual flecs::world_t* world_v() = 0; @@ -29323,6 +29357,11 @@ struct alert final : entity inline alerts::alerts(flecs::world& world) { /* Import C module */ FlecsAlertsImport(world); + + world.entity("::flecs::alerts::Alert"); + world.entity("::flecs::alerts::Info"); + world.entity("::flecs::alerts::Warning"); + world.entity("::flecs::alerts::Error"); } template diff --git a/include/flecs/addons/alerts.h b/include/flecs/addons/alerts.h index 0f07659079..70dc442c78 100644 --- a/include/flecs/addons/alerts.h +++ b/include/flecs/addons/alerts.h @@ -36,10 +36,18 @@ extern "C" { FLECS_API extern ECS_COMPONENT_DECLARE(FlecsAlerts); /* Module components */ + +/** Tag added to alert, and used as first element of alert severity pair */ FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlert); FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertInstance); FLECS_API extern ECS_COMPONENT_DECLARE(EcsAlertsActive); +/* Alert severity tags */ +FLECS_API extern ECS_TAG_DECLARE(EcsAlertInfo); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertWarning); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertError); +FLECS_API extern ECS_TAG_DECLARE(EcsAlertCritical); + /** Alert information. Added to each alert instance */ typedef struct EcsAlertInstance { char *message; @@ -73,6 +81,10 @@ typedef struct ecs_alert_desc_t { /* Description of metric. Will only be set if FLECS_DOC addon is enabled */ const char *brief; + + /* Metric kind. Must be EcsAlertInfo, EcsAlertWarning, EcsAlertError or + * EcsAlertCritical. Defaults to EcsAlertError. */ + ecs_entity_t severity; } ecs_alert_desc_t; /** Create a new alert. diff --git a/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp b/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp index 17faea8673..7ade8b0bfb 100644 --- a/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp +++ b/include/flecs/addons/cpp/mixins/alerts/builder_i.hpp @@ -45,6 +45,24 @@ struct alert_builder_i : filter_builder_i { return *this; } + /** Set severity of alert (default is Error) + * + * @see ecs_alert_desc_t::severity + */ + Base& severity(flecs::entity_t kind) { + m_desc->severity = kind; + return *this; + } + + /** Set severity of alert (default is Error) + * + * @see ecs_alert_desc_t::severity + */ + template + Base& severity() { + return severity(_::cpp_type::id(world_v())); + } + protected: virtual flecs::world_t* world_v() = 0; diff --git a/include/flecs/addons/cpp/mixins/alerts/decl.hpp b/include/flecs/addons/cpp/mixins/alerts/decl.hpp index b019623e40..3dc82d6084 100644 --- a/include/flecs/addons/cpp/mixins/alerts/decl.hpp +++ b/include/flecs/addons/cpp/mixins/alerts/decl.hpp @@ -15,12 +15,16 @@ namespace flecs { * @{ */ -static const flecs::entity_t Alert = ecs_id(EcsAlert); -using AlertInstance = EcsAlertInstance; -using AlertsActive = EcsAlertsActive; - /** Module */ struct alerts { + using AlertsActive = EcsAlertsActive; + using Instance = EcsAlertInstance; + + struct Alert { }; + struct Info { }; + struct Warning { }; + struct Error { }; + alerts(flecs::world& world); }; diff --git a/include/flecs/addons/cpp/mixins/alerts/impl.hpp b/include/flecs/addons/cpp/mixins/alerts/impl.hpp index c5cda7d1da..4f1f2d6c79 100644 --- a/include/flecs/addons/cpp/mixins/alerts/impl.hpp +++ b/include/flecs/addons/cpp/mixins/alerts/impl.hpp @@ -33,6 +33,11 @@ struct alert final : entity inline alerts::alerts(flecs::world& world) { /* Import C module */ FlecsAlertsImport(world); + + world.entity("::flecs::alerts::Alert"); + world.entity("::flecs::alerts::Info"); + world.entity("::flecs::alerts::Warning"); + world.entity("::flecs::alerts::Error"); } template diff --git a/src/addons/alerts.c b/src/addons/alerts.c index 3e8abdd557..1f66f1d0ee 100644 --- a/src/addons/alerts.c +++ b/src/addons/alerts.c @@ -117,7 +117,7 @@ void MonitorAlerts(ecs_iter_t *it) { ecs_set(world, ai, EcsAlertInstance, { .message = NULL }); ecs_set(world, ai, EcsMetricSource, { .entity = e }); ecs_set(world, ai, EcsMetricValue, { .value = 0 }); - ecs_doc_set_color(world, ai, "#b5494b"); + // ecs_doc_set_color(world, ai, "#b5494b"); ecs_defer_suspend(it->world); flecs_alerts_add_alert_to_src(world, e, a, ai); ecs_defer_resume(it->world); @@ -243,6 +243,14 @@ ecs_entity_t ecs_alert_init( ecs_add(world, result, EcsMetric); ecs_add_pair(world, result, EcsMetric, EcsCounter); + /* Add severity to alert */ + ecs_entity_t severity = desc->severity; + if (!severity) { + severity = EcsAlertError; + } + + ecs_add_pair(world, result, ecs_id(EcsAlert), severity); + if (desc->brief) { #ifdef FLECS_DOC ecs_doc_set_brief(world, result, desc->brief); @@ -316,11 +324,17 @@ void FlecsAlertsImport(ecs_world_t *world) { ecs_set_name_prefix(world, "Ecs"); ECS_COMPONENT_DEFINE(world, EcsAlert); + ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); ecs_set_name_prefix(world, "EcsAlert"); ECS_COMPONENT_DEFINE(world, EcsAlertInstance); ECS_COMPONENT_DEFINE(world, EcsAlertsActive); + ECS_TAG_DEFINE(world, EcsAlertInfo); + ECS_TAG_DEFINE(world, EcsAlertWarning); + ECS_TAG_DEFINE(world, EcsAlertError); + ECS_TAG_DEFINE(world, EcsAlertCritical); + ecs_add_id(world, ecs_id(EcsAlertsActive), EcsPrivate); ecs_struct(world, { diff --git a/src/addons/json/serialize.c b/src/addons/json/serialize.c index 5e638ae0c2..24295d1d8b 100644 --- a/src/addons/json/serialize.c +++ b/src/addons/json/serialize.c @@ -1022,17 +1022,24 @@ int ecs_entity_to_json_buf( while (ecs_map_next(&it)) { flecs_json_next(buf); flecs_json_object_push(buf); + ecs_entity_t a = ecs_map_key(&it); ecs_entity_t ai = ecs_map_value(&it); char *alert_name = ecs_get_fullpath(world, ai); flecs_json_memberl(buf, "alert"); flecs_json_string(buf, alert_name); ecs_os_free(alert_name); + ecs_entity_t severity_id = ecs_get_target( + world, a, ecs_id(EcsAlert), 0); + const char *severity = ecs_get_name(world, severity_id); + const EcsAlertInstance *alert = ecs_get( world, ai, EcsAlertInstance); if (alert) { flecs_json_memberl(buf, "message"); flecs_json_string(buf, alert->message); + flecs_json_memberl(buf, "severity"); + flecs_json_string(buf, severity); } flecs_json_object_pop(buf); } diff --git a/src/world.c b/src/world.c index 81284a12b0..c8641785b7 100644 --- a/src/world.c +++ b/src/world.c @@ -173,6 +173,10 @@ static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; ECS_COMPONENT_DECLARE(EcsAlert); ECS_COMPONENT_DECLARE(EcsAlertInstance); ECS_COMPONENT_DECLARE(EcsAlertsActive); +ECS_TAG_DECLARE(EcsAlertInfo); +ECS_TAG_DECLARE(EcsAlertWarning); +ECS_TAG_DECLARE(EcsAlertError); +ECS_TAG_DECLARE(EcsAlertCritical); #endif /* -- Private functions -- */ diff --git a/test/addons/project.json b/test/addons/project.json index 0201b33b77..6ac981b131 100644 --- a/test/addons/project.json +++ b/test/addons/project.json @@ -1558,7 +1558,10 @@ "alert_message_w_changed_var", "set_brief", "alert_instance_has_doc_name", - "reraise_alert" + "reraise_alert", + "info_severity", + "warning_severity", + "error_severity" ] }] } diff --git a/test/addons/src/Alerts.c b/test/addons/src/Alerts.c index 2819aa77b5..d34e95f716 100644 --- a/test/addons/src/Alerts.c +++ b/test/addons/src/Alerts.c @@ -628,3 +628,60 @@ void Alerts_reraise_alert() { ecs_fini(world); } + +void Alerts_info_severity() { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsAlerts); + + ECS_COMPONENT(world, Position); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_new_entity(world, "has_position"), + .filter.expr = "Position", + .severity = EcsAlertInfo + }); + test_assert(alert != 0); + test_assert( + ecs_get_target(world, alert, ecs_id(EcsAlert), 0) == EcsAlertInfo); + + ecs_fini(world); +} + +void Alerts_warning_severity() { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsAlerts); + + ECS_COMPONENT(world, Position); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_new_entity(world, "has_position"), + .filter.expr = "Position", + .severity = EcsAlertWarning + }); + test_assert(alert != 0); + test_assert( + ecs_get_target(world, alert, ecs_id(EcsAlert), 0) == EcsAlertWarning); + + ecs_fini(world); +} + +void Alerts_error_severity() { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsAlerts); + + ECS_COMPONENT(world, Position); + + ecs_entity_t alert = ecs_alert(world, { + .entity = ecs_new_entity(world, "has_position"), + .filter.expr = "Position", + .severity = EcsAlertError + }); + test_assert(alert != 0); + test_assert( + ecs_get_target(world, alert, ecs_id(EcsAlert), 0) == EcsAlertError); + + ecs_fini(world); +} diff --git a/test/addons/src/main.c b/test/addons/src/main.c index 444aa22253..b2e796ae6e 100644 --- a/test/addons/src/main.c +++ b/test/addons/src/main.c @@ -1486,6 +1486,9 @@ void Alerts_alert_message_w_changed_var(void); void Alerts_set_brief(void); void Alerts_alert_instance_has_doc_name(void); void Alerts_reraise_alert(void); +void Alerts_info_severity(void); +void Alerts_warning_severity(void); +void Alerts_error_severity(void); bake_test_case Parser_testcases[] = { { @@ -7193,6 +7196,18 @@ bake_test_case Alerts_testcases[] = { { "reraise_alert", Alerts_reraise_alert + }, + { + "info_severity", + Alerts_info_severity + }, + { + "warning_severity", + Alerts_warning_severity + }, + { + "error_severity", + Alerts_error_severity } }; @@ -7439,7 +7454,7 @@ static bake_test_suite suites[] = { "Alerts", NULL, NULL, - 9, + 12, Alerts_testcases } }; diff --git a/test/cpp_api/project.json b/test/cpp_api/project.json index 56ff9f595a..4d430f6882 100644 --- a/test/cpp_api/project.json +++ b/test/cpp_api/project.json @@ -1192,7 +1192,11 @@ "counter_target_metric", "alert", "alert_w_message", - "alert_w_brief" + "alert_w_brief", + "alert_severity_info", + "alert_severity_warning", + "alert_severity_error", + "alert_severity_implicit" ] }, { "id": "Meta", diff --git a/test/cpp_api/src/Misc.cpp b/test/cpp_api/src/Misc.cpp index 7dac28886c..7d834c3c84 100644 --- a/test/cpp_api/src/Misc.cpp +++ b/test/cpp_api/src/Misc.cpp @@ -904,18 +904,18 @@ void Misc_alert() { ecs.progress(1.0); - test_assert(!e1.has()); - test_assert(e2.has()); + test_assert(!e1.has()); + test_assert(e2.has()); test_int(e2.alert_count(), 1); test_int(e2.alert_count(a), 1); { flecs::entity ai = ecs.filter_builder() - .with() + .with() .build() .first(); test_assert(ai != 0); - test_assert(ai.has()); + test_assert(ai.has()); test_assert(ai.has()); test_assert(ai.get()->entity == e2); test_assert(ai.parent() == a); @@ -927,7 +927,7 @@ void Misc_alert() { { flecs::entity ai = ecs.filter_builder() - .with() + .with() .build() .first(); test_assert(ai == 0); @@ -953,22 +953,22 @@ void Misc_alert_w_message() { ecs.progress(1.0); - test_assert(!e1.has()); - test_assert(e2.has()); + test_assert(!e1.has()); + test_assert(e2.has()); test_int(e2.alert_count(), 1); test_int(e2.alert_count(a), 1); { flecs::entity ai = ecs.filter_builder() - .with() + .with() .build() .first(); test_assert(ai != 0); - test_assert(ai.has()); + test_assert(ai.has()); test_assert(ai.has()); test_assert(ai.get()->entity == e2); test_assert(ai.parent() == a); - test_str(ai.get()->message, + test_str(ai.get()->message, "e2 has position but not velocity"); } @@ -978,7 +978,7 @@ void Misc_alert_w_message() { { flecs::entity ai = ecs.filter_builder() - .with() + .with() .build() .first(); test_assert(ai == 0); @@ -999,3 +999,62 @@ void Misc_alert_w_brief() { test_str(a.name().c_str(), "has_position"); test_str(a.doc_brief(), "Entity has Position"); } + +void Misc_alert_severity_info() { + flecs::world ecs; + + ecs.import(); + + flecs::entity a = ecs.alert("has_position") + .with() + .severity() + .build(); + + test_assert(a != 0); + test_str(a.name().c_str(), "has_position"); + test_assert(a.target() == ecs.id()); +} + +void Misc_alert_severity_warning() { + flecs::world ecs; + + ecs.import(); + + flecs::entity a = ecs.alert("has_position") + .with() + .severity() + .build(); + + test_assert(a != 0); + test_str(a.name().c_str(), "has_position"); + test_assert(a.target() == ecs.id()); +} + +void Misc_alert_severity_error() { + flecs::world ecs; + + ecs.import(); + + flecs::entity a = ecs.alert("has_position") + .with() + .severity() + .build(); + + test_assert(a != 0); + test_str(a.name().c_str(), "has_position"); + test_assert(a.target() == ecs.id()); +} + +void Misc_alert_severity_implicit() { + flecs::world ecs; + + ecs.import(); + + flecs::entity a = ecs.alert("has_position") + .with() + .build(); + + test_assert(a != 0); + test_str(a.name().c_str(), "has_position"); + test_assert(a.target() == ecs.id()); +} diff --git a/test/cpp_api/src/main.cpp b/test/cpp_api/src/main.cpp index e5a62b2eea..9fb6a505a6 100644 --- a/test/cpp_api/src/main.cpp +++ b/test/cpp_api/src/main.cpp @@ -1139,6 +1139,10 @@ void Misc_counter_target_metric(void); void Misc_alert(void); void Misc_alert_w_message(void); void Misc_alert_w_brief(void); +void Misc_alert_severity_info(void); +void Misc_alert_severity_warning(void); +void Misc_alert_severity_error(void); +void Misc_alert_severity_implicit(void); // Testsuite 'Meta' void Meta_struct(void); @@ -5620,6 +5624,22 @@ bake_test_case Misc_testcases[] = { { "alert_w_brief", Misc_alert_w_brief + }, + { + "alert_severity_info", + Misc_alert_severity_info + }, + { + "alert_severity_warning", + Misc_alert_severity_warning + }, + { + "alert_severity_error", + Misc_alert_severity_error + }, + { + "alert_severity_implicit", + Misc_alert_severity_implicit } }; @@ -6117,7 +6137,7 @@ static bake_test_suite suites[] = { "Misc", Misc_setup, NULL, - 30, + 34, Misc_testcases }, { diff --git a/test/meta/src/SerializeToJson.c b/test/meta/src/SerializeToJson.c index e8bc31a7f5..6d660e1471 100644 --- a/test/meta/src/SerializeToJson.c +++ b/test/meta/src/SerializeToJson.c @@ -2306,7 +2306,8 @@ void SerializeToJson_serialize_entity_w_1_alert() { "\"ids\":[[\"Position\"]], " "\"alerts\":[{" "\"alert\":\"position_without_velocity.e1_alert\", " - "\"message\":\"e1 has Position but not Velocity\"" + "\"message\":\"e1 has Position but not Velocity\", " + "\"severity\":\"Error\"" "}]" "}"); @@ -2357,11 +2358,13 @@ void SerializeToJson_serialize_entity_w_2_alerts() { "\"path\":\"e1\", " "\"ids\":[[\"Position\"]], " "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert_1\", " - "\"message\":\"e1 has Position but not Velocity\"" - "}, {" "\"alert\":\"position_without_mass.e1_alert_2\", " - "\"message\":\"e1 has Position but not Mass\"" + "\"message\":\"e1 has Position but not Mass\", " + "\"severity\":\"Error\"" + "}, {" + "\"alert\":\"position_without_velocity.e1_alert_1\", " + "\"message\":\"e1 has Position but not Velocity\", " + "\"severity\":\"Error\"" "}]" "}");