validator.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #pragma once
  2. #include <jvalidate/_config.h> // IWYU pragma: keep
  3. #include <jvalidate/_macro.h>
  4. #include <jvalidate/detail/expect.h>
  5. #include <jvalidate/detail/on_block_exit.h>
  6. #include <jvalidate/forward.h>
  7. #include <jvalidate/regex.h>
  8. #include <jvalidate/validation_config.h>
  9. #include <jvalidate/validation_visitor.h>
  10. namespace jvalidate::detail {
  11. /**
  12. * @brief An implementation of an "Extension Constraint Visitor" plugin that
  13. * does nothing.
  14. */
  15. struct StubExtensionVisitor {};
  16. }
  17. namespace jvalidate {
  18. /**
  19. * @brief A validator is the tool by which a JSON object is actually validated
  20. * against a schema.
  21. *
  22. * @tparam RE A type that can be used to solve regular expressions
  23. */
  24. template <RegexEngine RE = JVALIDATE_IIF(JVALIDATE_HAS_ICU, ICURegexEngine, StdRegexEngine),
  25. typename ExtensionVisitor = detail::StubExtensionVisitor>
  26. class Validator {
  27. private:
  28. schema::Node const & schema_;
  29. ValidationConfig cfg_;
  30. ExtensionVisitor extension_{};
  31. RE regex_{};
  32. FormatValidator format_{RE::is_regex};
  33. public:
  34. /**
  35. * @brief Construct a Validator
  36. *
  37. * @param schema The root schema being validated against. Must outlive this.
  38. *
  39. * @param cfg Any special (runtime) configuration rules being applied to the
  40. * validator.
  41. */
  42. explicit Validator(schema::Node const & schema, ValidationConfig const & cfg = {})
  43. : schema_(schema), cfg_(cfg) {}
  44. /**
  45. * @brief Construct a Validator
  46. *
  47. * @param schema The root schema being validated against. Must outlive this.
  48. *
  49. * @param extension An extension visitor for processing user-defined
  50. * constraints
  51. *
  52. * @param cfg Any special (runtime) configuration rules being applied to the
  53. * validator.
  54. */
  55. explicit Validator(schema::Node const & schema, ExtensionVisitor extension,
  56. ValidationConfig const & cfg = {})
  57. : schema_(schema), cfg_(cfg), extension_(extension) {}
  58. /**
  59. * @brief Construct a Validator
  60. *
  61. * @param schema The root schema being validated against. Must outlive this.
  62. *
  63. * @param user_defined_formats A map of format-name to string validator for
  64. * user-defined format tools.
  65. *
  66. * @param cfg Any special (runtime) configuration rules being applied to the
  67. * validator. Because user_defined_formats is provided by this constructor,
  68. * we default to validate_format:=true.
  69. */
  70. Validator(schema::Node const & schema,
  71. FormatValidator::UserDefinedFormats const & user_defined_formats,
  72. ValidationConfig const & cfg = {.validate_format = true})
  73. : schema_(schema), cfg_(cfg), format_(user_defined_formats, RE::is_regex) {}
  74. /**
  75. * @brief Construct a Validator
  76. *
  77. * @param schema The root schema being validated against. Must outlive this.
  78. *
  79. * @param extension An extension visitor for processing user-defined
  80. * constraints
  81. *
  82. * @param user_defined_formats A map of format-name to string validator for
  83. * user-defined format tools.
  84. *
  85. * @param cfg Any special (runtime) configuration rules being applied to the
  86. * validator. Because user_defined_formats is provided by this constructor,
  87. * we default to validate_format:=true.
  88. */
  89. Validator(schema::Node const & schema, ExtensionVisitor extension,
  90. FormatValidator::UserDefinedFormats const & user_defined_formats,
  91. ValidationConfig const & cfg = {.validate_format = true})
  92. : schema_(schema), cfg_(cfg), extension_(extension),
  93. format_(user_defined_formats, RE::is_regex) {}
  94. template <typename... Args> Validator(schema::Node &&, Args &&...) = delete;
  95. /**
  96. * @brief Run validation on the given JSON
  97. *
  98. * @tparam A Any Adapter type, in principle a subclass of adapter::Adapter.
  99. * Disallows mutation via ValidationConfig.construct_default_values
  100. *
  101. * @param json The value being validated
  102. *
  103. * @param result An optional out-param of detailed information about
  104. * validation failures. If result is not provided, then the validator will
  105. * terminate on the first error. Otherwise it will run through the entire
  106. * schema to provide a record of all of the failures.
  107. */
  108. template <Adapter A>
  109. requires(not MutableAdapter<A>)
  110. bool validate(A const & json, ValidationResult * result = nullptr) {
  111. EXPECT_M(not cfg_.construct_default_values,
  112. "Cannot perform mutations on an immutable JSON Adapter");
  113. detail::OnBlockExit const _ = [&result, this]() { post_process(result); };
  114. ValidationVisitor visitor(schema_, json, cfg_, regex_, format_, extension_, result);
  115. return static_cast<bool>(visitor.validate(json));
  116. }
  117. /**
  118. * @brief Run validation on the given JSON
  119. *
  120. * @tparam A Any Adapter type that supports assignment, in principle a
  121. * subclass of adapter::Adapter.
  122. *
  123. * @param json The value being validated. Because A is a reference-wrapper,
  124. * the underlying value may be mutated.
  125. *
  126. * @param result An optional out-param of detailed information about
  127. * validation failures. If result is not provided, then the validator will
  128. * terminate on the first error. Otherwise it will run through the entire
  129. * schema to provide a record of all of the failures.
  130. */
  131. template <MutableAdapter A> bool validate(A const & json, ValidationResult * result = nullptr) {
  132. detail::OnBlockExit const _ = [&result, this]() { post_process(result); };
  133. ValidationVisitor visitor(schema_, json, cfg_, regex_, format_, extension_, result);
  134. return static_cast<bool>(visitor.validate(json));
  135. }
  136. /**
  137. * @brief Run validation on the given JSON
  138. *
  139. * @tparam JSON A concrete JSON type. Will be turned into an Adapter, or a
  140. * MutableAdapter (if json is non-const and exists).
  141. *
  142. * @param json The value being validated.
  143. *
  144. * @param result An optional out-param of detailed information about
  145. * validation failures. If result is not provided, then the validator will
  146. * terminate on the first error. Otherwise it will run through the entire
  147. * schema to provide a record of all of the failures.
  148. */
  149. template <typename JSON>
  150. requires(not Adapter<JSON>)
  151. bool validate(JSON & json, ValidationResult * result = nullptr) {
  152. return validate(adapter::AdapterFor<JSON>(json), result);
  153. }
  154. private:
  155. void post_process(ValidationResult *& result) const {
  156. if (result == nullptr) {
  157. return;
  158. }
  159. if (cfg_.only_return_results_with_error) {
  160. *result = result->only_errors();
  161. }
  162. }
  163. };
  164. }