validator.h 6.1 KB

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