11#include <initializer_list>
38 explicit operator bool() const noexcept {
return valid; }
44 std::vector<std::string> out;
45 out.reserve(
errors.size());
46 for (
const auto& e :
errors) out.push_back(e.message);
52 return a.valid == b.valid && a.errors == b.errors;
88 if (
errors.empty())
return std::nullopt;
94 if (
errors.empty())
return std::nullopt;
95 return errors.front().message;
99 [[nodiscard]] std::vector<Error>
101 std::vector<Error> out;
102 for (
const auto& e :
errors) {
103 if (e.key.field_path == field_path) out.push_back(e);
110 for (
const auto& e :
errors) {
111 if (e.key.rule_id ==
rule_id)
return true;
116 explicit operator bool() const noexcept {
return valid; }
120 return a.valid == b.valid
121 && a.pending == b.pending
122 && a.touched == b.touched
123 && a.dirty == b.dirty
124 && a.errors == b.errors
125 && a.warnings == b.warnings;
134template<PropertyValue T>
142 using Rule = std::function<std::optional<std::string>(
const T&)>;
147 baseline_(source.get()),
150 sub_ = source_->bind([
this](
const T& v) {
151 const bool new_dirty = !(v == baseline_);
152 if (new_dirty != state_.peek().dirty) {
153 auto s = state_.peek();
167 [[nodiscard]]
const std::string&
field_path() const noexcept {
174 const std::size_t auto_id = auto_id_counter_++;
175 if (rule_id_value.empty()) {
176 rule_id_value =
"rule_" + std::to_string(auto_id);
178 rules_.push_back(RuleEntry{std::move(r), std::move(rule_id_value)});
179 if (!suspend_) run_(source_->get());
183 template<std::predicate<const T&> P>
185 std::string rule_id_value = {}) {
187 [p = std::forward<P>(predicate), m = std::move(message)](
188 const T& v) -> std::optional<std::string> {
189 if (p(v))
return std::nullopt;
192 std::move(rule_id_value));
196 const std::size_t auto_id = auto_id_counter_++;
197 if (rule_id_value.empty()) {
198 rule_id_value =
"rule_" + std::to_string(auto_id);
200 warnings_.push_back(RuleEntry{std::move(r), std::move(rule_id_value)});
201 if (!suspend_) run_(source_->get());
205 template<std::predicate<const T&> P>
207 std::string rule_id_value = {}) {
209 [p = std::forward<P>(predicate), m = std::move(message)](
210 const T& v) -> std::optional<std::string> {
211 if (p(v))
return std::nullopt;
214 std::move(rule_id_value));
219 for (
const auto& r : rs) {
220 const std::size_t auto_id = auto_id_counter_++;
221 rules_.push_back(RuleEntry{r,
"rule_" + std::to_string(auto_id)});
224 run_(source_->get());
231 if (state_.peek().touched)
return;
232 auto s = state_.peek();
238 if (!state_.peek().touched)
return;
239 auto s = state_.peek();
249 baseline_ = source_->peek_ref();
250 if (!state_.peek_ref().dirty)
return;
251 auto s = state_.peek_ref();
253 state_.set(std::move(s));
257 if (state_.peek().pending)
return;
258 auto s = state_.peek();
277 async_errors_.clear();
278 async_errors_.reserve(extra_messages.size());
280 for (
auto& msg : extra_messages) {
291 async_errors_ = std::move(extra);
297 async_errors_.clear();
304 if (!state_.peek_ref().pending)
return;
305 auto s = state_.peek_ref();
307 state_.set(std::move(s));
321 std::weak_ptr<void> lifetime_token_() {
322 if (!lifetime_) lifetime_ = std::make_shared<char>();
331 void finish_pending_() {
333 auto s = state_.peek();
339 ::aria::trace::Validation{
341 ::aria::ValidationKey{field_path_, std::string{}},
345 run_(source_->get());
348 void run_(
const T& v) {
350 auto s = state_.peek_ref();
353 for (
auto& entry : rules_) {
354 if (
auto msg = entry.body(v)) {
357 ::aria::trace::Validation{
359 ::aria::ValidationKey{field_path_, entry.rule_id},
366 }
else if (tracing) {
368 ::aria::trace::Validation{
370 ::aria::ValidationKey{field_path_, entry.rule_id},
376 for (
auto& entry : warnings_) {
377 if (
auto msg = entry.body(v)) {
380 ::aria::trace::Validation{
382 ::aria::ValidationKey{field_path_, entry.rule_id},
389 }
else if (tracing) {
391 ::aria::trace::Validation{
393 ::aria::ValidationKey{field_path_, entry.rule_id},
399 for (
const auto& e : async_errors_) {
404 if (fixed.key.field_path.empty()) fixed.key.field_path = field_path_;
405 if (fixed.source.empty()) fixed.source =
"Validator";
406 if (fixed.is_warning()) s.warnings.push_back(std::move(fixed));
407 else s.errors.push_back(std::move(fixed));
410 s.valid = s.errors.empty();
420 state_.set(std::move(s));
421 result_.set(std::move(new_result));
426 std::string field_path_;
428 std::vector<RuleEntry> rules_;
429 std::vector<RuleEntry> warnings_;
430 std::vector<Error> async_errors_;
434 std::size_t auto_id_counter_ = 0;
435 bool suspend_ =
false;
436 std::shared_ptr<void> lifetime_;
RAII handle to a single subscription.
Definition subscription.hpp:44
const Property< ValidationResult > & result() const noexcept
Definition validator.hpp:316
std::function< std::optional< std::string >(const T &)> Rule
User-supplied rule body: returns a message when the value fails, std::nullopt when it passes.
Definition validator.hpp:142
void touch()
Definition validator.hpp:230
Validator & rules(std::initializer_list< Rule > rs)
Definition validator.hpp:217
void reset_touched()
Definition validator.hpp:237
Validator(Property< T > &source, std::string field_path={})
Definition validator.hpp:144
Validator & warning(Rule r, std::string rule_id_value={})
Definition validator.hpp:195
void begin_pending()
Definition validator.hpp:256
const std::string & field_path() const noexcept
Definition validator.hpp:167
~Validator()
Definition validator.hpp:161
Validator & should(P &&predicate, std::string message, std::string rule_id_value={})
Definition validator.hpp:206
void cancel_pending()
Cancel pending work while preserving the previous validation result, including async errors and warni...
Definition validator.hpp:303
const Property< ValidationState > & state() const noexcept
Definition validator.hpp:313
Validator & rule(Rule r, std::string rule_id_value={})
Definition validator.hpp:173
void end_pending(std::vector< Error > extra)
Settle pending with caller-shaped error records.
Definition validator.hpp:290
Property< ValidationState > & state() noexcept
Definition validator.hpp:312
void end_pending(std::vector< std::string > extra_messages)
Settle the async validation: clears pending and stores extra_messages as additional Error-severity en...
Definition validator.hpp:276
Validator & must(P &&predicate, std::string message, std::string rule_id_value={})
Definition validator.hpp:184
Property< ValidationResult > & result() noexcept
Definition validator.hpp:315
void end_pending()
Settle pending without any extra errors.
Definition validator.hpp:296
void reset_dirty()
Definition validator.hpp:244
Driver that turns a coroutine factory into a latest-wins async validation rule attached to a Validato...
Definition async_validator.hpp:210
Definition property.hpp:103
Definition async_command.hpp:118
auto batch(Fn &&fn) -> decltype(fn())
Sugar: batch([&]{ firstName = "..."; lastName = "..."; }).
Definition graph.hpp:369
@ Validation
A validator rule failed.
Definition error.hpp:74
void publish_trace_unchecked(const TraceEvent &event) noexcept
Publish an already-built event using one owning sink snapshot.
Definition diagnostics.hpp:293
validation_dsl::RulePart rule_id(std::string id) noexcept
Definition validation_key.hpp:96
bool has_trace_sink() noexcept
True iff a sink is currently installed.
Definition diagnostics.hpp:286
@ Validation
Validator / FormValidator rule runs.
Definition diagnostics.hpp:71
bool operator!=(const Error &a, const Error &b) noexcept
Definition error.hpp:271
bool operator==(const Error &a, const Error &b) noexcept
Definition error.hpp:264
Definition validation_key.hpp:110
One uniform error record.
Definition error.hpp:129
static Error validation(ValidationKey k, std::string msg)
Hard validation error.
Definition error.hpp:170
ErrorKind kind
Definition error.hpp:130
static Error validation_warning(ValidationKey k, std::string msg)
Soft validation advisory (severity = Warning).
Definition error.hpp:176
(field_path, rule_id) locator for a validation message.
Definition validation_key.hpp:52
Definition validator.hpp:34
std::vector< Error > errors
Definition validator.hpp:36
std::vector< std::string > error_messages() const
Project the rich error list down to plain message strings, in the order they were produced.
Definition validator.hpp:43
bool valid
Definition validator.hpp:35
Definition validator.hpp:61
std::optional< Error > first_error() const
First Error-severity entry, if any.
Definition validator.hpp:87
bool pending
True while an async validator is running.
Definition validator.hpp:67
std::vector< Error > errors
Hard failures (kind == Validation, severity == Error).
Definition validator.hpp:78
std::optional< std::string > first_error_message() const
First Error-severity message text, if any.
Definition validator.hpp:93
bool valid
True iff there are no Error-severity entries.
Definition validator.hpp:64
bool touched
True once the user has interacted with the field (typically on focus-out).
Definition validator.hpp:71
std::vector< Error > errors_for(std::string_view field_path) const
All Error-severity entries that target a given field path.
Definition validator.hpp:100
std::vector< Error > warnings
Soft advisories (kind == Validation, severity == Warning).
Definition validator.hpp:82
bool dirty
True once the field's value has moved away from its baseline.
Definition validator.hpp:74
bool has_error_with_rule(std::string_view rule_id) const noexcept
True if any Error-severity entry has the given rule_id.
Definition validator.hpp:109
Validation events.
Definition diagnostics.hpp:160