#pragma once #include #include #include #include #include #include namespace jvalidate::detail { /** * @brief An implementation of a regular expression "engine", for use with * constraints like "pattern" and "patternProperties". * Uses std::regex as its underlying implementation. * * While being std::regex means that it is the most sensible choice for a * default RegexEngine, the performance of std::regex is generally the worst * among C++ regex utilities, and it struggles to compile several patterns. * See https://stackoverflow.com/questions/70583395/ for an explaination. * * If you need to use complicated patterns in your json schema, provide a * RegexEngine compatible wrapper for a different library, such as re2. */ class StdRegexEngine { private: std::unordered_map cache_; public: bool search(std::string const & regex, std::string const & text) { auto const & re = cache_.try_emplace(regex, regex).first->second; return std::regex_search(text, re); } }; /** * @brief An implementation of an "Extension Constraint Visitor" plugin that * does nothing. */ struct StubExtensionVisitor {}; } namespace jvalidate { /** * @brief A validator is the tool by which a JSON object is actually validated * against a schema. * * @tparam RE A type that can be used to solve regular expressions */ template class Validator { private: schema::Node const & schema_; ValidationConfig cfg_; ExtensionVisitor extension_; RE regex_; public: /** * @brief Construct a Validator * * @param schema The root schema being validated against. Must outlive this. * * @param cfg Any special (runtime) configuration rules being applied to the * validator. */ Validator(schema::Node const & schema, ExtensionVisitor extension = {}, ValidationConfig const & cfg = {}) : schema_(schema), cfg_(cfg), extension_(extension) {} Validator(schema::Node const & schema, ValidationConfig const & cfg) : schema_(schema), cfg_(cfg) {} template Validator(schema::Node &&, Args &&...) = delete; /** * @brief Run validation on the given JSON * * @tparam A Any Adapter type, in principle a subclass of adapter::Adapter. * Disallows mutation via ValidationConfig.construct_default_values * * @param json The value being validated * * @param result An optional out-param of detailed information about * validation failures. If result is not provided, then the validator will * terminate on the first error. Otherwise it will run through the entire * schema to provide a record of all of the failures. */ template requires(not MutableAdapter) bool validate(A const & json, ValidationResult * result = nullptr) { EXPECT_M(not cfg_.construct_default_values, "Cannot perform mutations on an immutable JSON Adapter"); return static_cast( ValidationVisitor(schema_, cfg_, regex_, extension_, result).validate(json)); } /** * @brief Run validation on the given JSON * * @tparam A Any Adapter type that supports assignment, in principle a * subclass of adapter::Adapter. * * @param json The value being validated. Because A is a reference-wrapper, * the underlying value may be mutated. * * @param result An optional out-param of detailed information about * validation failures. If result is not provided, then the validator will * terminate on the first error. Otherwise it will run through the entire * schema to provide a record of all of the failures. */ template bool validate(A const & json, ValidationResult * result = nullptr) { return static_cast( ValidationVisitor(schema_, cfg_, regex_, extension_, result).validate(json)); } /** * @brief Run validation on the given JSON * * @tparam JSON A concrete JSON type. Will be turned into an Adapter, or a * MutableAdapter (if json is non-const and exists). * * @param json The value being validated. * * @param result An optional out-param of detailed information about * validation failures. If result is not provided, then the validator will * terminate on the first error. Otherwise it will run through the entire * schema to provide a record of all of the failures. */ template requires(not Adapter) bool validate(JSON & json, ValidationResult * result = nullptr) { return validate(adapter::AdapterFor(json), result); } }; }