vocabulary.h 4.8 KB

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