Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
async_command_result.hpp
Go to the documentation of this file.
1#pragma once
2
3// AsyncCommandResult<R>
4//
5// Strongly-typed return value of AsyncCommand::co_execute(). Captures
6// the FOUR distinct outcomes a command invocation may have, instead of
7// silently collapsing them onto a default-constructed R:
8//
9// * Completed — the action ran to completion.
10// For R != void, `value` holds the produced value.
11// For R == void, `value` is absent (the type has no
12// payload field).
13// * Dropped — the command's policy is DropIfRunning AND another
14// invocation was in flight when this one was issued,
15// so it never started.
16// * Cancelled — an OperationCancelled was observed before completion.
17// This includes:
18// - command-wide cancellation (dtor, manual cancel)
19// - per-invocation cancellation (LatestOnly preempt)
20// - user-initiated CancellationToken trips inside
21// the action body
22// `error` carries the structured cancellation Error.
23// * Failed — the action threw a non-cancellation exception.
24// `error` carries the mapped aria::Error (TimeoutError,
25// domain Errors, std::exception, …).
26//
27// Design rationale (vs. legacy `co_return R{}` on drop):
28// * `R == 0` is a valid business value for `Task<int>`. Collapsing
29// "I didn't run" onto it is undefined-by-convention. Users had no
30// way to distinguish "search returned 0 hits" from "search dropped
31// because previous one still running".
32// * Forces R to be DefaultConstructible. AsyncCommand<MyResult> with
33// a non-default-constructible MyResult failed to compile. The new
34// model lifts that constraint entirely.
35// * The four-state enum makes UI reactions explicit. A "Save" button
36// that uses DropIfRunning genuinely wants to know "did it actually
37// save, or was I rate-limited?" — not "I got a default Foo back,
38// hope that means something".
39//
40// co_execute() never throws under the new contract. All exceptions —
41// including OperationCancelled — fold into a status, so callers can
42// write straight-line code:
43//
44// auto r = co_await cmd.co_execute(query);
45// if (r) use(*r); // Completed
46// else if (r.dropped()) notify_busy();
47// else if (r.cancelled()) /* silently swallow, e.g. user typed */;
48// else if (r.failed()) show_error(*r.error);
49//
50// The fire-and-forget `execute()` path keeps the same observable
51// surface as before (`is_executing`, `last_error`, `last_result`); it
52// internally consumes an AsyncCommandResult too, but the user sees no
53// difference.
54
55#include "aria/error.hpp"
56
57#include <cstdint>
58#include <optional>
59#include <type_traits>
60#include <utility>
61
62namespace aria::async {
63
76
77namespace detail {
78
82struct AsyncCommandResultBase {
84 std::optional<::aria::Error> error{};
85
86 [[nodiscard]] bool completed() const noexcept { return status == AsyncCommandStatus::Completed; }
87 [[nodiscard]] bool dropped() const noexcept { return status == AsyncCommandStatus::Dropped; }
88 [[nodiscard]] bool cancelled() const noexcept { return status == AsyncCommandStatus::Cancelled; }
89 [[nodiscard]] bool failed() const noexcept { return status == AsyncCommandStatus::Failed; }
90};
91
92} // namespace detail
93
104template<typename R>
105struct AsyncCommandResult : detail::AsyncCommandResultBase {
106 static_assert(!std::is_reference_v<R>,
107 "AsyncCommandResult<R&> is not supported. Use a value type or "
108 "wrap in std::reference_wrapper at the action layer.");
109
110 std::optional<R> value{};
111
112 [[nodiscard]] explicit operator bool() const noexcept { return completed(); }
113
119 [[nodiscard]] R& operator*() & noexcept { return *value; }
120 [[nodiscard]] const R& operator*() const& noexcept { return *value; }
121 [[nodiscard]] R&& operator*() && noexcept { return std::move(*value); }
122 [[nodiscard]] const R&& operator*() const&& noexcept { return std::move(*value); }
123
124 [[nodiscard]] R* operator->() noexcept { return &*value; }
125 [[nodiscard]] const R* operator->() const noexcept { return &*value; }
126
130 template<typename U>
131 [[nodiscard]] R value_or(U&& fallback) const& {
132 return completed() ? *value : static_cast<R>(std::forward<U>(fallback));
133 }
134 template<typename U>
135 [[nodiscard]] R value_or(U&& fallback) && {
136 return completed() ? std::move(*value)
137 : static_cast<R>(std::forward<U>(fallback));
138 }
139
140 // ── Factories ─────────────────────────────────────────────────
141 // Used by AsyncCommand internals; exposed publicly because they
142 // are the cleanest way to mock an AsyncCommandResult in user
143 // tests (e.g. injecting a fake command stub).
144 [[nodiscard]] static AsyncCommandResult completed_with(R v) {
147 r.value.emplace(std::move(v));
148 return r;
149 }
150 [[nodiscard]] static AsyncCommandResult dropped_() {
153 return r;
154 }
158 r.error.emplace(std::move(e));
159 return r;
160 }
161 [[nodiscard]] static AsyncCommandResult failed_(::aria::Error e) {
164 r.error.emplace(std::move(e));
165 return r;
166 }
167};
168
171template<>
172struct AsyncCommandResult<void> : detail::AsyncCommandResultBase {
173 [[nodiscard]] explicit operator bool() const noexcept { return completed(); }
174
175 [[nodiscard]] static AsyncCommandResult completed_with() {
178 return r;
179 }
180 [[nodiscard]] static AsyncCommandResult dropped_() {
183 return r;
184 }
188 r.error.emplace(std::move(e));
189 return r;
190 }
191 [[nodiscard]] static AsyncCommandResult failed_(::aria::Error e) {
194 r.error.emplace(std::move(e));
195 return r;
196 }
197};
198
199} // namespace aria::async
Definition async_command.hpp:118
AsyncCommandStatus
Outcome category for a single AsyncCommand invocation.
Definition async_command_result.hpp:70
@ Completed
action finished, value (if any) produced
Definition async_command_result.hpp:71
@ Dropped
DropIfRunning rejected this invocation; never started.
Definition async_command_result.hpp:72
@ Cancelled
OperationCancelled observed before completion.
Definition async_command_result.hpp:73
@ Failed
action threw a non-cancellation exception
Definition async_command_result.hpp:74
One uniform error record.
Definition error.hpp:129
static AsyncCommandResult cancelled_(::aria::Error e)
Definition async_command_result.hpp:185
static AsyncCommandResult failed_(::aria::Error e)
Definition async_command_result.hpp:191
static AsyncCommandResult completed_with()
Definition async_command_result.hpp:175
static AsyncCommandResult dropped_()
Definition async_command_result.hpp:180
Result of AsyncCommand<R, Args...>::co_execute(...).
Definition async_command_result.hpp:105
const R && operator*() const &&noexcept
Definition async_command_result.hpp:122
const R & operator*() const &noexcept
Definition async_command_result.hpp:120
static AsyncCommandResult dropped_()
Definition async_command_result.hpp:150
static AsyncCommandResult failed_(::aria::Error e)
Definition async_command_result.hpp:161
R value_or(U &&fallback) const &
Convenience: extract value or fall back.
Definition async_command_result.hpp:131
R * operator->() noexcept
Definition async_command_result.hpp:124
const R * operator->() const noexcept
Definition async_command_result.hpp:125
R value_or(U &&fallback) &&
Definition async_command_result.hpp:135
R && operator*() &&noexcept
Definition async_command_result.hpp:121
static AsyncCommandResult cancelled_(::aria::Error e)
Definition async_command_result.hpp:155
R & operator*() &noexcept
Direct access to the produced value.
Definition async_command_result.hpp:119
std::optional< R > value
Definition async_command_result.hpp:110
static AsyncCommandResult completed_with(R v)
Definition async_command_result.hpp:144