76 std::string message) {
87struct AsyncValidatorState
88 : std::enable_shared_from_this<AsyncValidatorState<T>>
90 using Factory = std::function<
101 std::atomic<std::uint64_t> gen{0};
106 std::optional<T> last_value;
111 std::weak_ptr<void> target_lifetime;
112 ::aria::Subscription source_subscription;
113 std::uint64_t attachment{0};
117 [[nodiscard]]
bool has_target() const noexcept {
118 return target && !target_lifetime.expired();
121 AsyncValidatorState(IExecutor& u, IExecutor& w, Factory f)
122 : ui(&u), worker(&w), factory(std::move(f)) {}
126Task<void> async_validator_run_one_(
127 std::shared_ptr<AsyncValidatorState<T>> self,
129 std::uint64_t my_gen,
130 CancellationToken tok)
132 std::optional<AsyncRuleResult> outcome;
133 std::optional<::aria::Error> failure;
134 bool cancelled =
false;
137 tok.throw_if_cancelled();
138 outcome =
co_await self->factory(std::move(value), tok);
139 tok.throw_if_cancelled();
140 }
catch (
const OperationCancelled&) {
149 std::current_exception(),
"AsyncValidator");
152 if (self->gen.load(std::memory_order_acquire) != my_gen)
co_return;
158 if (self->gen.load(std::memory_order_acquire) != my_gen) {
161 if (!self->has_target()) {
165 if (cancelled || tok.is_cancelled()) {
166 self->target->cancel_pending();
170 std::vector<::aria::Error> extras;
171 if (failure.has_value()) {
172 extras.push_back(std::move(*failure));
173 }
else if (outcome.has_value()) {
174 for (
auto& e : outcome->errors) extras.push_back(std::move(e));
175 for (
auto& w : outcome->warnings) extras.push_back(std::move(w));
177 self->target->end_pending(std::move(extras));
212 using Factory =
typename detail::AsyncValidatorState<T>::Factory;
215 : state_(
std::make_shared<detail::AsyncValidatorState<T>>(
216 ui, worker,
std::move(factory))) {}
219 auto retired = std::move(state_);
227 if (
this != &other) {
228 auto retired = std::exchange(state_, std::move(other.state_));
240 if (!state)
throw std::logic_error(
"AsyncValidator: cannot attach a moved-from driver");
241 auto lifetime = v.lifetime_token_();
242 const auto attachment = detach_(state);
243 if (state->attachment != attachment)
return {};
245 state->target_lifetime = std::move(lifetime);
246 state->last_value.reset();
250 std::weak_ptr<detail::AsyncValidatorState<T>> weak = state;
251 state->source_subscription = source.
on_changed(
252 [weak, attachment](
const T& value) {
253 if (
auto current = weak.lock(); current && current->attachment == attachment) {
254 fire_(current, value);
258 if (
auto current = weak.lock(); current && current->attachment == attachment) {
263 fire_(state, source.
get());
268 static std::uint64_t detach_(
269 const std::shared_ptr<detail::AsyncValidatorState<T>>& state)
noexcept {
270 if (!state)
return 0;
271 const auto attachment = ++state->attachment;
272 state->gen.fetch_add(1, std::memory_order_acq_rel);
273 auto* target = std::exchange(state->target,
nullptr);
274 auto lifetime = std::move(state->target_lifetime);
275 auto cancellation = std::move(state->cancel);
276 state->source_subscription.release();
279 if (target && !lifetime.expired()) {
285 try { cancellation.cancel(); }
292 static void fire_(
const std::shared_ptr<detail::AsyncValidatorState<T>>& state,
295 if (!state->has_target())
return;
296 if (state->last_value.has_value() && *state->last_value == value) {
301 state->last_value = snapshot;
303 CancellationSource next;
304 auto previous = std::move(state->cancel);
305 state->cancel = std::move(next);
306 auto token = state->cancel.token();
308 state->gen.fetch_add(1, std::memory_order_acq_rel) + 1;
310 if (state->gen.load(std::memory_order_acquire) != my_gen || !state->has_target())
return;
311 state->target->begin_pending();
312 if (state->gen.load(std::memory_order_acquire) != my_gen || !state->has_target())
return;
313 detail::async_validator_run_one_<T>(state, std::move(snapshot), my_gen, std::move(token))
317 std::shared_ptr<detail::AsyncValidatorState<T>> state_;
RAII handle to a single subscription.
Definition subscription.hpp:44
Definition validator.hpp:135
void cancel_pending()
Cancel pending work while preserving the previous validation result, including async errors and warni...
Definition validator.hpp:303
AsyncValidator(AsyncValidator &&) noexcept=default
AsyncValidator(const AsyncValidator &)=delete
typename detail::AsyncValidatorState< T >::Factory Factory
Definition async_validator.hpp:212
AsyncValidator & operator=(const AsyncValidator &)=delete
~AsyncValidator()
Definition async_validator.hpp:218
::aria::Subscription attach_to(::aria::Validator< T > &v, ::aria::Property< T > &source)
Attach to a Validator + its source Property.
Definition async_validator.hpp:237
AsyncValidator(IExecutor &ui, IExecutor &worker, Factory factory)
Definition async_validator.hpp:214
Definition cancellation.hpp:218
Definition cancellation.hpp:182
Abstract executor interface — schedules a callable to run "somewhere".
Definition executor.hpp:36
Definition property.hpp:103
T get() const
Auto-tracked read.
Definition property.hpp:138
::aria::Subscription on_changed(std::function< void(const T &)> fn)
Run fn(new_value) every time the value changes.
Definition property.hpp:200
Definition async_command.hpp:118
auto schedule_on(IExecutor &exec)
Schedule a coroutine to resume on the given executor.
Definition executor.hpp:396
void report_callback_failure(std::string_view category, std::exception_ptr exception, std::string_view message={}) noexcept
Report a callback failure.
Definition validation_key.hpp:110
static Error from_exception(std::exception_ptr ex, std::string source_tag)
Catch-all converter from a thrown exception_ptr to a typed Error.
Definition error.hpp:237
static Error validation(ValidationKey k, std::string msg)
Hard validation error.
Definition error.hpp:170
(field_path, rule_id) locator for a validation message.
Definition validation_key.hpp:52
Outcome of an async rule invocation.
Definition async_validator.hpp:65
std::vector<::aria::Error > warnings
Soft advisories.
Definition async_validator.hpp:70
static AsyncRuleResult passed()
Definition async_validator.hpp:72
std::vector<::aria::Error > errors
Hard failures. Empty iff the rule passed.
Definition async_validator.hpp:67
static AsyncRuleResult failed(::aria::ValidationKey key, std::string message)
Convenience: build a single-error failure under the given key.
Definition async_validator.hpp:75