Cosmetic improvements for schema_parser.hpp and validation_results.hpp

This commit is contained in:
Tristan Penman 2020-07-06 11:30:41 +10:00
parent 71f4cdaa84
commit 217b990b00
2 changed files with 142 additions and 264 deletions

View File

@ -41,16 +41,13 @@ public:
kDraft7 kDraft7
}; };
/// Version of JSON Schema that should be expected when parsing
const Version version;
/** /**
* @brief Construct a new SchemaParser for a given version of JSON Schema * @brief Construct a new SchemaParser for a given version of JSON Schema
* *
* @param version Version of JSON Schema that will be expected * @param version Version of JSON Schema that will be expected
*/ */
explicit SchemaParser(const Version version = kDraft7) explicit SchemaParser(const Version version = kDraft7)
: version(version) { } : m_version(version) { }
/** /**
* @brief Release memory associated with custom ConstraintBuilders * @brief Release memory associated with custom ConstraintBuilders
@ -68,14 +65,13 @@ public:
template<typename AdapterType> template<typename AdapterType>
struct FunctionPtrs struct FunctionPtrs
{ {
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
DocumentType;
/// Templated function pointer type for fetching remote documents /// Templated function pointer type for fetching remote documents
typedef std::function< const DocumentType* (const std::string &uri) > FetchDoc ; typedef std::function<const DocumentType* (const std::string &uri)> FetchDoc;
/// Templated function pointer type for freeing fetched documents /// Templated function pointer type for freeing fetched documents
typedef std::function< void (const DocumentType *)> FreeDoc ; typedef std::function<void (const DocumentType *)> FreeDoc;
}; };
/** /**
@ -96,8 +92,7 @@ public:
* @todo Add additional checks for key conflicts, empty keys, and * @todo Add additional checks for key conflicts, empty keys, and
* potential restrictions relating to case sensitivity * potential restrictions relating to case sensitivity
*/ */
void addConstraintBuilder(const std::string &key, void addConstraintBuilder(const std::string &key, const ConstraintBuilder *builder)
const ConstraintBuilder *builder)
{ {
constraintBuilders.push_back(std::make_pair(key, builder)); constraintBuilders.push_back(std::make_pair(key, builder));
} }
@ -122,17 +117,14 @@ public:
typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = nullptr ) typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = nullptr )
{ {
if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) { if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) {
throw std::runtime_error( throw std::runtime_error("Remote document fetching can't be enabled without both fetch and free functions");
"Remote document fetching cannot be enabled without both "
"fetch and free functions");
} }
typename DocumentCache<AdapterType>::Type docCache; typename DocumentCache<AdapterType>::Type docCache;
SchemaCache schemaCache; SchemaCache schemaCache;
try { try {
resolveThenPopulateSchema(schema, node, node, schema, resolveThenPopulateSchema(schema, node, node, schema, opt::optional<std::string>(), "", fetchDoc, nullptr,
opt::optional<std::string>(), "", nullptr, docCache, schemaCache);
fetchDoc, nullptr, nullptr, docCache, schemaCache);
} catch (...) { } catch (...) {
freeDocumentCache<AdapterType>(docCache, freeDoc); freeDocumentCache<AdapterType>(docCache, freeDoc);
throw; throw;
@ -151,8 +143,7 @@ private:
template<typename AdapterType> template<typename AdapterType>
struct DocumentCache struct DocumentCache
{ {
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
DocumentType;
typedef std::map<std::string, const DocumentType*> Type; typedef std::map<std::string, const DocumentType*> Type;
}; };
@ -204,16 +195,15 @@ private:
* portions of URI provided by the resolution scope. * portions of URI provided by the resolution scope.
*/ */
virtual opt::optional<std::string> findAbsoluteDocumentUri( virtual opt::optional<std::string> findAbsoluteDocumentUri(
const opt::optional<std::string> resolutionScope, const opt::optional<std::string>& resolutionScope,
const opt::optional<std::string> documentUri) const opt::optional<std::string>& documentUri)
{ {
if (resolutionScope) { if (resolutionScope) {
if (documentUri) { if (documentUri) {
if (internal::uri::isUriAbsolute(*documentUri)) { if (internal::uri::isUriAbsolute(*documentUri)) {
return *documentUri; return *documentUri;
} else { } else {
return internal::uri::resolveRelativeUri( return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri);
*resolutionScope, *documentUri);
} }
} else { } else {
return *resolutionScope; return *resolutionScope;
@ -248,8 +238,7 @@ private:
if (itr == o.end()) { if (itr == o.end()) {
return false; return false;
} else if (!itr->second.asString(result)) { } else if (!itr->second.asString(result)) {
throw std::invalid_argument( throw std::invalid_argument("$ref property expected to contain string value.");
"$ref property expected to contain string value.");
} }
return true; return true;
@ -258,7 +247,7 @@ private:
/** /**
* Sanitise an optional JSON Pointer, trimming trailing slashes * Sanitise an optional JSON Pointer, trimming trailing slashes
*/ */
std::string sanitiseJsonPointer(const opt::optional<std::string> input) static std::string sanitiseJsonPointer(const opt::optional<std::string>& input)
{ {
if (input) { if (input) {
// Trim trailing slash(es) // Trim trailing slash(es)
@ -310,15 +299,14 @@ private:
* usage of the schema cache during development, and is not expected * usage of the schema cache during development, and is not expected
* to occur otherwise, even for malformed schemas. * to occur otherwise, even for malformed schemas.
*/ */
void updateSchemaCache(SchemaCache &schemaCache, static void updateSchemaCache(SchemaCache &schemaCache,
const std::vector<std::string> &keysToCreate, const std::vector<std::string> &keysToCreate,
const Subschema *schema) const Subschema *schema)
{ {
for (const std::string &keyToCreate : keysToCreate) { for (const std::string &keyToCreate : keysToCreate) {
const SchemaCache::value_type value(keyToCreate, schema); const SchemaCache::value_type value(keyToCreate, schema);
if (!schemaCache.insert(value).second) { if (!schemaCache.insert(value).second) {
throw std::logic_error( throw std::logic_error("Key '" + keyToCreate + "' already in schema cache.");
"Key '" + keyToCreate + "' already in schema cache.");
} }
} }
} }
@ -375,16 +363,13 @@ private:
// Construct a key that we can use to search the schema cache for // Construct a key that we can use to search the schema cache for
// a schema corresponding to the current node // a schema corresponding to the current node
const std::string schemaCacheKey = const std::string schemaCacheKey = currentScope ? (*currentScope + nodePath) : nodePath;
currentScope ? (*currentScope + nodePath) : nodePath;
// Retrieve an existing schema from the cache if possible // Retrieve an existing schema from the cache if possible
const Subschema *cachedPtr = const Subschema *cachedPtr = querySchemaCache(schemaCache, schemaCacheKey);
querySchemaCache(schemaCache, schemaCacheKey);
// Create a new schema otherwise // Create a new schema otherwise
const Subschema *subschema = cachedPtr ? cachedPtr : const Subschema *subschema = cachedPtr ? cachedPtr : rootSchema.createSubschema();
rootSchema.createSubschema();
// Add cache entries for keys belonging to any $ref nodes that were // Add cache entries for keys belonging to any $ref nodes that were
// visited before arriving at the current node // visited before arriving at the current node
@ -404,8 +389,7 @@ private:
// Returns a document URI if the reference points somewhere // Returns a document URI if the reference points somewhere
// other than the current document // other than the current document
const opt::optional<std::string> documentUri = const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
internal::json_reference::getJsonReferenceUri(jsonRef);
// Extract JSON Pointer from JSON Reference, with any trailing // Extract JSON Pointer from JSON Reference, with any trailing
// slashes removed so that keys in the schema cache end // slashes removed so that keys in the schema cache end
@ -417,12 +401,10 @@ private:
// scope. An absolute document URI will take precedence when // scope. An absolute document URI will take precedence when
// present, otherwise we need to resolve the URI relative to // present, otherwise we need to resolve the URI relative to
// the current resolution scope // the current resolution scope
const opt::optional<std::string> actualDocumentUri = const opt::optional<std::string> actualDocumentUri = findAbsoluteDocumentUri(currentScope, documentUri);
findAbsoluteDocumentUri(currentScope, documentUri);
// Construct a key to search the schema cache for an existing schema // Construct a key to search the schema cache for an existing schema
const std::string queryKey = actualDocumentUri ? const std::string queryKey = actualDocumentUri ? (*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
(*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
// Check for the second termination condition (found a $ref node that // Check for the second termination condition (found a $ref node that
// already has an entry in the schema cache) // already has an entry in the schema cache)
@ -441,8 +423,7 @@ private:
if (docCacheItr == docCache.end()) { if (docCacheItr == docCache.end()) {
// Resolve reference against remote document // Resolve reference against remote document
if (!fetchDoc) { if (!fetchDoc) {
throw std::runtime_error( throw std::runtime_error("Fetching of remote JSON References not enabled.");
"Fetching of remote JSON References not enabled.");
} }
// Returns a pointer to the remote document that was // Returns a pointer to the remote document that was
@ -453,9 +434,7 @@ private:
// Can't proceed without the remote document // Can't proceed without the remote document
if (!newDoc) { if (!newDoc) {
throw std::runtime_error( throw std::runtime_error("Failed to fetch referenced schema document: " + *actualDocumentUri);
"Failed to fetch referenced schema document: " +
*actualDocumentUri);
} }
typedef typename DocumentCache<AdapterType>::Type::value_type typedef typename DocumentCache<AdapterType>::Type::value_type
@ -579,7 +558,7 @@ private:
const AdapterType &rootNode, const AdapterType &rootNode,
const AdapterType &node, const AdapterType &node,
const Subschema &subschema, const Subschema &subschema,
const opt::optional<std::string> currentScope, const opt::optional<std::string>& currentScope,
const std::string &nodePath, const std::string &nodePath,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc, const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
const Subschema *parentSubschema, const Subschema *parentSubschema,
@ -593,7 +572,7 @@ private:
"appropriate Adapter implementation"); "appropriate Adapter implementation");
if (!node.isObject()) { if (!node.isObject()) {
if (version == kDraft7 && node.maybeBool()) { if (m_version == kDraft7 && node.maybeBool()) {
// Boolean schema // Boolean schema
if (!node.asBool()) { if (!node.asBool()) {
rootSchema.setAlwaysInvalid(&subschema, true); rootSchema.setAlwaysInvalid(&subschema, true);
@ -603,7 +582,7 @@ private:
std::string s; std::string s;
s += "Expected node at "; s += "Expected node at ";
s += nodePath; s += nodePath;
if (version == kDraft7) { if (m_version == kDraft7) {
s += " to contain schema object or boolean value; actual node type is: "; s += " to contain schema object or boolean value; actual node type is: ";
} else { } else {
s += " to contain schema object; actual node type is: "; s += " to contain schema object; actual node type is: ";
@ -618,15 +597,13 @@ private:
// Check for 'id' attribute and update current scope // Check for 'id' attribute and update current scope
opt::optional<std::string> updatedScope; opt::optional<std::string> updatedScope;
if ((itr = object.find("id")) != object.end() && if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) {
itr->second.maybeString()) {
const std::string id = itr->second.asString(); const std::string id = itr->second.asString();
rootSchema.setSubschemaId(&subschema, itr->second.asString()); rootSchema.setSubschemaId(&subschema, itr->second.asString());
if (!currentScope || internal::uri::isUriAbsolute(id)) { if (!currentScope || internal::uri::isUriAbsolute(id)) {
updatedScope = id; updatedScope = id;
} else { } else {
updatedScope = internal::uri::resolveRelativeUri( updatedScope = internal::uri::resolveRelativeUri(*currentScope, id);
*currentScope, id);
} }
} else { } else {
updatedScope = currentScope; updatedScope = currentScope;
@ -679,7 +656,7 @@ private:
} }
if ((itr = object.find("divisibleBy")) != object.end()) { if ((itr = object.find("divisibleBy")) != object.end()) {
if (version == kDraft3) { if (m_version == kDraft3) {
if (itr->second.maybeInteger()) { if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMultipleOfIntConstraint(itr->second), makeMultipleOfIntConstraint(itr->second),
@ -720,10 +697,8 @@ private:
additionalItemsItr = object.find("additionalItems"); additionalItemsItr = object.find("additionalItems");
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeLinearItemsConstraint(rootSchema, rootNode, makeLinearItemsConstraint(rootSchema, rootNode,
itemsItr != object.end() ? itemsItr != object.end() ? &itemsItr->second : nullptr,
&itemsItr->second : nullptr, additionalItemsItr != object.end() ? &additionalItemsItr->second : nullptr,
additionalItemsItr != object.end() ?
&additionalItemsItr->second : nullptr,
updatedScope, nodePath + "/items", updatedScope, nodePath + "/items",
nodePath + "/additionalItems", fetchDoc, nodePath + "/additionalItems", fetchDoc,
docCache, schemaCache), docCache, schemaCache),
@ -738,7 +713,7 @@ private:
const typename AdapterType::Object::const_iterator elseItr = object.find("else"); const typename AdapterType::Object::const_iterator elseItr = object.find("else");
if (object.end() != ifItr) { if (object.end() != ifItr) {
if (version == kDraft7) { if (m_version == kDraft7) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeConditionalConstraint(rootSchema, rootNode, makeConditionalConstraint(rootSchema, rootNode,
ifItr->second, ifItr->second,
@ -752,7 +727,7 @@ private:
} }
} }
if (version == kDraft7) { if (m_version == kDraft7) {
if ((itr = object.find("exclusiveMaximum")) != object.end()) { if ((itr = object.find("exclusiveMaximum")) != object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMaximumConstraintExclusive(itr->second), makeMaximumConstraintExclusive(itr->second),
@ -773,13 +748,11 @@ private:
&subschema); &subschema);
} else { } else {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMaximumConstraint(itr->second, makeMaximumConstraint(itr->second, &exclusiveMaximumItr->second),
&exclusiveMaximumItr->second),
&subschema); &subschema);
} }
} else if (object.find("exclusiveMaximum") != object.end()) { } else if (object.find("exclusiveMaximum") != object.end()) {
throw std::runtime_error( throw std::runtime_error("'exclusiveMaximum' constraint only valid if a 'maximum' "
"'exclusiveMaximum' constraint only valid if a 'maximum' "
"constraint is also present"); "constraint is also present");
} }
@ -798,11 +771,10 @@ private:
makeMaxPropertiesConstraint(itr->second), &subschema); makeMaxPropertiesConstraint(itr->second), &subschema);
} }
if (version == kDraft7) { if (m_version == kDraft7) {
if ((itr = object.find("exclusiveMinimum")) != object.end()) { if ((itr = object.find("exclusiveMinimum")) != object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMinimumConstraintExclusive(itr->second), makeMinimumConstraintExclusive(itr->second), &subschema);
&subschema);
} }
if ((itr = object.find("minimum")) != object.end()) { if ((itr = object.find("minimum")) != object.end()) {
@ -811,22 +783,18 @@ private:
&subschema); &subschema);
} }
} else if ((itr = object.find("minimum")) != object.end()) { } else if ((itr = object.find("minimum")) != object.end()) {
typename AdapterType::Object::const_iterator exclusiveMinimumItr = typename AdapterType::Object::const_iterator exclusiveMinimumItr = object.find("exclusiveMinimum");
object.find("exclusiveMinimum");
if (exclusiveMinimumItr == object.end()) { if (exclusiveMinimumItr == object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMinimumConstraint<AdapterType>(itr->second, nullptr), makeMinimumConstraint<AdapterType>(itr->second, nullptr),
&subschema); &subschema);
} else { } else {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMinimumConstraint<AdapterType>( makeMinimumConstraint<AdapterType>(itr->second, &exclusiveMinimumItr->second),
itr->second,
&exclusiveMinimumItr->second),
&subschema); &subschema);
} }
} else if (object.find("exclusiveMinimum") != object.end()) { } else if (object.find("exclusiveMinimum") != object.end()) {
throw std::runtime_error( throw std::runtime_error("'exclusiveMinimum' constraint only valid if a 'minimum' "
"'exclusiveMinimum' constraint only valid if a 'minimum' "
"constraint is also present"); "constraint is also present");
} }
@ -846,9 +814,8 @@ private:
} }
if ((itr = object.find("multipleOf")) != object.end()) { if ((itr = object.find("multipleOf")) != object.end()) {
if (version == kDraft3) { if (m_version == kDraft3) {
throw std::runtime_error( throw std::runtime_error("'multipleOf' constraint not available in draft 3");
"'multipleOf' constraint not available in draft 3");
} else if (itr->second.maybeInteger()) { } else if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeMultipleOfIntConstraint(itr->second), makeMultipleOfIntConstraint(itr->second),
@ -858,23 +825,20 @@ private:
makeMultipleOfDoubleConstraint(itr->second), makeMultipleOfDoubleConstraint(itr->second),
&subschema); &subschema);
} else { } else {
throw std::runtime_error("Expected an numeric value for " throw std::runtime_error("Expected an numeric value for 'divisibleBy' constraint.");
" 'divisibleBy' constraint.");
} }
} }
if ((itr = object.find("not")) != object.end()) { if ((itr = object.find("not")) != object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeNotConstraint(rootSchema, rootNode, itr->second, makeNotConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/not", fetchDoc,
updatedScope, nodePath + "/not", fetchDoc, docCache, docCache, schemaCache),
schemaCache),
&subschema); &subschema);
} }
if ((itr = object.find("oneOf")) != object.end()) { if ((itr = object.find("oneOf")) != object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeOneOfConstraint(rootSchema, rootNode, itr->second, makeOneOfConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/oneOf", fetchDoc,
updatedScope, nodePath + "/oneOf", fetchDoc,
docCache, schemaCache), docCache, schemaCache),
&subschema); &subschema);
} }
@ -896,12 +860,9 @@ private:
object.end() != additionalPropertiesItr) { object.end() != additionalPropertiesItr) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makePropertiesConstraint(rootSchema, rootNode, makePropertiesConstraint(rootSchema, rootNode,
propertiesItr != object.end() ? propertiesItr != object.end() ? &propertiesItr->second : nullptr,
&propertiesItr->second : nullptr, patternPropertiesItr != object.end() ? &patternPropertiesItr->second : nullptr,
patternPropertiesItr != object.end() ? additionalPropertiesItr != object.end() ? &additionalPropertiesItr->second : nullptr,
&patternPropertiesItr->second : nullptr,
additionalPropertiesItr != object.end() ?
&additionalPropertiesItr->second : nullptr,
updatedScope, nodePath + "/properties", updatedScope, nodePath + "/properties",
nodePath + "/patternProperties", nodePath + "/patternProperties",
nodePath + "/additionalProperties", nodePath + "/additionalProperties",
@ -911,7 +872,7 @@ private:
} }
if ((itr = object.find("propertyNames")) != object.end()) { if ((itr = object.find("propertyNames")) != object.end()) {
if (version == kDraft7) { if (m_version == kDraft7) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope, makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope,
nodePath, fetchDoc, docCache, schemaCache), nodePath, fetchDoc, docCache, schemaCache),
@ -922,60 +883,49 @@ private:
} }
if ((itr = object.find("required")) != object.end()) { if ((itr = object.find("required")) != object.end()) {
if (version == kDraft3) { if (m_version == kDraft3) {
if (parentSubschema && ownName) { if (parentSubschema && ownName) {
opt::optional<constraints::RequiredConstraint> opt::optional<constraints::RequiredConstraint> constraint =
constraint = makeRequiredConstraintForSelf( makeRequiredConstraintForSelf(itr->second, *ownName);
itr->second, *ownName);
if (constraint) { if (constraint) {
rootSchema.addConstraintToSubschema(*constraint, rootSchema.addConstraintToSubschema(*constraint, parentSubschema);
parentSubschema);
} }
} else { } else {
throw std::runtime_error( throw std::runtime_error("'required' constraint not valid here");
"'required' constraint not valid here");
} }
} else { } else {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(makeRequiredConstraint(itr->second), &subschema);
makeRequiredConstraint(itr->second), &subschema);
} }
} }
if ((itr = object.find("title")) != object.end()) { if ((itr = object.find("title")) != object.end()) {
if (itr->second.maybeString()) { if (itr->second.maybeString()) {
rootSchema.setSubschemaTitle(&subschema, rootSchema.setSubschemaTitle(&subschema, itr->second.asString());
itr->second.asString());
} else { } else {
throw std::runtime_error( throw std::runtime_error("'title' attribute should have a string value");
"'title' attribute should have a string value");
} }
} }
if ((itr = object.find("type")) != object.end()) { if ((itr = object.find("type")) != object.end()) {
rootSchema.addConstraintToSubschema( rootSchema.addConstraintToSubschema(
makeTypeConstraint(rootSchema, rootNode, itr->second, makeTypeConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/type", fetchDoc,
updatedScope, nodePath + "/type", fetchDoc,
docCache, schemaCache), docCache, schemaCache),
&subschema); &subschema);
} }
if ((itr = object.find("uniqueItems")) != object.end()) { if ((itr = object.find("uniqueItems")) != object.end()) {
opt::optional<constraints::UniqueItemsConstraint> constraint = opt::optional<constraints::UniqueItemsConstraint> constraint = makeUniqueItemsConstraint(itr->second);
makeUniqueItemsConstraint(itr->second);
if (constraint) { if (constraint) {
rootSchema.addConstraintToSubschema(*constraint, &subschema); rootSchema.addConstraintToSubschema(*constraint, &subschema);
} }
} }
for (ConstraintBuilders::const_iterator for (const auto & constraintBuilder : constraintBuilders) {
builderItr = constraintBuilders.begin(); if ((itr = object.find(constraintBuilder.first)) != object.end()) {
builderItr != constraintBuilders.end(); ++builderItr) {
if ((itr = object.find(builderItr->first)) != object.end()) {
constraints::Constraint *constraint = nullptr; constraints::Constraint *constraint = nullptr;
try { try {
constraint = builderItr->second->make(itr->second); constraint = constraintBuilder.second->make(itr->second);
rootSchema.addConstraintToSubschema(*constraint, rootSchema.addConstraintToSubschema(*constraint, &subschema);
&subschema);
delete constraint; delete constraint;
} catch (...) { } catch (...) {
delete constraint; delete constraint;
@ -1026,16 +976,14 @@ private:
{ {
std::string jsonRef; std::string jsonRef;
if (!extractJsonReference(node, jsonRef)) { if (!extractJsonReference(node, jsonRef)) {
populateSchema(rootSchema, rootNode, node, subschema, currentScope, populateSchema(rootSchema, rootNode, node, subschema, currentScope, nodePath, fetchDoc, parentSchema,
nodePath, fetchDoc, parentSchema, ownName, docCache, ownName, docCache, schemaCache);
schemaCache);
return; return;
} }
// Returns a document URI if the reference points somewhere // Returns a document URI if the reference points somewhere
// other than the current document // other than the current document
const opt::optional<std::string> documentUri = const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
internal::json_reference::getJsonReferenceUri(jsonRef);
// Extract JSON Pointer from JSON Reference // Extract JSON Pointer from JSON Reference
const std::string actualJsonPointer = sanitiseJsonPointer( const std::string actualJsonPointer = sanitiseJsonPointer(
@ -1044,48 +992,37 @@ private:
if (documentUri && internal::uri::isUriAbsolute(*documentUri)) { if (documentUri && internal::uri::isUriAbsolute(*documentUri)) {
// Resolve reference against remote document // Resolve reference against remote document
if (!fetchDoc) { if (!fetchDoc) {
throw std::runtime_error( throw std::runtime_error("Fetching of remote JSON References not enabled.");
"Fetching of remote JSON References not enabled.");
} }
const typename DocumentCache<AdapterType>::DocumentType *newDoc = const typename DocumentCache<AdapterType>::DocumentType *newDoc = fetchDoc(*documentUri);
fetchDoc(*documentUri);
// Can't proceed without the remote document // Can't proceed without the remote document
if (!newDoc) { if (!newDoc) {
throw std::runtime_error( throw std::runtime_error("Failed to fetch referenced schema document: " + *documentUri);
"Failed to fetch referenced schema document: " +
*documentUri);
} }
// Add to document cache // Add to document cache
typedef typename DocumentCache<AdapterType>::Type::value_type typedef typename DocumentCache<AdapterType>::Type::value_type DocCacheValueType;
DocCacheValueType;
docCache.insert(DocCacheValueType(*documentUri, newDoc)); docCache.insert(DocCacheValueType(*documentUri, newDoc));
const AdapterType newRootNode(*newDoc); const AdapterType newRootNode(*newDoc);
const AdapterType &referencedAdapter = const AdapterType &referencedAdapter =
internal::json_pointer::resolveJsonPointer( internal::json_pointer::resolveJsonPointer(newRootNode, actualJsonPointer);
newRootNode, actualJsonPointer);
// TODO: Need to detect degenerate circular references // TODO: Need to detect degenerate circular references
resolveThenPopulateSchema(rootSchema, newRootNode, resolveThenPopulateSchema(rootSchema, newRootNode, referencedAdapter, subschema, {}, actualJsonPointer,
referencedAdapter, subschema, opt::optional<std::string>(), fetchDoc, parentSchema, ownName, docCache, schemaCache);
actualJsonPointer, fetchDoc, parentSchema, ownName,
docCache, schemaCache);
} else { } else {
const AdapterType &referencedAdapter = const AdapterType &referencedAdapter =
internal::json_pointer::resolveJsonPointer( internal::json_pointer::resolveJsonPointer(rootNode, actualJsonPointer);
rootNode, actualJsonPointer);
// TODO: Need to detect degenerate circular references // TODO: Need to detect degenerate circular references
resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter, resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter, subschema, {}, actualJsonPointer,
subschema, opt::optional<std::string>(), fetchDoc, parentSchema, ownName, docCache, schemaCache);
actualJsonPointer, fetchDoc,
parentSchema, ownName, docCache, schemaCache);
} }
} }
@ -1119,15 +1056,14 @@ private:
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
if (!node.maybeArray()) { if (!node.maybeArray()) {
throw std::runtime_error( throw std::runtime_error("Expected array value for 'allOf' constraint.");
"Expected array value for 'allOf' constraint.");
} }
constraints::AllOfConstraint constraint; constraints::AllOfConstraint constraint;
int index = 0; int index = 0;
for (const AdapterType schemaNode : node.asArray()) { for (const AdapterType schemaNode : node.asArray()) {
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) { if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
const std::string childPath = nodePath + "/" + std::to_string(index); const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope, rootSchema, rootNode, schemaNode, currentScope,
@ -1135,8 +1071,7 @@ private:
constraint.addSubschema(subschema); constraint.addSubschema(subschema);
index++; index++;
} else { } else {
throw std::runtime_error( throw std::runtime_error("Expected element to be a valid schema in 'allOf' constraint.");
"Expected element to be a valid schema in 'allOf' constraint.");
} }
} }
@ -1173,15 +1108,14 @@ private:
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
if (!node.maybeArray()) { if (!node.maybeArray()) {
throw std::runtime_error( throw std::runtime_error("Expected array value for 'anyOf' constraint.");
"Expected array value for 'anyOf' constraint.");
} }
constraints::AnyOfConstraint constraint; constraints::AnyOfConstraint constraint;
int index = 0; int index = 0;
for (const AdapterType schemaNode : node.asArray()) { for (const AdapterType schemaNode : node.asArray()) {
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) { if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
const std::string childPath = nodePath + "/" + std::to_string(index); const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope, rootSchema, rootNode, schemaNode, currentScope,
@ -1189,8 +1123,7 @@ private:
constraint.addSubschema(subschema); constraint.addSubschema(subschema);
index++; index++;
} else { } else {
throw std::runtime_error( throw std::runtime_error("Expected array element to be a valid schema in 'anyOf' constraint.");
"Expected array element to be a valid schema in 'anyOf' constraint.");
} }
} }
@ -1316,7 +1249,7 @@ private:
{ {
constraints::ContainsConstraint constraint; constraints::ContainsConstraint constraint;
if (contains.isObject() || (version == kDraft7 && contains.maybeBool())) { if (contains.isObject() || (m_version == kDraft7 && contains.maybeBool())) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, contains, currentScope, containsPath, rootSchema, rootNode, contains, currentScope, containsPath,
fetchDoc, nullptr, nullptr, docCache, schemaCache); fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -1329,8 +1262,7 @@ private:
} else { } else {
// All other formats will result in an exception being thrown. // All other formats will result in an exception being thrown.
throw std::runtime_error( throw std::runtime_error("Expected valid schema for 'contains' constraint.");
"Expected valid schema for 'contains' constraint.");
} }
return constraint; return constraint;
@ -1420,7 +1352,7 @@ private:
// exercised the flexibility by loosely-typed Adapter types. If the // exercised the flexibility by loosely-typed Adapter types. If the
// value of the dependency mapping is an object, then we'll try to // value of the dependency mapping is an object, then we'll try to
// process it as a dependent schema. // process it as a dependent schema.
} else if (member.second.isObject() || (version == kDraft7 && member.second.maybeBool())) { } else if (member.second.isObject() || (m_version == kDraft7 && member.second.maybeBool())) {
// Parse dependent subschema // Parse dependent subschema
const Subschema *childSubschema = const Subschema *childSubschema =
makeOrReuseSchema<AdapterType>(rootSchema, rootNode, makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
@ -1432,7 +1364,7 @@ private:
// If we're supposed to be parsing a Draft3 schema, then the value // If we're supposed to be parsing a Draft3 schema, then the value
// of the dependency mapping can also be a string containing the // of the dependency mapping can also be a string containing the
// name of a single dependency. // name of a single dependency.
} else if (version == kDraft3 && member.second.isString()) { } else if (m_version == kDraft3 && member.second.isString()) {
dependenciesConstraint.addPropertyDependency(member.first, dependenciesConstraint.addPropertyDependency(member.first,
member.second.getString()); member.second.getString());
@ -1522,8 +1454,7 @@ private:
// and is set to true, then additional array items do not need // and is set to true, then additional array items do not need
// to satisfy any constraints. // to satisfy any constraints.
if (additionalItems->asBool()) { if (additionalItems->asBool()) {
constraint.setAdditionalItemsSubschema( constraint.setAdditionalItemsSubschema(rootSchema.emptySubschema());
rootSchema.emptySubschema());
} }
} else if (additionalItems->maybeObject()) { } else if (additionalItems->maybeObject()) {
// If the value of the additionalItems property is an object, // If the value of the additionalItems property is an object,
@ -1537,8 +1468,7 @@ private:
} else { } else {
// Any other format for the additionalItems property will result // Any other format for the additionalItems property will result
// in an exception being thrown. // in an exception being thrown.
throw std::runtime_error( throw std::runtime_error("Expected bool or object value for 'additionalItems'");
"Expected bool or object value for 'additionalItems'");
} }
} else { } else {
// The default value for the additionalItems property is an empty // The default value for the additionalItems property is an empty
@ -1567,9 +1497,7 @@ private:
index++; index++;
} }
} else { } else {
throw std::runtime_error( throw std::runtime_error("Expected array value for non-singular 'items' constraint.");
"Expected array value for non-singular 'items' "
"constraint.");
} }
} }
@ -1622,7 +1550,7 @@ private:
// array is provided, or a single Schema object, in an object value is // array is provided, or a single Schema object, in an object value is
// provided. If the items constraint is not provided, then array items // provided. If the items constraint is not provided, then array items
// will be validated against the additionalItems schema. // will be validated against the additionalItems schema.
if (items.isObject() || (version == kDraft7 && items.maybeBool())) { if (items.isObject() || (m_version == kDraft7 && items.maybeBool())) {
// If the items constraint contains an object value, then it // If the items constraint contains an object value, then it
// should contain a Schema that will be used to validate all // should contain a Schema that will be used to validate all
// items in a target array. Any schema defined by the // items in a target array. Any schema defined by the
@ -1639,8 +1567,7 @@ private:
} else { } else {
// All other formats will result in an exception being thrown. // All other formats will result in an exception being thrown.
throw std::runtime_error( throw std::runtime_error("Expected valid schema for singular 'items' constraint.");
"Expected valid schema for singular 'items' constraint.");
} }
return constraint; return constraint;
@ -1669,8 +1596,7 @@ private:
const AdapterType *exclusiveMaximum) const AdapterType *exclusiveMaximum)
{ {
if (!node.maybeDouble()) { if (!node.maybeDouble()) {
throw std::runtime_error( throw std::runtime_error("Expected numeric value for maximum constraint.");
"Expected numeric value for maximum constraint.");
} }
constraints::MaximumConstraint constraint; constraints::MaximumConstraint constraint;
@ -1678,9 +1604,7 @@ private:
if (exclusiveMaximum) { if (exclusiveMaximum) {
if (!exclusiveMaximum->maybeBool()) { if (!exclusiveMaximum->maybeBool()) {
throw std::runtime_error( throw std::runtime_error("Expected boolean value for exclusiveMaximum constraint.");
"Expected boolean value for exclusiveMaximum "
"constraint.");
} }
constraint.setExclusiveMaximum(exclusiveMaximum->asBool()); constraint.setExclusiveMaximum(exclusiveMaximum->asBool());
@ -1733,9 +1657,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected non-negative integer value for 'maxItems' constraint.");
"Expected non-negative integer value for 'maxItems' "
"constraint.");
} }
/** /**
@ -1759,9 +1681,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected a non-negative integer value for 'maxLength' constraint.");
"Expected a non-negative integer value for 'maxLength' "
"constraint.");
} }
/** /**
@ -1787,9 +1707,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected a non-negative integer for 'maxProperties' constraint.");
"Expected a non-negative integer for 'maxProperties' "
"constraint.");
} }
/** /**
@ -1810,8 +1728,7 @@ private:
const AdapterType *exclusiveMinimum) const AdapterType *exclusiveMinimum)
{ {
if (!node.maybeDouble()) { if (!node.maybeDouble()) {
throw std::runtime_error( throw std::runtime_error("Expected numeric value for minimum constraint.");
"Expected numeric value for minimum constraint.");
} }
constraints::MinimumConstraint constraint; constraints::MinimumConstraint constraint;
@ -1819,9 +1736,7 @@ private:
if (exclusiveMinimum) { if (exclusiveMinimum) {
if (!exclusiveMinimum->maybeBool()) { if (!exclusiveMinimum->maybeBool()) {
throw std::runtime_error( throw std::runtime_error("Expected boolean value for 'exclusiveMinimum' constraint.");
"Expected boolean value for 'exclusiveMinimum' "
"constraint.");
} }
constraint.setExclusiveMinimum(exclusiveMinimum->asBool()); constraint.setExclusiveMinimum(exclusiveMinimum->asBool());
@ -1862,8 +1777,7 @@ private:
* @return pointer to a new MinItemsConstraint that belongs to the caller * @return pointer to a new MinItemsConstraint that belongs to the caller
*/ */
template<typename AdapterType> template<typename AdapterType>
constraints::MinItemsConstraint makeMinItemsConstraint( constraints::MinItemsConstraint makeMinItemsConstraint(const AdapterType &node)
const AdapterType &node)
{ {
if (node.maybeInteger()) { if (node.maybeInteger()) {
const int64_t value = node.asInteger(); const int64_t value = node.asInteger();
@ -1874,9 +1788,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected a non-negative integer value for 'minItems' constraint.");
"Expected a non-negative integer value for 'minItems' "
"constraint.");
} }
/** /**
@ -1888,8 +1800,7 @@ private:
* @return pointer to a new MinLengthConstraint that belongs to the caller * @return pointer to a new MinLengthConstraint that belongs to the caller
*/ */
template<typename AdapterType> template<typename AdapterType>
constraints::MinLengthConstraint makeMinLengthConstraint( constraints::MinLengthConstraint makeMinLengthConstraint(const AdapterType &node)
const AdapterType &node)
{ {
if (node.maybeInteger()) { if (node.maybeInteger()) {
const int64_t value = node.asInteger(); const int64_t value = node.asInteger();
@ -1900,9 +1811,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected a non-negative integer value for 'minLength' constraint.");
"Expected a non-negative integer value for 'minLength' "
"constraint.");
} }
@ -1917,8 +1826,7 @@ private:
* caller * caller
*/ */
template<typename AdapterType> template<typename AdapterType>
constraints::MinPropertiesConstraint makeMinPropertiesConstraint( constraints::MinPropertiesConstraint makeMinPropertiesConstraint(const AdapterType &node)
const AdapterType &node)
{ {
if (node.maybeInteger()) { if (node.maybeInteger()) {
int64_t value = node.asInteger(); int64_t value = node.asInteger();
@ -1929,9 +1837,7 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected a non-negative integer for 'minProperties' constraint.");
"Expected a non-negative integer for 'minProperties' "
"constraint.");
} }
/** /**
@ -1943,8 +1849,7 @@ private:
* @return a MultipleOfConstraint * @return a MultipleOfConstraint
*/ */
template<typename AdapterType> template<typename AdapterType>
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint( constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(const AdapterType &node)
const AdapterType &node)
{ {
constraints::MultipleOfDoubleConstraint constraint; constraints::MultipleOfDoubleConstraint constraint;
constraint.setDivisor(node.asDouble()); constraint.setDivisor(node.asDouble());
@ -1960,8 +1865,7 @@ private:
* @return a MultipleOfIntConstraint * @return a MultipleOfIntConstraint
*/ */
template<typename AdapterType> template<typename AdapterType>
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint( constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(const AdapterType &node)
const AdapterType &node)
{ {
constraints::MultipleOfIntConstraint constraint; constraints::MultipleOfIntConstraint constraint;
constraint.setDivisor(node.asInteger()); constraint.setDivisor(node.asInteger());
@ -1996,7 +1900,7 @@ private:
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
if (node.maybeObject() || (version == kDraft7 && node.maybeBool())) { if (node.maybeObject() || (m_version == kDraft7 && node.maybeBool())) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, node, currentScope, nodePath, rootSchema, rootNode, node, currentScope, nodePath,
fetchDoc, nullptr, nullptr, docCache, schemaCache); fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -2040,8 +1944,7 @@ private:
int index = 0; int index = 0;
for (const AdapterType schemaNode : node.getArray()) { for (const AdapterType schemaNode : node.getArray()) {
const std::string childPath = nodePath + "/" + const std::string childPath = nodePath + "/" + std::to_string(index);
std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope, childPath, rootSchema, rootNode, schemaNode, currentScope, childPath,
fetchDoc, nullptr, nullptr, docCache, schemaCache); fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -2166,8 +2069,7 @@ private:
// If it has a boolean value that is 'true', then an empty // If it has a boolean value that is 'true', then an empty
// schema should be used. // schema should be used.
if (additionalProperties->asBool()) { if (additionalProperties->asBool()) {
constraint.setAdditionalPropertiesSubschema( constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
rootSchema.emptySubschema());
} }
} else if (additionalProperties->isObject()) { } else if (additionalProperties->isObject()) {
// If additionalProperties is an object, it should be used as // If additionalProperties is an object, it should be used as
@ -2179,14 +2081,12 @@ private:
constraint.setAdditionalPropertiesSubschema(subschema); constraint.setAdditionalPropertiesSubschema(subschema);
} else { } else {
// All other types are invalid // All other types are invalid
throw std::runtime_error( throw std::runtime_error("Invalid type for 'additionalProperties' constraint.");
"Invalid type for 'additionalProperties' constraint.");
} }
} else { } else {
// If an additionalProperties constraint is not provided, then the // If an additionalProperties constraint is not provided, then the
// default value is an empty schema. // default value is an empty schema.
constraint.setAdditionalPropertiesSubschema( constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
rootSchema.emptySubschema());
} }
return constraint; return constraint;
@ -2203,10 +2103,8 @@ private:
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, currentNode, currentScope,
currentNode, currentScope, nodePath, fetchDoc, nullptr, nullptr, docCache, nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
schemaCache);
constraints::PropertyNamesConstraint constraint; constraints::PropertyNamesConstraint constraint;
constraint.setSubschema(subschema); constraint.setSubschema(subschema);
return constraint; return constraint;
@ -2261,8 +2159,7 @@ private:
for (const AdapterType v : node.getArray()) { for (const AdapterType v : node.getArray()) {
if (!v.maybeString()) { if (!v.maybeString()) {
throw std::runtime_error("Expected required property name to " throw std::runtime_error("Expected required property name to be a string value");
"be a string value");
} }
constraint.addRequiredProperty(v.getString()); constraint.addRequiredProperty(v.getString());
@ -2305,9 +2202,8 @@ private:
if (node.maybeString()) { if (node.maybeString()) {
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString()); const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString());
if (type == TypeConstraint::kAny && version == kDraft4) { if (type == TypeConstraint::kAny && m_version == kDraft4) {
throw std::runtime_error( throw std::runtime_error("'any' type is not supported in version 4 schemas.");
"'any' type is not supported in version 4 schemas.");
} }
constraint.addNamedType(type); constraint.addNamedType(type);
@ -2316,22 +2212,17 @@ private:
int index = 0; int index = 0;
for (const AdapterType v : node.getArray()) { for (const AdapterType v : node.getArray()) {
if (v.maybeString()) { if (v.maybeString()) {
const TypeConstraint::JsonType type = const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(v.getString());
TypeConstraint::jsonTypeFromString(v.getString()); if (type == TypeConstraint::kAny && m_version == kDraft4) {
throw std::runtime_error("'any' type is not supported in version 4 schemas.");
if (type == TypeConstraint::kAny && version == kDraft4) {
throw std::runtime_error(
"'any' type is not supported in version 4 "
"schemas.");
} }
constraint.addNamedType(type); constraint.addNamedType(type);
} else if (v.maybeObject() && version == kDraft3) { } else if (v.maybeObject() && m_version == kDraft3) {
const std::string childPath = nodePath + "/" + std::to_string(index); const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, v, currentScope,
rootSchema, rootNode, v, currentScope, childPath, childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
fetchDoc, nullptr, nullptr, docCache, schemaCache);
constraint.addSchemaType(subschema); constraint.addSchemaType(subschema);
} else { } else {
@ -2341,10 +2232,9 @@ private:
index++; index++;
} }
} else if (node.maybeObject() && version == kDraft3) { } else if (node.maybeObject() && m_version == kDraft3) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>( const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, node, currentScope,
rootSchema, rootNode, node, currentScope, nodePath, nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
fetchDoc, nullptr, nullptr, docCache, schemaCache);
constraint.addSchemaType(subschema); constraint.addSchemaType(subschema);
} else { } else {
@ -2376,10 +2266,13 @@ private:
} }
} }
throw std::runtime_error( throw std::runtime_error("Expected boolean value for 'uniqueItems' constraint.");
"Expected boolean value for 'uniqueItems' constraint.");
} }
private:
/// Version of JSON Schema that should be expected when parsing
const Version m_version;
}; };
} // namespace valijson } // namespace valijson

