#pragma once #include #include #include #include namespace jvalidate::detail { template struct ParserContext; } namespace jvalidate::vocabulary { /** * @brief Metadata tag for marking a keyword as no longer supported. This is * needed because we store the constraints by version using * std::map::lower_bound, instead of needing to pound them out for every version * of the JSON Schema specification. * * While it is permitted to reuse keywords that have been removed, it should be * avoided to minimize confusion. */ constexpr struct { } Removed; constexpr struct { } Literal; /** * @brief Metadata tag for marking a keyword as containing either a single * subschema, or an array of subschema (e.g. "not", "oneOf", etc.). When parsing * a schema, we need to be able to identify these to search for "$id" and * "$anchor" tags, as they can allow us to jump into otherwise unreachable * sections of the schema. */ constexpr struct { } Keyword; /** * @brief Metadata tag for marking a keyword as containing a map of names onto * subschemas (e.g. "properties"). Because the keys in this node of the schema * are arbitrary strings, we need to jump past them whe searching for the "$id" * and "$anchor" tags. * We cannot simply do a blind recursive-walk of the schema JSON, because you * could put an "$id" tag in an "example" block, where it should not be scanned. */ constexpr struct { } KeywordMap; /** * @brief Metadata tag for marking a keyword as needing to wait until after all * other (non-PostConstraint) keywords are validated. * This tag is used specifically to mark "unevaluatedItems" and * "unevaluatedProperties", since the rules they use to decide where to run * cannot be compiled the way that "additionalItems" and "additionalProperties" * can. */ constexpr struct { } PostConstraint; /** * @brief A Metadata tag for marking a keyword as participating as dependent on * another keyword in order to generate annotations or validate instances. * However - we still must evaluate the keyword for "$id" and "$anchor" tags, * since jumping past the keyword is permissible. * Currently the only example of this is the handling of "if"/"then"/"else", * where the "then" and "else" clauses should not be directly used if the "if" * clause is not present in the schema. */ struct DependentKeyword : std::string_view {}; enum class KeywordType : char { None, Keyword, KeywordMap }; /** * @brief This type represents a union of several different "parsing handler" * states, aligned to a specific starting version: * A) Annotation keywords * A.1) Removed - This keyword is no longer supported. * A.2) Literal - This is a pure annotation. e.g. "$comment". * A.3) KeywordMap - This keyword does not produce any constraints, * but must be evaluated for annotations and anchors. e.g. "$defs". * A.4) DependentKeyword(id) - A keyword whose annotations are only evaluated * if the depended on keyword is also present in the current schema. It * may be connected to a constraint, but that constraint will be parsed * in the depended keyword. * * B) Constraint keywords * B.1) Make - A parser that does not contain any subschemas. * B.2) Make, Keyword - A parser that contains either a single * subschema or an array of subschemas. e.g. "not", "oneOf". * B.3) Make, KeywordMap - A parser that contains a key-value mapping onto * subschemas. e.g. "properties". * B.4) Make, PostConstraint - A parser whose constraint applies in * Unevaluated Locations (11). Assumed to be a single subschema. */ template struct Metadata { using pConstraint = std::unique_ptr; Metadata(decltype(Removed)) : valid(false) {} Metadata(decltype(Literal)) {} Metadata(decltype(KeywordMap)) : type(KeywordType::KeywordMap) {} Metadata(DependentKeyword dep) : type(KeywordType::Keyword) {} template Metadata(F make) : make(make) {} template Metadata(F make, decltype(Keyword)) : make(make), type(KeywordType::Keyword) {} template Metadata(F make, decltype(KeywordMap)) : make(make), type(KeywordType::KeywordMap) {} template Metadata(F make, decltype(PostConstraint)) : make(make), type(KeywordType::Keyword), is_post_constraint(true) {} explicit operator bool() const { return valid; } operator std::function const &)>() const { return make; } std::function const &)> make = nullptr; bool valid = true; KeywordType type = KeywordType::None; bool is_post_constraint = false; }; }