37template<
class Source,
class Value,
class Project = std::
identity>
38auto form_reader(Source& source, Value expired, Project project = {}) {
39 if constexpr (std::derived_from<Source, ::aria::reactive::Node>) {
40 return [&source, handle = ::aria::reactive::detail::NodeHandle{&source},
41 expired = std::move(expired), project = std::move(project)] -> Value {
42 return handle ? project(source.get()) : expired;
45 return [&source, project = std::move(project)] -> Value {
return project(source.get()); };
51template<PropertyValue T>
71 initial_(
value.get()) {
75 [[nodiscard]]
const std::string&
field_path() const noexcept {
80 std::string rule_id_value = {}) {
81 validator.rule(std::move(r), std::move(rule_id_value));
85 template<std::predicate<const T&> P>
87 std::string rule_id_value = {}) {
88 validator.must(std::forward<P>(predicate),
90 std::move(rule_id_value));
95 requires requires(
const T& v) { { v.empty() } -> std::convertible_to<bool>; }
97 return must([](
const T& v) {
return !v.empty(); },
103 requires requires(
const T& v) { { v.size() } -> std::convertible_to<std::size_t>; }
105 return must([n](
const T& v) {
return v.size() >= n; },
112 value = std::move(v);
131 dirty = !(v == initial_);
136 SubscriptionBag bag_;
144 template<
typename Field>
146 fields_.push_back(FieldHooks{
147 detail::form_reader(f.is_valid,
true),
148 detail::form_reader(f.dirty,
false)
150 bag_ += f.is_valid.on_changed([
this](
bool) { recompute_(); });
151 bag_ += f.dirty.on_changed([
this](
bool) { recompute_(); });
164 std::function<bool()> valid;
165 std::function<bool()> dirty;
169 bool all_valid =
true;
170 bool any_dirty =
false;
171 for (
auto& f : fields_) {
172 all_valid = all_valid && f.valid();
173 any_dirty = any_dirty || f.dirty();
179 std::vector<FieldHooks> fields_;
180 SubscriptionBag bag_;
201 template<
typename Field>
203 auto pending = [&f] -> std::function<bool()> {
204 if constexpr (
requires { f.validator.state(); }) {
205 return detail::form_reader(f.validator.state(),
false,
208 return [] {
return false; };
211 fields_.push_back(FieldHooks{
212 detail::form_reader(f.is_valid,
true),
213 detail::form_reader(f.dirty,
false),
215 detail::form_reader(f.error_full, std::optional<::aria::Error>{}),
218 bag_ += f.is_valid.on_changed ([
this](
bool) { recompute_(); });
219 bag_ += f.dirty.on_changed ([
this](
bool) { recompute_(); });
220 bag_ += f.error_full.on_changed ([
this](
const std::optional<::aria::Error>&) {
223 using ValueT = std::remove_reference_t<
decltype(f.value.get())>;
224 bag_ += f.value.on_changed([
this](
const ValueT&) { recompute_(); });
225 if constexpr (
requires { f.validator.state(); }) {
226 bag_ += f.validator.state().on_changed([
this](
const ValidationState&) { recompute_(); });
235 template<std::predicate Pred>
236 void rule(Pred predicate, std::string message,
237 std::string rule_id_value = {}) {
238 if (rule_id_value.empty()) {
239 rule_id_value =
"form_rule_" + std::to_string(rules_.size());
241 rules_.push_back(Rule{
242 std::function<bool()>(std::move(predicate)),
244 std::move(rule_id_value),
266 std::function<bool()> valid;
267 std::function<bool()> dirty;
268 std::function<bool()> pending;
269 std::function<std::optional<::aria::Error>()> error;
272 std::function<bool()> predicate;
278 const auto revision = revision_;
279 const auto rules = rules_;
280 bool all_valid =
true;
281 bool any_dirty =
false;
282 bool any_pending =
false;
283 std::optional<::aria::Error> headline;
285 for (
const auto& r : rules) {
286 const bool passed = r.predicate();
287 if (revision != revision_)
return;
292 ValidationKey{std::string{}, r.rule_id},
294 e.
source =
"FormValidator";
295 headline = std::move(e);
299 for (
auto& f : fields_) {
300 const bool v = f.valid();
301 const bool d = f.dirty();
302 const bool p = f.pending();
303 all_valid = all_valid && v;
304 any_dirty = any_dirty || d;
305 any_pending = any_pending || p;
306 if (!headline && !v) {
307 headline = f.error();
316 first_error = headline ? headline->message : std::string{};
320 std::vector<FieldHooks> fields_;
321 std::vector<Rule> rules_;
322 SubscriptionBag bag_;
323 std::size_t revision_ = 0;
Definition validator.hpp:135
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
const std::string & field_path() const noexcept
Definition validator.hpp:167
Definition property.hpp:103
::aria::Subscription on_changed(std::function< void(const T &)> fn)
Run fn(new_value) every time the value changes.
Definition property.hpp:215
Definition binding_engine.hpp:25
auto batch(Fn &&fn) -> decltype(fn())
Sugar: batch([&]{ firstName = "..."; lastName = "..."; }).
Definition graph.hpp:369
validation_dsl::RulePart rule_id(std::string id) noexcept
Definition validation_key.hpp:96
Definition validation_key.hpp:110
static Error validation(ValidationKey k, std::string msg)
Hard validation error.
Definition error.hpp:170
std::string source
Free-form locator: which subsystem produced this.
Definition error.hpp:136
Definition validator.hpp:34
std::vector< Error > errors
Definition validator.hpp:36
bool valid
Definition validator.hpp:35
Definition validator.hpp:61