View File

@ -2,6 +2,7 @@
#include <deque> #include <deque>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace valijson { namespace valijson {
@ -25,21 +26,6 @@ public:
*/ */
struct Error struct Error
{ {
/**
* @brief Construct an Error object with no context or description.
*/
Error() { }
/**
* @brief Construct an Error object using a context and description.
*
* @param context Context string to use
* @param description Description string to use
*/
Error(const std::vector<std::string> &context, const std::string &description)
: context(context),
description(description) { }
/// Path to the node that failed validation. /// Path to the node that failed validation.
std::vector<std::string> context; std::vector<std::string> context;
@ -52,7 +38,7 @@ public:
*/ */
std::deque<Error>::const_iterator begin() const std::deque<Error>::const_iterator begin() const
{ {
return errors.begin(); return m_errors.begin();
} }
/** /**
@ -60,7 +46,7 @@ public:
*/ */
std::deque<Error>::const_iterator end() const std::deque<Error>::const_iterator end() const
{ {
return errors.end(); return m_errors.end();
} }
/** /**
@ -68,7 +54,7 @@ public:
*/ */
size_t numErrors() const size_t numErrors() const
{ {
return errors.size(); return m_errors.size();
} }
/** /**
@ -78,7 +64,7 @@ public:
*/ */
void pushError(const Error &error) void pushError(const Error &error)
{ {
errors.push_back(error); m_errors.push_back(error);
} }
/** /**
@ -90,7 +76,7 @@ public:
void void
pushError(const std::vector<std::string> &context, const std::string &description) pushError(const std::vector<std::string> &context, const std::string &description)
{ {
errors.push_back(Error(context, description)); m_errors.push_back({context, description});
} }
/** /**
@ -103,20 +89,19 @@ public:
bool bool
popError(Error &error) popError(Error &error)
{ {
if (errors.empty()) { if (m_errors.empty()) {
return false; return false;
} }
error = errors.front(); error = m_errors.front();
errors.pop_front(); m_errors.pop_front();
return true; return true;
} }
private: private:
/// FIFO queue of validation errors that have been reported /// FIFO queue of validation errors that have been reported
std::deque<Error> errors; std::deque<Error> m_errors;
}; };
} // namespace valijson } // namespace valijson