22 container_schema& container_schema::require(std::string_view key, value_types type)
24 field_def field(key, type,
true);
25 fields_.push_back(std::move(field));
29 container_schema& container_schema::optional(std::string_view key, value_types type)
31 field_def field(key, type,
false);
32 fields_.push_back(std::move(field));
36 container_schema& container_schema::require(std::string_view key, value_types type,
37 const container_schema& nested_schema)
39 field_def field(key, type,
true);
40 field.nested_schema = std::make_unique<container_schema>(nested_schema);
41 fields_.push_back(std::move(field));
45 container_schema& container_schema::optional(std::string_view key, value_types type,
46 const container_schema& nested_schema)
48 field_def field(key, type,
false);
49 field.nested_schema = std::make_unique<container_schema>(nested_schema);
50 fields_.push_back(std::move(field));
58 container_schema& container_schema::range_int64(std::string_view key, int64_t min, int64_t max)
60 if (
auto* field = find_field(key))
68 container_schema& container_schema::range_double(std::string_view key,
double min,
double max)
70 if (
auto* field = find_field(key))
72 field->min_double = min;
73 field->max_double = max;
78 container_schema& container_schema::length(std::string_view key,
size_t min,
size_t max)
80 if (
auto* field = find_field(key))
82 field->min_length = min;
83 field->max_length = max;
88 container_schema& container_schema::pattern(std::string_view key, std::string_view regex_pattern)
90 if (
auto* field = find_field(key))
92 field->pattern_str = std::string(regex_pattern);
95 field->compiled_pattern = std::regex(std::string(regex_pattern));
97 catch (
const std::regex_error&)
100 field->compiled_pattern = std::nullopt;
106 container_schema& container_schema::one_of(std::string_view key, std::vector<std::string> allowed)
108 if (
auto* field = find_field(key))
110 field->allowed_values = std::move(allowed);
115 container_schema& container_schema::custom(std::string_view key, validator_fn validator)
117 if (
auto* field = find_field(key))
119 field->custom_validators.push_back(std::move(validator));
128 std::optional<validation_error> container_schema::validate(
129 const value_container& container)
const noexcept
131 auto errors = validate_all(container);
136 return errors.front();
139 std::vector<validation_error> container_schema::validate_all(
140 const value_container& container)
const noexcept
142 std::vector<validation_error> errors;
145 for (
const auto& field : fields_)
147 auto value_opt = container.get(field.name);
149 if (!value_opt.has_value())
154 errors.push_back(validation_error::missing_required(field.name));
160 validate_field(field, value_opt.value(), errors);
166#if SCHEMA_HAS_COMMON_RESULT
167 kcenon::common::VoidResult container_schema::validate_result(
168 const value_container& container)
const noexcept
170 auto error_opt = validate(container);
171 if (!error_opt.has_value())
173 return kcenon::common::ok();
176 const auto& err = error_opt.value();
177 return kcenon::common::VoidResult(
178 kcenon::common::error_info{
181 "container_schema"});
185 bool container_schema::has_field(std::string_view key)
const noexcept
187 return find_field(key) !=
nullptr;
190 bool container_schema::is_required(std::string_view key)
const noexcept
192 const auto* field = find_field(key);
193 return field !=
nullptr && field->required;
200 container_schema::field_def* container_schema::find_field(std::string_view key)
noexcept
202 auto it = std::find_if(fields_.begin(), fields_.end(),
203 [key](
const field_def& f) { return f.name == key; });
204 return it != fields_.end() ? &(*it) :
nullptr;
207 const container_schema::field_def* container_schema::find_field(std::string_view key)
const noexcept
209 auto it = std::find_if(fields_.begin(), fields_.end(),
210 [key](
const field_def& f) { return f.name == key; });
211 return it != fields_.end() ? &(*it) :
nullptr;
214 bool container_schema::validate_field(
const field_def& field,
215 const optimized_value& value,
216 std::vector<validation_error>& errors)
const noexcept
221 if (!validate_type(field, value, errors))
227 if (!validate_range(field, value, errors))
233 if (!validate_length(field, value, errors))
239 if (!validate_pattern(field, value, errors))
245 if (!validate_allowed(field, value, errors))
251 if (!validate_custom(field, value, errors))
257 if (!validate_nested(field, value, errors))
265 bool container_schema::validate_type(
const field_def& field,
266 const optimized_value& value,
267 std::vector<validation_error>& errors)
const noexcept
269 if (value.type != field.type)
271 errors.push_back(validation_error::type_mismatch(
272 field.name, field.type, value.type));
278 bool container_schema::validate_range(
const field_def& field,
279 const optimized_value& value,
280 std::vector<validation_error>& errors)
const noexcept
283 if (field.min_int.has_value() && field.max_int.has_value())
286 bool has_value =
false;
290 case value_types::short_value:
291 if (
auto* p = std::get_if<short>(&value.data))
297 case value_types::ushort_value:
298 if (
auto* p = std::get_if<unsigned short>(&value.data))
304 case value_types::int_value:
305 if (
auto* p = std::get_if<int>(&value.data))
311 case value_types::uint_value:
312 if (
auto* p = std::get_if<unsigned int>(&value.data))
318 case value_types::long_value:
319 if (
auto* p = std::get_if<long>(&value.data))
325 case value_types::ulong_value:
326 if (
auto* p = std::get_if<unsigned long>(&value.data))
328 val =
static_cast<int64_t
>(*p);
332 case value_types::llong_value:
333 if (
auto* p = std::get_if<long long>(&value.data))
339 case value_types::ullong_value:
340 if (
auto* p = std::get_if<unsigned long long>(&value.data))
342 val =
static_cast<int64_t
>(*p);
352 if (val < field.min_int.value() || val > field.max_int.value())
354 errors.push_back(validation_error::out_of_range(
355 field.name, val, field.min_int.value(), field.max_int.value()));
362 if (field.min_double.has_value() && field.max_double.has_value())
365 bool has_value =
false;
367 if (value.type == value_types::float_value)
369 if (
auto* p = std::get_if<float>(&value.data))
371 val =
static_cast<double>(*p);
375 else if (value.type == value_types::double_value)
377 if (
auto* p = std::get_if<double>(&value.data))
386 if (val < field.min_double.value() || val > field.max_double.value())
388 errors.push_back(validation_error::out_of_range(
389 field.name, val, field.min_double.value(), field.max_double.value()));
398 bool container_schema::validate_length(
const field_def& field,
399 const optimized_value& value,
400 std::vector<validation_error>& errors)
const noexcept
402 if (!field.min_length.has_value() || !field.max_length.has_value())
408 bool has_length =
false;
410 if (value.type == value_types::string_value)
412 if (
auto* p = std::get_if<std::string>(&value.data))
418 else if (value.type == value_types::bytes_value)
420 if (
auto* p = std::get_if<std::vector<uint8_t>>(&value.data))
429 if (len < field.min_length.value() || len > field.max_length.value())
431 errors.push_back(validation_error::invalid_length(
432 field.name, len, field.min_length.value(), field.max_length.value()));
440 bool container_schema::validate_pattern(
const field_def& field,
441 const optimized_value& value,
442 std::vector<validation_error>& errors)
const noexcept
444 if (!field.compiled_pattern.has_value())
449 if (value.type != value_types::string_value)
454 auto* str_ptr = std::get_if<std::string>(&value.data);
462 if (!std::regex_match(*str_ptr, field.compiled_pattern.value()))
464 errors.push_back(validation_error::pattern_mismatch(
465 field.name, field.pattern_str.value_or(
"")));
469 catch (
const std::regex_error&)
472 errors.push_back(validation_error::pattern_mismatch(
473 field.name, field.pattern_str.value_or(
"")));
480 bool container_schema::validate_allowed(
const field_def& field,
481 const optimized_value& value,
482 std::vector<validation_error>& errors)
const noexcept
484 if (!field.allowed_values.has_value())
489 if (value.type != value_types::string_value)
494 auto* str_ptr = std::get_if<std::string>(&value.data);
500 const auto& allowed = field.allowed_values.value();
501 auto it = std::find(allowed.begin(), allowed.end(), *str_ptr);
502 if (it == allowed.end())
504 errors.push_back(validation_error::not_allowed(field.name, *str_ptr));
511 bool container_schema::validate_custom(
const field_def& field,
512 const optimized_value& value,
513 std::vector<validation_error>& errors)
const noexcept
515 for (
const auto& validator : field.custom_validators)
519 auto result = validator(value);
520 if (result.has_value())
522 errors.push_back(validation_error::custom_failed(
523 field.name, result.value()));
529 errors.push_back(validation_error::custom_failed(
530 field.name,
"Validator threw an exception"));
537 bool container_schema::validate_nested(
const field_def& field,
538 const optimized_value& value,
539 std::vector<validation_error>& errors)
const noexcept
541 if (!field.nested_schema)
546 if (value.type != value_types::container_value)
551 const auto* container_ptr = std::get_if<std::shared_ptr<value_container>>(&value.data);
552 if (container_ptr ==
nullptr || !(*container_ptr))
554 errors.push_back(validation_error::nested_failed(field.name, {}));
558 auto nested_errors = field.nested_schema->validate_all(**container_ptr);
559 if (!nested_errors.empty())
561 errors.push_back(validation_error::nested_failed(field.name, nested_errors));
563 for (
auto& err : nested_errors)
565 err.field = field.name +
"." + err.field;
566 errors.push_back(std::move(err));