#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 { public: std::regex regex_; public: StdRegexEngine(std::string const & regex) : regex_(detail::regex_escape(regex)) {} bool search(std::string const & text) const { return std::regex_search(text, regex_); } }; } 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 ValidatorT { private: schema::Node const & schema_; ValidationConfig cfg_; std::unordered_map regex_cache_; 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. */ ValidatorT(schema::Node const & schema, ValidationConfig const & cfg = {}) : schema_(schema), cfg_(cfg) {} /** * @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(json, schema_, cfg_, regex_cache_, result).validate()); } /** * @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(json, schema_, cfg_, regex_cache_, result).validate()); } /** * @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); } }; /** * @brief Syntactic sugar for ValidatorT<>. */ class Validator : public ValidatorT<> { public: using Validator::ValidatorT::ValidatorT; }; }