vocabulary.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #pragma once
  2. #include <functional>
  3. #include <memory>
  4. #include <string_view>
  5. #include <jvalidate/forward.h>
  6. namespace jvalidate::detail {
  7. template <Adapter A> struct ParserContext;
  8. }
  9. namespace jvalidate::vocabulary {
  10. /**
  11. * @brief Metadata tag for marking a keyword as no longer supported. This is
  12. * needed because we store the constraints by version using
  13. * std::map::lower_bound, instead of needing to pound them out for every version
  14. * of the JSON Schema specification.
  15. *
  16. * While it is permitted to reuse keywords that have been removed, it should be
  17. * avoided to minimize confusion.
  18. */
  19. constexpr struct {
  20. } Removed;
  21. constexpr struct {
  22. } Literal;
  23. /**
  24. * @brief Metadata tag for marking a keyword as containing either a single
  25. * subschema, or an array of subschema (e.g. "not", "oneOf", etc.). When parsing
  26. * a schema, we need to be able to identify these to search for "$id" and
  27. * "$anchor" tags, as they can allow us to jump into otherwise unreachable
  28. * sections of the schema.
  29. */
  30. constexpr struct {
  31. } Keyword;
  32. /**
  33. * @brief Metadata tag for marking a keyword as containing a map of names onto
  34. * subschemas (e.g. "properties"). Because the keys in this node of the schema
  35. * are arbitrary strings, we need to jump past them whe searching for the "$id"
  36. * and "$anchor" tags.
  37. * We cannot simply do a blind recursive-walk of the schema JSON, because you
  38. * could put an "$id" tag in an "example" block, where it should not be scanned.
  39. */
  40. constexpr struct {
  41. } KeywordMap;
  42. /**
  43. * @brief Metadata tag for marking a keyword as needing to wait until after all
  44. * other (non-PostConstraint) keywords are validated.
  45. * This tag is used specifically to mark "unevaluatedItems" and
  46. * "unevaluatedProperties", since the rules they use to decide where to run
  47. * cannot be compiled the way that "additionalItems" and "additionalProperties"
  48. * can.
  49. */
  50. constexpr struct {
  51. } PostConstraint;
  52. /**
  53. * @brief A Metadata tag for marking a keyword as participating as dependent on
  54. * another keyword in order to generate annotations or validate instances.
  55. * However - we still must evaluate the keyword for "$id" and "$anchor" tags,
  56. * since jumping past the keyword is permissible.
  57. * Currently the only example of this is the handling of "if"/"then"/"else",
  58. * where the "then" and "else" clauses should not be directly used if the "if"
  59. * clause is not present in the schema.
  60. */
  61. struct DependentKeyword : std::string_view {};
  62. enum class KeywordType : char { None, Keyword, KeywordMap };
  63. /**
  64. * @brief This type represents a union of several different "parsing handler"
  65. * states, aligned to a specific starting version:
  66. * A) Annotation keywords
  67. * A.1) Removed - This keyword is no longer supported.
  68. * A.2) Literal - This is a pure annotation. e.g. "$comment".
  69. * A.3) KeywordMap - This keyword does not produce any constraints,
  70. * but must be evaluated for annotations and anchors. e.g. "$defs".
  71. * A.4) DependentKeyword(id) - A keyword whose annotations are only evaluated
  72. * if the depended on keyword is also present in the current schema. It
  73. * may be connected to a constraint, but that constraint will be parsed
  74. * in the depended keyword.
  75. *
  76. * B) Constraint keywords
  77. * B.1) Make - A parser that does not contain any subschemas.
  78. * B.2) Make, Keyword - A parser that contains either a single
  79. * subschema or an array of subschemas. e.g. "not", "oneOf".
  80. * B.3) Make, KeywordMap - A parser that contains a key-value mapping onto
  81. * subschemas. e.g. "properties".
  82. * B.4) Make, PostConstraint - A parser whose constraint applies in
  83. * Unevaluated Locations (11). Assumed to be a single subschema.
  84. */
  85. template <Adapter A> struct Metadata {
  86. using pConstraint = std::unique_ptr<constraint::Constraint>;
  87. Metadata(decltype(Removed)) : valid(false) {}
  88. Metadata(decltype(Literal)) {}
  89. Metadata(decltype(KeywordMap)) : type(KeywordType::KeywordMap) {}
  90. Metadata(DependentKeyword dep) : type(KeywordType::Keyword) {}
  91. template <typename F> Metadata(F make) : make(make) {}
  92. template <typename F>
  93. Metadata(F make, decltype(Keyword)) : make(make), type(KeywordType::Keyword) {}
  94. template <typename F>
  95. Metadata(F make, decltype(KeywordMap)) : make(make), type(KeywordType::KeywordMap) {}
  96. template <typename F>
  97. Metadata(F make, decltype(PostConstraint))
  98. : make(make), type(KeywordType::Keyword), is_post_constraint(true) {}
  99. explicit operator bool() const { return valid; }
  100. operator std::function<pConstraint(detail::ParserContext<A> const &)>() const { return make; }
  101. std::function<pConstraint(detail::ParserContext<A> const &)> make = nullptr;
  102. bool valid = true;
  103. KeywordType type = KeywordType::None;
  104. bool is_post_constraint = false;
  105. };
  106. }