// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // Watches and samples resolve by path, including into their values // ("bodies[1].pos.y" against the watch "bodies"), and set the same way: // writing one member of a watched struct writes the whole struct back. Sets // respect bounds and read-only watches, computed watches set through their // setter (which may refuse), samples keep a frame-stamped history, unknown // paths get a suggestion, and a Group's watches go with it. import std; import Crafter.Debug; using namespace Crafter::Debug; namespace { std::int32_t failures = 0; void Check(bool ok, std::string_view what) { std::println("{} {}", ok ? "PASS" : "FAIL", what); if (!ok) ++failures; } struct Vec3 { float x = 0; float y = 0; float z = 0; }; struct Body { std::int32_t id = 0; Vec3 pos; }; float gravity = -9.81F; const std::int32_t build = 7; std::vector bodies = { { 1, { 0, 1, 0 } }, { 2, { 3, 4, 5 } } }; std::int32_t lives = 3; Detail::Registry& registry() { return Detail::Registry::Instance(); } } int main() { Watch("physics.gravity", gravity, { .min = -50, .max = 0, .help = "m/s^2" }); Watch("build", build); Watch("bodies", bodies); Watch("lives", [] { return lives; }, [](const std::int32_t& value) -> Result<> { if (value < 0) return Error{ "Negative", "lives cannot be negative" }; lives = value; return {}; }); Watch("score", [] { return 1234; }); Check(registry().Resolve("physics.gravity").value.AsNumber() == static_cast(-9.81F), "a variable watch reads the variable"); Check(registry().Resolve("bodies[1].pos.y").value.AsNumber() == 4, "a path reads into a watched value"); Check(registry().Resolve("bodies[1]").value.Find("id")->AsInteger() == 2, "an index reads an item"); Check(!registry().Set("physics.gravity", Value(-3.5)) && gravity == -3.5F, "a set writes the variable"); const auto bounded = registry().Set("physics.gravity", Value(10)); Check(bounded && bounded->code == "InvalidParams" && bounded->message.find("outside") != std::string::npos && gravity == -3.5F, "a set outside the bounds is refused"); Check(registry().Set("build", Value(8))->code == "ReadOnly" && build == 7, "a const variable's watch is read-only"); Check(!registry().Set("bodies[0].pos.x", Value(9)) && bodies[0].pos.x == 9 && bodies[0].pos.y == 1, "a set into a member writes back the whole value"); const auto wrongType = registry().Set("physics.gravity", Value("down")); Check(wrongType && wrongType->message.find("expected a number") != std::string::npos, "a set of the wrong type is refused"); Check(!registry().Set("lives", Value(5)) && lives == 5, "a computed watch sets through its setter"); const auto negative = registry().Set("lives", Value(-1)); Check(negative && negative->message.find("lives cannot be negative") != std::string::npos && lives == 5, "a setter can refuse"); Check(registry().Set("score", Value(1))->code == "ReadOnly", "a getter-only watch is read-only"); const auto missing = registry().Resolve("physics.gravty"); Check(!missing.Ok() && missing.error.find("did you mean 'physics.gravity'") != std::string::npos, "an unknown path gets a suggestion"); const auto badMember = registry().Resolve("bodies[0].pos.w"); Check(!badMember.Ok() && badMember.error.find("has no member 'w'") != std::string::npos, "an unknown member is named"); const auto outOfRange = registry().Resolve("bodies[5]"); Check(!outOfRange.Ok() && outOfRange.error.find("2 items") != std::string::npos, "an index out of range gives the size"); for (std::uint64_t frame = 1; frame <= 3; ++frame) { Detail::SetFrame(frame); Sample("xpbd.residual", static_cast(frame) / 10); } Check(registry().Resolve("xpbd.residual").value.AsNumber() == 0.3, "a sample resolves to its latest value"); const auto history = registry().SampleHistory("xpbd.residual"); Check(history.size() == 3 && history.front().frame == 1 && history.back().frame == 3, "a sample keeps a frame-stamped history"); Check(registry().Watches("physics.*").size() == 1 && registry().Watches("*").size() == 5, "watches can be listed by glob"); Check(registry().Watches("physics.gravity").front()->help == "m/s^2" && registry().Watches("physics.gravity").front()->type.Find("maximum") != nullptr, "a watch carries its help and bounds"); { struct Fort { std::int32_t hp = 100; Group debug{ "fort1" }; Fort() { debug.Watch("core.hp", hp); } }; Fort fort; Check(registry().Resolve("fort1.core.hp").value.AsInteger() == 100, "a Group prefixes its watches"); } Check(!registry().Resolve("fort1.core.hp").Ok(), "a Group's watches go with it"); return failures == 0 ? 0 : 1; }