|
@@ -37,7 +37,7 @@ protected:
|
|
|
public:
|
|
public:
|
|
|
Node() = default;
|
|
Node() = default;
|
|
|
Node(std::string const & rejection_reason) : rejects_all_(rejection_reason) {}
|
|
Node(std::string const & rejection_reason) : rejects_all_(rejection_reason) {}
|
|
|
- template <Adapter A> Node(detail::ParserContext<A> context);
|
|
|
|
|
|
|
+ template <Adapter A> void construct(detail::ParserContext<A> context);
|
|
|
|
|
|
|
|
bool is_pure_reference() const {
|
|
bool is_pure_reference() const {
|
|
|
return reference_ && constraints_.empty() && post_constraints_.empty() && not default_;
|
|
return reference_ && constraints_.empty() && post_constraints_.empty() && not default_;
|
|
@@ -176,7 +176,7 @@ public:
|
|
|
|
|
|
|
|
external.cache_reference(URI(), json);
|
|
external.cache_reference(URI(), json);
|
|
|
detail::ParserContext<A> root{*this, json, version, factory, external};
|
|
detail::ParserContext<A> root{*this, json, version, factory, external};
|
|
|
- schema::Node::operator=(root);
|
|
|
|
|
|
|
+ construct(root);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -299,7 +299,7 @@ private:
|
|
|
std::optional schema = context.external.try_load(ref);
|
|
std::optional schema = context.external.try_load(ref);
|
|
|
if (not schema.has_value()) {
|
|
if (not schema.has_value()) {
|
|
|
std::string error = "URIResolver could not resolve " + std::string(ref.uri());
|
|
std::string error = "URIResolver could not resolve " + std::string(ref.uri());
|
|
|
- return alias(context.where, &cache_.try_emplace(context.where, error).first->second);
|
|
|
|
|
|
|
+ return alias(ref, &cache_.try_emplace(ref, error).first->second);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return fetch_schema(context.rebind(*schema, ref));
|
|
return fetch_schema(context.rebind(*schema, ref));
|
|
@@ -332,7 +332,7 @@ private:
|
|
|
|
|
|
|
|
// Do this here first in order to protect from infinite loops
|
|
// Do this here first in order to protect from infinite loops
|
|
|
alias(context.where, &it->second);
|
|
alias(context.where, &it->second);
|
|
|
- it->second = schema::Node(context);
|
|
|
|
|
|
|
+ it->second.construct(context);
|
|
|
if (not it->second.is_pure_reference()) {
|
|
if (not it->second.is_pure_reference()) {
|
|
|
return &it->second;
|
|
return &it->second;
|
|
|
}
|
|
}
|
|
@@ -424,7 +424,7 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-template <Adapter A> Node::Node(detail::ParserContext<A> context) {
|
|
|
|
|
|
|
+template <Adapter A> void Node::construct(detail::ParserContext<A> context) {
|
|
|
EXPECT(context.schema.type() == adapter::Type::Object);
|
|
EXPECT(context.schema.type() == adapter::Type::Object);
|
|
|
|
|
|
|
|
auto const schema = context.schema.as_object();
|
|
auto const schema = context.schema.as_object();
|
|
@@ -459,21 +459,32 @@ template <Adapter A> Node::Node(detail::ParserContext<A> context) {
|
|
|
description_ = schema["description"].as_string();
|
|
description_ = schema["description"].as_string();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- for (auto [key, subschema] : schema) {
|
|
|
|
|
|
|
+ // Prior to Draft 2019-09, reference keywords take precedence over everything
|
|
|
|
|
+ // else (instead of allowing direct extensions).
|
|
|
|
|
+ if (has_reference && context.version < Version::Draft2019_09) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (auto const & [key, subschema] : schema) {
|
|
|
// Using a constraint store allows overriding certain rules, or the creation
|
|
// Using a constraint store allows overriding certain rules, or the creation
|
|
|
// of user-defined extention vocabularies.
|
|
// of user-defined extention vocabularies.
|
|
|
- if (auto make_constraint = context.factory(key, context.version)) {
|
|
|
|
|
- EXPECT_M(not has_reference || context.version >= Version::Draft2019_09,
|
|
|
|
|
- "Cannot directly extend $ref schemas before Draft2019-09");
|
|
|
|
|
- // A constraint may return null if it is not applicable - but otherwise
|
|
|
|
|
- // well-formed. For example, before Draft-06 "exclusiveMaximum" was a
|
|
|
|
|
- // modifier property for "maximum", and not a unique constaint on its own.
|
|
|
|
|
- // Therefore, we parse it alongside parsing "maximum", and could return
|
|
|
|
|
- // nullptr when requesting a constraint pointer for "exclusiveMaximum".
|
|
|
|
|
- if (auto constraint = make_constraint(context.child(subschema, key))) {
|
|
|
|
|
- auto & into = context.factory.is_post_constraint(key) ? post_constraints_ : constraints_;
|
|
|
|
|
- into.emplace(key, std::move(constraint));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ auto make_constraint = context.factory(key, context.version);
|
|
|
|
|
+ if (not make_constraint) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ // A constraint may return null if it is not applicable - but otherwise
|
|
|
|
|
+ // well-formed. For example, before Draft-06 "exclusiveMaximum" was a
|
|
|
|
|
+ // modifier property for "maximum", and not a unique constaint on its own.
|
|
|
|
|
+ // Therefore, we parse it alongside parsing "maximum", and could return
|
|
|
|
|
+ // nullptr when requesting a constraint pointer for "exclusiveMaximum".
|
|
|
|
|
+ auto constraint = make_constraint(context.child(subschema, key));
|
|
|
|
|
+ if (not constraint) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (context.factory.is_post_constraint(key)) {
|
|
|
|
|
+ post_constraints_.emplace(key, std::move(constraint));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ constraints_.emplace(key, std::move(constraint));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|