|
20 | 20 | #include <string_view> |
21 | 21 | #include <type_traits> |
22 | 22 | #include <utility> |
| 23 | +#include <vector> |
23 | 24 |
|
24 | 25 | #include <vix/validation/Schema.hpp> |
25 | 26 | #include <vix/validation/ValidationError.hpp> |
@@ -94,6 +95,43 @@ namespace vix::validation |
94 | 95 | { |
95 | 96 | }; |
96 | 97 |
|
| 98 | + /** |
| 99 | + * @brief Detects: static bool set(Derived&, std::string_view, std::string_view). |
| 100 | + * |
| 101 | + * Optional ultra-minimal KV binder for examples and simple forms. |
| 102 | + */ |
| 103 | + template <typename Derived, typename = void> |
| 104 | + struct has_kv_set : std::false_type |
| 105 | + { |
| 106 | + }; |
| 107 | + |
| 108 | + template <typename Derived> |
| 109 | + struct has_kv_set<Derived, std::void_t<decltype(Derived::set( |
| 110 | + std::declval<Derived &>(), |
| 111 | + std::declval<std::string_view>(), |
| 112 | + std::declval<std::string_view>()))>> : std::true_type |
| 113 | + { |
| 114 | + }; |
| 115 | + |
| 116 | + template <typename Derived> |
| 117 | + inline constexpr bool has_kv_set_v = has_kv_set<Derived>::value; |
| 118 | + |
| 119 | + /** |
| 120 | + * @brief Detect KV input type: std::vector<std::pair<std::string_view, std::string_view>>. |
| 121 | + */ |
| 122 | + template <typename Input> |
| 123 | + struct is_kv_input : std::false_type |
| 124 | + { |
| 125 | + }; |
| 126 | + |
| 127 | + template <> |
| 128 | + struct is_kv_input<std::vector<std::pair<std::string_view, std::string_view>>> : std::true_type |
| 129 | + { |
| 130 | + }; |
| 131 | + |
| 132 | + template <typename Input> |
| 133 | + inline constexpr bool is_kv_input_v = is_kv_input<std::remove_cv_t<Input>>::value; |
| 134 | + |
97 | 135 | template <typename Derived, typename Input> |
98 | 136 | inline constexpr bool has_bind2_v = has_bind2<Derived, Input>::value; |
99 | 137 |
|
@@ -331,12 +369,27 @@ namespace vix::validation |
331 | 369 | return FormResult<cleaned_type>(std::move(errors)); |
332 | 370 | } |
333 | 371 | } |
| 372 | + else if constexpr (detail::is_kv_input_v<Input> && detail::has_kv_set_v<Derived>) |
| 373 | + { |
| 374 | + for (const auto &kv : in) |
| 375 | + { |
| 376 | + const bool ok = static_cast<bool>(Derived::set(form, kv.first, kv.second)); |
| 377 | + if (!ok) |
| 378 | + { |
| 379 | + errors.add(detail::make_form_error( |
| 380 | + "unknown or invalid field: " + std::string(kv.first), |
| 381 | + ValidationErrorCode::Format)); |
| 382 | + return FormResult<cleaned_type>(std::move(errors)); |
| 383 | + } |
| 384 | + } |
| 385 | + } |
334 | 386 | else |
335 | 387 | { |
336 | 388 | static_assert(vix::validation::detail::dependent_false_v<Input>, |
337 | 389 | "Form::validate(Input): Derived must implement a compatible bind(). " |
338 | 390 | "Expected: static bool bind(Derived&, const Input&, ValidationErrors&) " |
339 | | - "or static bool bind(Derived&, const Input&)."); |
| 391 | + "or static bool bind(Derived&, const Input&) " |
| 392 | + "or (KV input) static bool set(Derived&, std::string_view, std::string_view)."); |
340 | 393 | } |
341 | 394 |
|
342 | 395 | // 2) Validate using Schema<Derived> |
@@ -366,6 +419,20 @@ namespace vix::validation |
366 | 419 | } |
367 | 420 | } |
368 | 421 |
|
| 422 | + // helper KV input type used by validate_kv |
| 423 | + using kv_pair = std::pair<std::string_view, std::string_view>; |
| 424 | + using kv_list = std::initializer_list<kv_pair>; |
| 425 | + using kv_input = std::vector<kv_pair>; |
| 426 | + |
| 427 | + [[nodiscard]] static FormResult<cleaned_type> validate_kv(kv_list kv) |
| 428 | + { |
| 429 | + kv_input in; |
| 430 | + in.reserve(kv.size()); |
| 431 | + for (const auto &p : kv) |
| 432 | + in.push_back(p); |
| 433 | + return validate(in); |
| 434 | + } |
| 435 | + |
369 | 436 | /** |
370 | 437 | * @brief Access the cached schema associated with this form type. |
371 | 438 | * |
|
0 commit comments