// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // What a command handler returns besides a plain value: an Error with a // stable code and a message written for the agent reading it, a Result that // is either, and an Async for a result that takes frames to produce. export module Crafter.Debug.Core:Result; import std; export namespace Crafter::Debug { struct Error { // Stable and machine-readable, "InvalidScene": scripts branch on it. std::string code; // For whoever reads the reply: what went wrong, and the next step // ("not in a game; run Lobby_Start first"). std::string message; }; template class Result { public: // Anything T can be made from, so `return "text";` works for a // Result. template requires (std::constructible_from && !std::same_as, Error> && !std::same_as, Result>) Result(U&& value) : state(std::in_place_index<0>, std::forward(value)) {} Result(Error error) : state(std::in_place_index<1>, std::move(error)) {} bool Ok() const { return state.index() == 0; } explicit operator bool() const { return Ok(); } T& Get() { return std::get<0>(state); } const T& Get() const { return std::get<0>(state); } const Error& GetError() const { return std::get<1>(state); } private: std::variant state; }; template<> class Result { public: Result() = default; Result(Error error) : error(std::move(error)) {} bool Ok() const { return !error.has_value(); } explicit operator bool() const { return Ok(); } const Error& GetError() const { return *error; } private: std::optional error; }; // A result that takes frames to arrive, such as a GPU readback. The // session polls it once per Pump, on the app's thread, until it yields; // the caller simply waits, with no ready:false loop of its own. The // poller returns std::optional (empty while not ready), or // std::optional> to fail; for Async, a bool or a // std::optional>. template class Async { public: template requires std::invocable Async(F poller) : poll(Wrap(std::move(poller))) {} // Empty while not ready. std::optional> Poll() { return poll(); } private: std::function>()> poll; template static std::function>()> Wrap(F poller) { using R = std::invoke_result_t; if constexpr (std::is_same_v>>) { return poller; } else if constexpr (std::is_void_v) { static_assert(std::is_same_v, "an Async poller returns bool or std::optional>"); return [poller = std::move(poller)]() mutable -> std::optional> { if (!poller()) return std::nullopt; return Result(); }; } else { static_assert(std::is_same_v>, "an Async poller returns std::optional or std::optional>"); return [poller = std::move(poller)]() mutable -> std::optional> { std::optional value = poller(); if (!value) return std::nullopt; return Result(std::move(*value)); }; } } }; }