#pragma once #include #include #include #include #include namespace jvalidate::detail { template struct ParserContext; template class Vocabulary { public: friend class ConstraintFactory; using pConstraint = std::unique_ptr; using MakeConstraint = std::function const &)>; private: schema::Version version_; std::unordered_map make_; std::unordered_set permitted_; std::unordered_set vocabularies_; // TODO(samjaffe): Migrate this back to constraintsfactory std::unordered_set keywords_{"$defs", "additionalItems", "additionalProperties", "allOf", "anyOf", "definitions", "dependencies", "dependentSchemas", "else", "extends", "if", "items", "not", "oneOf", "patternProperties", "prefixItems", "properties", "then", "unevaluatedItems", "unevaluatedProperties"}; std::unordered_set property_keywords_{ "$defs", "definitions", "dependencies", "dependentSchemas", "patternProperties", "properties"}; std::unordered_set post_constraints_{"unevaluatedItems", "unevaluatedProperties"}; public: Vocabulary() = default; Vocabulary(schema::Version version, std::unordered_map make) : version_(version), make_(std::move(make)) { for (auto const & [keyword, _] : make_) { permitted_.emplace(keyword); } } void restrict(std::unordered_set const & permitted_keywords, std::unordered_set const & vocabularies) & { permitted_.clear(); vocabularies_ = vocabularies; for (auto const & [keyword, _] : make_) { if (permitted_keywords.contains(std::string(keyword))) { permitted_.insert(keyword); } } } schema::Version version() const { return version_; } bool is_format_assertion() const { // In Draft07 and prior - format assertions were considered enabled by // default. This is - of course - problematic because very few // implementations actually had full support for format constraints. if (version_ < schema::Version::Draft2019_09) { return true; } // Some implementations wouldn't even bother with format constraints, and // others would provide implementations that either missed a number of edge // cases or were flat-out wrong on certail matters. // Therefore - starting in Draft 2019-09, the format keyword is an // annotation by default, instead of an assertion. if (version_ == schema::Version::Draft2019_09) { return permitted_.contains("/vocab/format"); } // Draft 2020-12 makes this even more explicit - having separate vocabulary // documents for "format as assertion" and "format as annotation". Allowing // validators to add format constraints that are only used for annotating // results. return vocabularies_.contains("/vocab/format-assertion"); } /** * @brief Is the given "key"word actually a keyword? As in, would * I expect to resolve a constraint out of it. */ bool is_keyword(std::string_view word) const { return permitted_.contains(word) && make_.contains(word) && keywords_.contains(word); } /** * @brief Does the given "key"word represent a property object - that is to * say, an object containing some number of schemas mapped by arbitrary keys */ bool is_property_keyword(std::string_view word) const { return is_keyword(word) && property_keywords_.contains(word); } bool is_constraint(std::string_view word) const { return permitted_.contains(word) && make_.contains(word) && make_.at(word); } auto constraint(std::string_view word, ParserContext const & context) const { return std::make_pair(is_constraint(word) ? make_.at(word)(context) : nullptr, post_constraints_.contains(word)); } }; }