diff --git a/DEPENDENCIES b/DEPENDENCIES index 1abdd596..084efc87 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 8fb7ba6f57bfa52ecdae83ada221e0aecc8e4e42 +core https://github.com/sourcemeta/core 376f397db98e72be48d8401e2fbf2ee96f033966 bootstrap https://github.com/twbs/bootstrap 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e diff --git a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h index 17bebdd4..67f7ea1c 100644 --- a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h +++ b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h @@ -114,6 +114,28 @@ auto get(const JSON &document, const WeakPointer &pointer) -> const JSON &; // constant reference. auto get(JSON &&document, const WeakPointer &pointer) -> const JSON & = delete; +/// @ingroup jsonpointer +/// Get a value from a JSON document using a JSON WeakPointer (non-`const` +/// overload). For example: +/// +/// ```cpp +/// #include +/// #include +/// #include +/// #include +/// +/// std::istringstream stream{"[ { \"foo\": 1 }, { \"bar\": 2 } ]"}; +/// auto document{sourcemeta::core::parse_json(stream)}; +/// const sourcemeta::core::Pointer pointer{1, "bar"}; +/// sourcemeta::core::JSON &value{ +/// sourcemeta::core::get(document, +/// sourcemeta::core::to_weak_pointer(pointer))}; +/// value = sourcemeta::core::JSON{3}; +/// assert(document.at(1).at("bar").to_integer() == 3); +/// ``` +SOURCEMETA_CORE_JSONPOINTER_EXPORT +auto get(JSON &document, const WeakPointer &pointer) -> JSON &; + /// @ingroup jsonpointer /// Get a value from a JSON document using a Pointer, returning an optional that /// is not set if the path does not exist in the document. For example: diff --git a/vendor/core/src/core/jsonpointer/jsonpointer.cc b/vendor/core/src/core/jsonpointer/jsonpointer.cc index 1656254d..794e8b46 100644 --- a/vendor/core/src/core/jsonpointer/jsonpointer.cc +++ b/vendor/core/src/core/jsonpointer/jsonpointer.cc @@ -162,6 +162,14 @@ auto get(JSON &document, const Pointer &pointer) -> JSON & { return traverse_all(document, pointer); } +auto get(JSON &document, const WeakPointer &pointer) -> JSON & { + if (pointer.empty()) { + return document; + } + + return traverse_all(document, pointer); +} + auto try_get(const JSON &document, const Pointer &pointer) -> const JSON * { return pointer.empty() ? &document : try_traverse(document, pointer); } diff --git a/vendor/core/src/core/jsonschema/bundle.cc b/vendor/core/src/core/jsonschema/bundle.cc index 22b154bc..f9bfa842 100644 --- a/vendor/core/src/core/jsonschema/bundle.cc +++ b/vendor/core/src/core/jsonschema/bundle.cc @@ -10,8 +10,9 @@ namespace { -auto is_official_metaschema_reference(const sourcemeta::core::Pointer &pointer, - const std::string &destination) -> bool { +auto is_official_metaschema_reference( + const sourcemeta::core::WeakPointer &pointer, + const std::string &destination) -> bool { assert(!pointer.empty()); assert(pointer.back().is_property()); return pointer.back().to_property() == "$schema" && @@ -47,7 +48,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema, if (reference.base.empty()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, "Could not resolve schema reference"); + reference.destination, sourcemeta::core::to_pointer(pointer), + "Could not resolve schema reference"); } // To not infinitely loop on circular references @@ -59,7 +61,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema, // find the base, then we are facing an unresolved fragment if (frame.traverse(reference.base).has_value()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, "Could not resolve schema reference"); + reference.destination, sourcemeta::core::to_pointer(pointer), + "Could not resolve schema reference"); } assert(!reference.base.empty()); @@ -72,14 +75,16 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema, if (!sourcemeta::core::is_schema(remote.value())) { throw sourcemeta::core::SchemaReferenceError( - identifier, pointer, "The JSON document is not a valid JSON Schema"); + identifier, sourcemeta::core::to_pointer(pointer), + "The JSON document is not a valid JSON Schema"); } const auto remote_base_dialect{sourcemeta::core::base_dialect( remote.value(), resolver, default_dialect)}; if (!remote_base_dialect.has_value()) { throw sourcemeta::core::SchemaReferenceError( - identifier, pointer, "The JSON document is not a valid JSON Schema"); + identifier, sourcemeta::core::to_pointer(pointer), + "The JSON document is not a valid JSON Schema"); } callback(origin, pointer, identifier, remote.value()); @@ -161,12 +166,14 @@ auto bundle_schema(sourcemeta::core::JSON &root, // find base, then we are facing an unresolved fragment if (!reference.base.empty() && frame.traverse(reference.base).has_value()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, "Could not resolve schema reference"); + reference.destination, sourcemeta::core::to_pointer(pointer), + "Could not resolve schema reference"); } if (reference.base.empty()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, "Could not resolve schema reference"); + reference.destination, sourcemeta::core::to_pointer(pointer), + "Could not resolve schema reference"); } assert(!reference.base.empty()); @@ -182,7 +189,7 @@ auto bundle_schema(sourcemeta::core::JSON &root, if (!remote.has_value()) { if (frame.traverse(identifier).has_value()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, + reference.destination, sourcemeta::core::to_pointer(pointer), "Could not resolve schema reference"); } @@ -192,14 +199,16 @@ auto bundle_schema(sourcemeta::core::JSON &root, if (!sourcemeta::core::is_schema(remote.value())) { throw sourcemeta::core::SchemaReferenceError( - identifier, pointer, "The JSON document is not a valid JSON Schema"); + identifier, sourcemeta::core::to_pointer(pointer), + "The JSON document is not a valid JSON Schema"); } const auto remote_base_dialect{sourcemeta::core::base_dialect( remote.value(), resolver, default_dialect)}; if (!remote_base_dialect.has_value()) { throw sourcemeta::core::SchemaReferenceError( - identifier, pointer, "The JSON document is not a valid JSON Schema"); + identifier, sourcemeta::core::to_pointer(pointer), + "The JSON document is not a valid JSON Schema"); } // If the reference has a fragment, verify it exists in the remote @@ -213,7 +222,7 @@ auto bundle_schema(sourcemeta::core::JSON &root, identifier); if (!remote_frame.traverse(reference.destination).has_value()) { throw sourcemeta::core::SchemaReferenceError( - reference.destination, pointer, + reference.destination, sourcemeta::core::to_pointer(pointer), "Could not resolve schema reference"); } } diff --git a/vendor/core/src/core/jsonschema/format.cc b/vendor/core/src/core/jsonschema/format.cc index cc785c0d..266abead 100644 --- a/vendor/core/src/core/jsonschema/format.cc +++ b/vendor/core/src/core/jsonschema/format.cc @@ -4,6 +4,7 @@ #include // std::numeric_limits #include // std::string_view #include // std::unordered_map +#include // std::vector namespace { @@ -139,19 +140,28 @@ auto format(JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver, std::string_view default_dialect) -> void { assert(is_schema(schema)); - SchemaFrame frame{SchemaFrame::Mode::Locations}; - frame.analyse(schema, walker, resolver, default_dialect); - - for (const auto &entry : frame.locations()) { - if (entry.second.type != SchemaFrame::LocationType::Resource && - entry.second.type != SchemaFrame::LocationType::Subschema) { - continue; + std::vector objects_to_reorder; + + { + SchemaFrame frame{SchemaFrame::Mode::Locations}; + frame.analyse(schema, walker, resolver, default_dialect); + + for (const auto &entry : frame.locations()) { + if (entry.second.type != SchemaFrame::LocationType::Resource && + entry.second.type != SchemaFrame::LocationType::Subschema) { + continue; + } + + auto &subschema{get(schema, entry.second.pointer)}; + if (subschema.is_object()) { + objects_to_reorder.push_back(&subschema); + } } + } - auto &value{get(schema, entry.second.pointer)}; - if (value.is_object()) { - value.reorder(keyword_compare); - } + // Now apply the reordering after the frame is destroyed + for (auto *object : objects_to_reorder) { + object->reorder(keyword_compare); } } diff --git a/vendor/core/src/core/jsonschema/frame.cc b/vendor/core/src/core/jsonschema/frame.cc index bde79f3f..96cedbd7 100644 --- a/vendor/core/src/core/jsonschema/frame.cc +++ b/vendor/core/src/core/jsonschema/frame.cc @@ -13,6 +13,12 @@ enum class AnchorType : std::uint8_t { Static, Dynamic, All }; +// Static keyword strings for reference pointers +static const std::string KEYWORD_SCHEMA{"$schema"}; +static const std::string KEYWORD_REF{"$ref"}; +static const std::string KEYWORD_RECURSIVE_REF{"$recursiveRef"}; +static const std::string KEYWORD_DYNAMIC_REF{"$dynamicRef"}; + namespace { auto find_anchors(const sourcemeta::core::JSON &schema, @@ -287,7 +293,7 @@ auto store(sourcemeta::core::SchemaFrame::Locations &frame, {.parent = parent, .type = entry_type, .base = base, - .pointer = to_pointer(pointer_from_root), + .pointer = pointer_from_root, .relative_pointer = relative_pointer_offset, .dialect = dialect, .base_dialect = base_dialect}}); @@ -357,9 +363,9 @@ auto SchemaFrame::to_json( entry.assign_assume_new("pointer", sourcemeta::core::to_json(location.second.pointer)); if (tracker.has_value()) { - entry.assign_assume_new( - "position", sourcemeta::core::to_json( - tracker.value().get(location.second.pointer))); + entry.assign_assume_new("position", + sourcemeta::core::to_json(tracker.value().get( + to_pointer(location.second.pointer)))); } else { entry.assign_assume_new("position", sourcemeta::core::to_json(nullptr)); } @@ -400,8 +406,8 @@ auto SchemaFrame::to_json( if (tracker.has_value()) { entry.assign_assume_new("position", - sourcemeta::core::to_json( - tracker.value().get(reference.first.second))); + sourcemeta::core::to_json(tracker.value().get( + to_pointer(reference.first.second)))); } else { entry.assign_assume_new("position", sourcemeta::core::to_json(nullptr)); } @@ -527,7 +533,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, for (const auto &entry_index : current_subschema_entries) { const auto &entry{subschema_entries[entry_index]}; const auto &common_pointer_weak{entry.common.pointer}; - const auto common_pointer{to_pointer(common_pointer_weak)}; const auto &common_parent{entry.common.parent}; if (entry.id.has_value()) { assert(entry.common.base_dialect.has_value()); @@ -573,7 +578,7 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, const auto maybe_match{ this->locations_.find({SchemaReferenceType::Static, new_id})}; if (maybe_match != this->locations_.cend() && - maybe_match->second.pointer != common_pointer) { + maybe_match->second.pointer != common_pointer_weak) { throw_already_exists(new_id); } @@ -618,8 +623,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, metaschema.canonicalize(); assert(entry.common.subschema.get().defines("$schema")); + auto schema_pointer{common_pointer_weak}; + schema_pointer.push_back(std::cref(KEYWORD_SCHEMA)); const auto [it, inserted] = this->references_.insert_or_assign( - {SchemaReferenceType::Static, common_pointer.concat({"$schema"})}, + {SchemaReferenceType::Static, std::move(schema_pointer)}, SchemaFrame::ReferencesEntry{.original = maybe_metaschema, .destination = metaschema.recompose(), @@ -809,7 +816,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, // Resolve references after all framing was performed for (const auto &entry : subschema_entries) { const auto &common_pointer_weak{entry.common.pointer}; - const auto common_pointer{to_pointer(common_pointer_weak)}; if (entry.common.subschema.get().is_object()) { const auto nearest_bases{find_nearest_bases( base_uris, common_pointer_weak, @@ -825,8 +831,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, } ref.canonicalize(); + auto ref_pointer{common_pointer_weak}; + ref_pointer.push_back(std::cref(KEYWORD_REF)); const auto [it, inserted] = this->references_.insert_or_assign( - {SchemaReferenceType::Static, common_pointer.concat({"$ref"})}, + {SchemaReferenceType::Static, std::move(ref_pointer)}, SchemaFrame::ReferencesEntry{.original = original, .destination = ref.recompose(), .base = std::string_view{}, @@ -848,7 +856,8 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, // https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.8.2.4.2.1 if (ref != "#") { throw sourcemeta::core::SchemaReferenceError( - entry.id.value_or(""), common_pointer.concat({"$recursiveRef"}), + entry.id.value_or(""), + to_pointer(common_pointer_weak).concat({"$recursiveRef"}), "Invalid recursive reference"); } @@ -860,8 +869,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, ? SchemaReferenceType::Static : SchemaReferenceType::Dynamic}; const sourcemeta::core::URI anchor_uri{anchor_uri_string}; + auto recursive_ref_pointer{common_pointer_weak}; + recursive_ref_pointer.push_back(std::cref(KEYWORD_RECURSIVE_REF)); const auto [it, inserted] = this->references_.insert_or_assign( - {reference_type, common_pointer.concat({"$recursiveRef"})}, + {reference_type, std::move(recursive_ref_pointer)}, SchemaFrame::ReferencesEntry{.original = ref, .destination = anchor_uri.recompose(), .base = std::string_view{}, @@ -896,10 +907,12 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, !has_fragment || (has_fragment && maybe_static_frame != this->locations_.end() && maybe_dynamic_frame == this->locations_.end())}; + auto dynamic_ref_pointer{common_pointer_weak}; + dynamic_ref_pointer.push_back(std::cref(KEYWORD_DYNAMIC_REF)); const auto [it, inserted] = this->references_.insert_or_assign( {behaves_as_static ? SchemaReferenceType::Static : SchemaReferenceType::Dynamic, - common_pointer.concat({"$dynamicRef"})}, + std::move(dynamic_ref_pointer)}, SchemaFrame::ReferencesEntry{.original = original, .destination = std::move(ref_string), .base = std::string_view{}, @@ -979,7 +992,7 @@ auto SchemaFrame::references() const noexcept -> const References & { } auto SchemaFrame::reference(const SchemaReferenceType type, - const Pointer &pointer) const + const WeakPointer &pointer) const -> std::optional> { const auto result{this->references_.find({type, pointer})}; if (result != this->references_.cend()) { @@ -1015,7 +1028,7 @@ auto SchemaFrame::vocabularies(const Location &location, } auto SchemaFrame::uri(const Location &location, - const Pointer &relative_schema_location) const + const WeakPointer &relative_schema_location) const -> JSON::String { return to_uri(this->relative_instance_location(location).concat( relative_schema_location), @@ -1024,7 +1037,7 @@ auto SchemaFrame::uri(const Location &location, } auto SchemaFrame::traverse(const Location &location, - const Pointer &relative_schema_location) const + const WeakPointer &relative_schema_location) const -> const Location & { const auto new_uri{this->uri(location, relative_schema_location)}; const auto static_match{ @@ -1057,7 +1070,7 @@ auto SchemaFrame::traverse(const std::string_view uri) const return std::nullopt; } -auto SchemaFrame::traverse(const Pointer &pointer) const +auto SchemaFrame::traverse(const WeakPointer &pointer) const -> std::optional> { // TODO: This is slow. Consider adding a pointer-indexed secondary // lookup structure to SchemaFrame @@ -1070,7 +1083,7 @@ auto SchemaFrame::traverse(const Pointer &pointer) const return std::nullopt; } -auto SchemaFrame::uri(const Pointer &pointer) const +auto SchemaFrame::uri(const WeakPointer &pointer) const -> std::optional> { for (const auto &entry : this->locations_) { if (entry.second.pointer == pointer) { @@ -1082,11 +1095,11 @@ auto SchemaFrame::uri(const Pointer &pointer) const } auto SchemaFrame::dereference(const Location &location, - const Pointer &relative_schema_location) const + const WeakPointer &relative_schema_location) const -> std::pair>> { const auto effective_location{ - location.pointer.concat({relative_schema_location})}; + location.pointer.concat(relative_schema_location)}; const auto maybe_reference_entry{this->references_.find( {SchemaReferenceType::Static, effective_location})}; if (maybe_reference_entry == this->references_.cend()) { @@ -1118,7 +1131,7 @@ auto SchemaFrame::for_each_resource_uri( } auto SchemaFrame::for_each_unresolved_reference( - const std::function + const std::function &callback) const -> void { for (const auto &[key, reference] : this->references_) { if (!this->traverse(reference.destination).has_value()) { @@ -1127,7 +1140,7 @@ auto SchemaFrame::for_each_unresolved_reference( } } -auto SchemaFrame::has_references_to(const Pointer &pointer) const -> bool { +auto SchemaFrame::has_references_to(const WeakPointer &pointer) const -> bool { for (const auto &reference : this->references_) { assert(!reference.first.second.empty()); assert(reference.first.second.back().is_property()); @@ -1157,7 +1170,8 @@ auto SchemaFrame::has_references_to(const Pointer &pointer) const -> bool { return false; } -auto SchemaFrame::has_references_through(const Pointer &pointer) const -> bool { +auto SchemaFrame::has_references_through(const WeakPointer &pointer) const + -> bool { for (const auto &reference : this->references_) { assert(!reference.first.second.empty()); assert(reference.first.second.back().is_property()); @@ -1187,8 +1201,40 @@ auto SchemaFrame::has_references_through(const Pointer &pointer) const -> bool { return false; } +auto SchemaFrame::has_references_through(const WeakPointer &pointer, + const WeakPointer::Token &tail) const + -> bool { + for (const auto &reference : this->references_) { + assert(!reference.first.second.empty()); + assert(reference.first.second.back().is_property()); + + if (reference.first.first == SchemaReferenceType::Static) { + const auto match{this->locations_.find( + {reference.first.first, reference.second.destination})}; + if (match != this->locations_.cend() && + match->second.pointer.starts_with(pointer, tail)) { + return true; + } + } else { + for (const auto &location : this->locations_) { + if (location.second.type == LocationType::Anchor && + location.first.first == SchemaReferenceType::Dynamic && + location.second.pointer.starts_with(pointer, tail)) { + if (!reference.second.fragment.has_value() || + URI{location.first.second}.fragment().value_or("") == + reference.second.fragment.value()) { + return true; + } + } + } + } + } + + return false; +} + auto SchemaFrame::relative_instance_location(const Location &location) const - -> Pointer { + -> WeakPointer { return location.pointer.slice(location.relative_pointer); } diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_bundle.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_bundle.h index 17dbebbd..18f4fe64 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_bundle.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_bundle.h @@ -24,8 +24,8 @@ namespace sourcemeta::core { /// - Pointer (reference keyword from the origin) /// - Target URI /// - Target schema -using DependencyCallback = std::function; +using DependencyCallback = std::function; /// @ingroup jsonschema /// diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h index 497f7679..9f95dbe8 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h @@ -83,7 +83,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { /// have a static and a dynamic reference to the same location /// on the same schema object. using References = - std::map, ReferencesEntry>; + std::map, ReferencesEntry>; #if defined(__GNUC__) #pragma GCC diagnostic push @@ -109,8 +109,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { std::optional parent; LocationType type; std::string_view base; - // TODO: Turn this into a weak pointer - Pointer pointer; + WeakPointer pointer; std::size_t relative_pointer; std::string_view dialect; SchemaBaseDialect base_dialect; @@ -151,7 +150,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { /// Get a specific reference entry by type and pointer [[nodiscard]] auto reference(const SchemaReferenceType type, - const Pointer &pointer) const + const WeakPointer &pointer) const -> std::optional>; /// Check whether the analysed schema has no external references @@ -168,12 +167,12 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { /// Get the URI associated with a location entry [[nodiscard]] auto uri(const Location &location, - const Pointer &relative_schema_location = empty_pointer) const + const WeakPointer &relative_schema_location = empty_weak_pointer) const -> JSON::String; /// Get the location associated by traversing a pointer from another location [[nodiscard]] auto traverse(const Location &location, - const Pointer &relative_schema_location) const + const WeakPointer &relative_schema_location) const -> const Location &; /// Get the location associated with a given URI @@ -181,17 +180,17 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { -> std::optional>; /// Get the location associated with a given pointer - [[nodiscard]] auto traverse(const Pointer &pointer) const + [[nodiscard]] auto traverse(const WeakPointer &pointer) const -> std::optional>; /// Turn an absolute pointer into a location URI - [[nodiscard]] auto uri(const Pointer &pointer) const + [[nodiscard]] auto uri(const WeakPointer &pointer) const -> std::optional>; /// Try to dereference a reference location into its destination location - [[nodiscard]] auto - dereference(const Location &location, - const Pointer &relative_schema_location = empty_pointer) const + [[nodiscard]] auto dereference( + const Location &location, + const WeakPointer &relative_schema_location = empty_weak_pointer) const -> std::pair>>; @@ -202,19 +201,25 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { /// Iterate over all unresolved references (where destination cannot be /// traversed) auto for_each_unresolved_reference( - const std::function + const std::function &callback) const -> void; /// Check if there are any references to a given location pointer - [[nodiscard]] auto has_references_to(const Pointer &pointer) const -> bool; + [[nodiscard]] auto has_references_to(const WeakPointer &pointer) const + -> bool; /// Check if there are any references that go through a given location pointer - [[nodiscard]] auto has_references_through(const Pointer &pointer) const + [[nodiscard]] auto has_references_through(const WeakPointer &pointer) const -> bool; + /// Check if there are any references that go through a given location pointer + /// with a tail token + [[nodiscard]] auto + has_references_through(const WeakPointer &pointer, + const WeakPointer::Token &tail) const -> bool; /// Get the relative instance location pointer for a given location entry [[nodiscard]] auto relative_instance_location(const Location &location) const - -> Pointer; + -> WeakPointer; /// Check if the frame has no analysed data [[nodiscard]] auto empty() const noexcept -> bool; diff --git a/vendor/core/src/core/jsonschema/transformer.cc b/vendor/core/src/core/jsonschema/transformer.cc index a46e5e84..d044eb56 100644 --- a/vendor/core/src/core/jsonschema/transformer.cc +++ b/vendor/core/src/core/jsonschema/transformer.cc @@ -107,13 +107,16 @@ auto SchemaTransformer::check(const JSON &schema, const SchemaWalker &walker, // Framing may report resource twice or more given default identifiers and // nested resources, risking reporting the same errors twice - if (!visited.insert(entry.second.pointer).second) { + const auto [visited_iterator, inserted] = + visited.insert(to_pointer(entry.second.pointer)); + if (!inserted) { continue; } + const auto &entry_pointer{*visited_iterator}; subschema_count += 1; - const auto ¤t{get(schema, entry.second.pointer)}; + const auto ¤t{get(schema, entry_pointer)}; const auto current_vocabularies{frame.vocabularies(entry.second, resolver)}; bool subresult{true}; for (const auto &rule : this->rules) { @@ -121,7 +124,7 @@ auto SchemaTransformer::check(const JSON &schema, const SchemaWalker &walker, walker, resolver, frame, entry.second)}; if (outcome.applies) { subresult = false; - callback(entry.second.pointer, rule->name(), rule->message(), outcome); + callback(entry_pointer, rule->name(), rule->message(), outcome); } } @@ -180,13 +183,16 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, // Framing may report resource twice or more given default identifiers and // nested resources, risking reporting the same errors twice - if (!visited.insert(entry.second.pointer).second) { + const auto [visited_iterator, inserted] = + visited.insert(to_pointer(entry.second.pointer)); + if (!inserted) { continue; } + const auto &entry_pointer{*visited_iterator}; subschema_count += 1; - auto ¤t{get(schema, entry.second.pointer)}; + auto ¤t{get(schema, entry_pointer)}; const auto current_vocabularies{ frame.vocabularies(entry.second, resolver)}; @@ -199,10 +205,6 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, continue; } - // Store data we need before invalidating the frame - const auto transformed_pointer{entry.second.pointer}; - const auto transformed_relative_pointer{entry.second.relative_pointer}; - // Collect reference information BEFORE invalidating the frame. // We need to save this data because after the transform, the old // frame's views may point to invalid memory, and a new frame won't @@ -218,8 +220,9 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, const auto &target{destination.value().get()}; potentially_broken_references.push_back( - {reference.first.second, JSON::String{reference.second.original}, - reference.second.destination, target.pointer, + {to_pointer(reference.first.second), + JSON::String{reference.second.original}, + reference.second.destination, to_pointer(target.pointer), target.relative_pointer}); } @@ -228,7 +231,7 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, } catch (const SchemaAbortError &) { result = false; subschema_failed = true; - callback(transformed_pointer, rule->name(), rule->message(), outcome); + callback(entry_pointer, rule->name(), rule->message(), outcome); continue; } @@ -236,7 +239,7 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, frame.analyse(schema, walker, resolver, default_dialect, default_id); - const auto new_location{frame.traverse(transformed_pointer)}; + const auto new_location{frame.traverse(to_weak_pointer(entry_pointer))}; // The location should still exist after transform assert(new_location.has_value()); @@ -271,7 +274,8 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, saved_reference.destination, saved_reference.origin, saved_reference.target_pointer.slice( saved_reference.target_relative_pointer), - transformed_pointer.slice(transformed_relative_pointer))}; + entry_pointer.slice( + new_location.value().get().relative_pointer))}; // Note we use the base from the original reference before any // canonicalisation takes place so that we don't overly change @@ -290,7 +294,7 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker, current.fast_hash()}; if (processed_rules.contains(mark)) { throw SchemaTransformRuleProcessedTwiceError(rule->name(), - transformed_pointer); + entry_pointer); } processed_rules.emplace(std::move(mark)); diff --git a/vendor/core/src/extension/alterschema/common/content_schema_without_media_type.h b/vendor/core/src/extension/alterschema/common/content_schema_without_media_type.h index dc26b817..fce5a85e 100644 --- a/vendor/core/src/extension/alterschema/common/content_schema_without_media_type.h +++ b/vendor/core/src/extension/alterschema/common/content_schema_without_media_type.h @@ -1,4 +1,7 @@ class ContentSchemaWithoutMediaType final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"contentSchema"}; + public: ContentSchemaWithoutMediaType() : SchemaTransformRule{ @@ -18,14 +21,14 @@ class ContentSchemaWithoutMediaType final : public SchemaTransformRule { ONLY_CONTINUE_IF(vocabularies.contains_any( {Vocabularies::Known::JSON_Schema_2020_12_Content, Vocabularies::Known::JSON_Schema_2019_09_Content}) && - schema.is_object() && schema.defines("contentSchema") && + schema.is_object() && schema.defines(KEYWORD) && !schema.defines("contentMediaType")); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"contentSchema"}))); - return APPLIES_TO_KEYWORDS("contentSchema"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("contentSchema"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/else_empty.h b/vendor/core/src/extension/alterschema/common/else_empty.h index 4b7f406b..fe883ad5 100644 --- a/vendor/core/src/extension/alterschema/common/else_empty.h +++ b/vendor/core/src/extension/alterschema/common/else_empty.h @@ -1,4 +1,7 @@ class ElseEmpty final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"else"}; + public: ElseEmpty() : SchemaTransformRule{"else_empty", @@ -15,17 +18,17 @@ class ElseEmpty final : public SchemaTransformRule { {Vocabularies::Known::JSON_Schema_2020_12_Applicator, Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7}) && - schema.is_object() && schema.defines("else") && - is_schema(schema.at("else")) && is_empty_schema(schema.at("else")) && - (schema.at("else").is_object() || + schema.is_object() && schema.defines(KEYWORD) && + is_schema(schema.at(KEYWORD)) && is_empty_schema(schema.at(KEYWORD)) && + (schema.at(KEYWORD).is_object() || (!schema.defines("if") || !(schema.at("if").is_boolean() && schema.at("if").to_boolean())))); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"else"}))); - return APPLIES_TO_KEYWORDS("else"); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("else"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/else_without_if.h b/vendor/core/src/extension/alterschema/common/else_without_if.h index 5039ec10..7b871dd0 100644 --- a/vendor/core/src/extension/alterschema/common/else_without_if.h +++ b/vendor/core/src/extension/alterschema/common/else_without_if.h @@ -1,4 +1,7 @@ class ElseWithoutIf final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"else"}; + public: ElseWithoutIf() : SchemaTransformRule{"else_without_if", @@ -18,14 +21,14 @@ class ElseWithoutIf final : public SchemaTransformRule { {Vocabularies::Known::JSON_Schema_2020_12_Applicator, Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7}) && - schema.is_object() && schema.defines("else") && + schema.is_object() && schema.defines(KEYWORD) && !schema.defines("if")); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"else"}))); - return APPLIES_TO_KEYWORDS("else"); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("else"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/if_without_then_else.h b/vendor/core/src/extension/alterschema/common/if_without_then_else.h index 923fe117..3f19961c 100644 --- a/vendor/core/src/extension/alterschema/common/if_without_then_else.h +++ b/vendor/core/src/extension/alterschema/common/if_without_then_else.h @@ -1,4 +1,7 @@ class IfWithoutThenElse final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"if"}; + public: IfWithoutThenElse() : SchemaTransformRule{ @@ -19,14 +22,14 @@ class IfWithoutThenElse final : public SchemaTransformRule { {Vocabularies::Known::JSON_Schema_2020_12_Applicator, Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7}) && - schema.is_object() && schema.defines("if") && + schema.is_object() && schema.defines(KEYWORD) && !schema.defines("then") && !schema.defines("else")); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"if"}))); - return APPLIES_TO_KEYWORDS("if"); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("if"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/non_applicable_additional_items.h b/vendor/core/src/extension/alterschema/common/non_applicable_additional_items.h index d1a9b2b2..6b7f39e2 100644 --- a/vendor/core/src/extension/alterschema/common/non_applicable_additional_items.h +++ b/vendor/core/src/extension/alterschema/common/non_applicable_additional_items.h @@ -1,4 +1,7 @@ class NonApplicableAdditionalItems final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"additionalItems"}; + public: NonApplicableAdditionalItems() : SchemaTransformRule{ @@ -21,21 +24,20 @@ class NonApplicableAdditionalItems final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_6, Vocabularies::Known::JSON_Schema_Draft_4, Vocabularies::Known::JSON_Schema_Draft_3}) && - schema.is_object() && schema.defines("additionalItems")); - + schema.is_object() && schema.defines(KEYWORD)); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"additionalItems"}))); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); if (schema.defines("items") && is_schema(schema.at("items"))) { - return APPLIES_TO_KEYWORDS("additionalItems", "items"); + return APPLIES_TO_KEYWORDS(KEYWORD, "items"); } else if (!schema.defines("items")) { - return APPLIES_TO_KEYWORDS("additionalItems"); + return APPLIES_TO_KEYWORDS(KEYWORD); } else { return false; } } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("additionalItems"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/non_applicable_type_specific_keywords.h b/vendor/core/src/extension/alterschema/common/non_applicable_type_specific_keywords.h index 0c90c8ce..98bebe19 100644 --- a/vendor/core/src/extension/alterschema/common/non_applicable_type_specific_keywords.h +++ b/vendor/core/src/extension/alterschema/common/non_applicable_type_specific_keywords.h @@ -75,7 +75,7 @@ class NonApplicableTypeSpecificKeywords final : public SchemaTransformRule { if ((metadata.instances & current_types).none()) { // Skip keywords that have references pointing to them if (frame.has_references_through( - location.pointer.concat({entry.first}))) { + location.pointer, WeakPointer::Token{std::cref(entry.first)})) { continue; } diff --git a/vendor/core/src/extension/alterschema/common/not_false.h b/vendor/core/src/extension/alterschema/common/not_false.h index 65764398..f9016890 100644 --- a/vendor/core/src/extension/alterschema/common/not_false.h +++ b/vendor/core/src/extension/alterschema/common/not_false.h @@ -1,4 +1,7 @@ class NotFalse final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"not"}; + public: NotFalse() : SchemaTransformRule{"not_false", @@ -17,15 +20,15 @@ class NotFalse final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_7, Vocabularies::Known::JSON_Schema_Draft_6, Vocabularies::Known::JSON_Schema_Draft_4}) && - schema.is_object() && schema.defines("not") && - schema.at("not").is_boolean() && - !schema.at("not").to_boolean()); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"not"}))); - return APPLIES_TO_KEYWORDS("not"); + schema.is_object() && schema.defines(KEYWORD) && + schema.at(KEYWORD).is_boolean() && + !schema.at(KEYWORD).to_boolean()); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("not"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/orphan_definitions.h b/vendor/core/src/extension/alterschema/common/orphan_definitions.h index 0ff2e3e6..426cd562 100644 --- a/vendor/core/src/extension/alterschema/common/orphan_definitions.h +++ b/vendor/core/src/extension/alterschema/common/orphan_definitions.h @@ -28,7 +28,6 @@ class OrphanDefinitions final : public SchemaTransformRule { schema.defines("definitions")}; ONLY_CONTINUE_IF(has_defs || has_definitions); - const auto prefix_size{location.pointer.size()}; bool has_external_to_defs{false}; bool has_external_to_definitions{false}; std::unordered_set outside_referenced_defs; @@ -37,16 +36,16 @@ class OrphanDefinitions final : public SchemaTransformRule { for (const auto &[key, reference] : frame.references()) { const auto destination_location{frame.traverse(reference.destination)}; if (destination_location.has_value()) { + const auto &destination_pointer{destination_location->get().pointer}; if (has_defs) { - process_reference(key.second, destination_location->get().pointer, - location.pointer, prefix_size, "$defs", - has_external_to_defs, outside_referenced_defs); + process_reference(key.second, destination_pointer, location.pointer, + "$defs", has_external_to_defs, + outside_referenced_defs); } if (has_definitions) { - process_reference(key.second, destination_location->get().pointer, - location.pointer, prefix_size, "definitions", - has_external_to_definitions, + process_reference(key.second, destination_pointer, location.pointer, + "definitions", has_external_to_definitions, outside_referenced_definitions); } } @@ -77,18 +76,16 @@ class OrphanDefinitions final : public SchemaTransformRule { } private: - static auto - process_reference(const Pointer &source_pointer, - const Pointer &destination_pointer, const Pointer &prefix, - const std::size_t prefix_size, std::string_view container, - bool &has_external, - std::unordered_set &referenced) -> void { + static auto process_reference( + const WeakPointer &source_pointer, const WeakPointer &destination_pointer, + const WeakPointer &prefix, std::string_view container, bool &has_external, + std::unordered_set &referenced) -> void { if (!destination_pointer.starts_with(prefix, container) || - destination_pointer.size() <= prefix_size + 1) { + destination_pointer.size() <= prefix.size() + 1) { return; } - const auto &entry_token{destination_pointer.at(prefix_size + 1)}; + const auto &entry_token{destination_pointer.at(prefix.size() + 1)}; if (entry_token.is_property()) { const auto &entry_name{entry_token.to_property()}; if (!source_pointer.starts_with(prefix, container)) { diff --git a/vendor/core/src/extension/alterschema/common/required_properties_in_properties.h b/vendor/core/src/extension/alterschema/common/required_properties_in_properties.h index 53e8baeb..8cddcb46 100644 --- a/vendor/core/src/extension/alterschema/common/required_properties_in_properties.h +++ b/vendor/core/src/extension/alterschema/common/required_properties_in_properties.h @@ -77,7 +77,7 @@ class RequiredPropertiesInProperties final : public SchemaTransformRule { const SchemaResolver &resolver, const JSON::String &property) const -> bool { if (location.parent.has_value()) { - const auto parent_pointer{to_pointer(location.parent.value())}; + const auto &parent_pointer{location.parent.value()}; const auto relative_pointer{ location.pointer.resolve_from(parent_pointer)}; assert(!relative_pointer.empty() && relative_pointer.at(0).is_property()); @@ -92,8 +92,8 @@ class RequiredPropertiesInProperties final : public SchemaTransformRule { type == SchemaKeywordType::ApplicatorValueInPlaceMaybe || type == SchemaKeywordType::ApplicatorValueInPlaceNegate || type == SchemaKeywordType::ApplicatorValueInPlaceOther) { - return this->defined_in_properties_sibling( - get(root, location.parent.value()), property); + return this->defined_in_properties_sibling(get(root, parent_pointer), + property); } } diff --git a/vendor/core/src/extension/alterschema/common/then_empty.h b/vendor/core/src/extension/alterschema/common/then_empty.h index 832c9f78..36c1d220 100644 --- a/vendor/core/src/extension/alterschema/common/then_empty.h +++ b/vendor/core/src/extension/alterschema/common/then_empty.h @@ -1,4 +1,7 @@ class ThenEmpty final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"then"}; + public: ThenEmpty() : SchemaTransformRule{"then_empty", @@ -15,17 +18,17 @@ class ThenEmpty final : public SchemaTransformRule { {Vocabularies::Known::JSON_Schema_2020_12_Applicator, Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7}) && - schema.is_object() && schema.defines("then") && - is_schema(schema.at("then")) && is_empty_schema(schema.at("then")) && - (schema.at("then").is_object() || + schema.is_object() && schema.defines(KEYWORD) && + is_schema(schema.at(KEYWORD)) && is_empty_schema(schema.at(KEYWORD)) && + (schema.at(KEYWORD).is_object() || (!schema.defines("if") || !(schema.at("if").is_boolean() && schema.at("if").to_boolean())))); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"then"}))); - return APPLIES_TO_KEYWORDS("then"); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("then"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/then_without_if.h b/vendor/core/src/extension/alterschema/common/then_without_if.h index 86354ad6..ed557568 100644 --- a/vendor/core/src/extension/alterschema/common/then_without_if.h +++ b/vendor/core/src/extension/alterschema/common/then_without_if.h @@ -1,4 +1,7 @@ class ThenWithoutIf final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"then"}; + public: ThenWithoutIf() : SchemaTransformRule{"then_without_if", @@ -18,14 +21,14 @@ class ThenWithoutIf final : public SchemaTransformRule { {Vocabularies::Known::JSON_Schema_2020_12_Applicator, Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7}) && - schema.is_object() && schema.defines("then") && + schema.is_object() && schema.defines(KEYWORD) && !schema.defines("if")); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"then"}))); - return APPLIES_TO_KEYWORDS("then"); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("then"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/unknown_local_ref.h b/vendor/core/src/extension/alterschema/common/unknown_local_ref.h index 8bb1b6b1..c204a319 100644 --- a/vendor/core/src/extension/alterschema/common/unknown_local_ref.h +++ b/vendor/core/src/extension/alterschema/common/unknown_local_ref.h @@ -1,4 +1,7 @@ class UnknownLocalRef final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"$ref"}; + public: UnknownLocalRef() : SchemaTransformRule{ @@ -22,13 +25,14 @@ class UnknownLocalRef final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_6, Vocabularies::Known::JSON_Schema_Draft_4, Vocabularies::Known::JSON_Schema_Draft_3})); - ONLY_CONTINUE_IF(schema.is_object() && schema.defines("$ref") && - schema.at("$ref").is_string()); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines(KEYWORD) && + schema.at(KEYWORD).is_string()); // Find the keyword location entry - const auto absolute_ref_pointer{location.pointer.concat({"$ref"})}; + auto keyword_pointer{location.pointer}; + keyword_pointer.push_back(std::cref(KEYWORD)); const auto reference_entry{ - frame.reference(SchemaReferenceType::Static, absolute_ref_pointer)}; + frame.reference(SchemaReferenceType::Static, keyword_pointer)}; ONLY_CONTINUE_IF(reference_entry.has_value()); // If the keyword has no fragment, continue @@ -46,10 +50,10 @@ class UnknownLocalRef final : public SchemaTransformRule { ONLY_CONTINUE_IF(frame.traverse(reference_base).has_value()); } - return APPLIES_TO_KEYWORDS("$ref"); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("$ref"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h b/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h index 4a02137b..0ab990ba 100644 --- a/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h +++ b/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h @@ -1,4 +1,7 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"allOf"}; + public: UnnecessaryAllOfWrapper() : SchemaTransformRule{"unnecessary_allof_wrapper", @@ -16,9 +19,9 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_7, Vocabularies::Known::JSON_Schema_Draft_6, Vocabularies::Known::JSON_Schema_Draft_4})); - ONLY_CONTINUE_IF(schema.is_object() && schema.defines("allOf") && - schema.at("allOf").is_array() && - !schema.at("allOf").empty()); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines(KEYWORD) && + schema.at(KEYWORD).is_array() && + !schema.at(KEYWORD).empty()); std::unordered_set dependency_blocked; for (const auto &entry : schema.as_object()) { @@ -39,7 +42,7 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { ? parse_schema_type(schema.at("type")) : JSON::TypeSet{}}; - const auto &all_of{schema.at("allOf")}; + const auto &all_of{schema.at(KEYWORD)}; std::vector locations; std::unordered_set elevated; @@ -52,7 +55,9 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { } // Skip entries that have direct references pointing to them - const auto entry_pointer{location.pointer.concat({"allOf", index - 1})}; + auto entry_pointer{location.pointer}; + entry_pointer.push_back(std::cref(KEYWORD)); + entry_pointer.push_back(index - 1); if (frame.has_references_to(entry_pointer)) { continue; } @@ -91,7 +96,7 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { continue; } - locations.push_back(Pointer{"allOf", index - 1, keyword}); + locations.push_back(Pointer{KEYWORD, index - 1, keyword}); elevated.emplace(keyword); for (const auto &dependency : metadata.dependencies) { @@ -112,8 +117,8 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { const auto allof_index{location.at(1).to_index()}; const auto &keyword{location.at(2).to_property()}; schema.try_assign_before( - keyword, schema.at("allOf").at(allof_index).at(keyword), "allOf"); - schema.at("allOf").at(allof_index).erase(keyword); + keyword, schema.at(KEYWORD).at(allof_index).at(keyword), KEYWORD); + schema.at(KEYWORD).at(allof_index).erase(keyword); } } @@ -122,7 +127,7 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { const Pointer ¤t) const -> Pointer override { // The rule moves keywords from /allOf// to / - const auto allof_prefix{current.concat({"allOf"})}; + const auto allof_prefix{current.concat({KEYWORD})}; const auto relative{target.resolve_from(allof_prefix)}; const auto &keyword{relative.at(1).to_property()}; const Pointer old_prefix{allof_prefix.concat({relative.at(0), keyword})}; diff --git a/vendor/core/src/extension/alterschema/linter/additional_properties_default.h b/vendor/core/src/extension/alterschema/linter/additional_properties_default.h index 1a54886d..0e1eed09 100644 --- a/vendor/core/src/extension/alterschema/linter/additional_properties_default.h +++ b/vendor/core/src/extension/alterschema/linter/additional_properties_default.h @@ -1,4 +1,7 @@ class AdditionalPropertiesDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"additionalProperties"}; + public: AdditionalPropertiesDefault() : SchemaTransformRule{ @@ -15,29 +18,27 @@ class AdditionalPropertiesDefault final : public SchemaTransformRule { const sourcemeta::core::SchemaWalker &, const sourcemeta::core::SchemaResolver &) const -> sourcemeta::core::SchemaTransformRule::Result override { - ONLY_CONTINUE_IF(vocabularies.contains_any( - {Vocabularies::Known::JSON_Schema_2020_12_Applicator, - Vocabularies::Known::JSON_Schema_2019_09_Applicator, - Vocabularies::Known::JSON_Schema_Draft_7, - Vocabularies::Known::JSON_Schema_Draft_6, - Vocabularies::Known::JSON_Schema_Draft_4, - Vocabularies::Known::JSON_Schema_Draft_3, - Vocabularies::Known::JSON_Schema_Draft_2, - Vocabularies::Known::JSON_Schema_Draft_2_Hyper, - Vocabularies::Known::JSON_Schema_Draft_1, - Vocabularies::Known::JSON_Schema_Draft_1_Hyper}) && - schema.is_object() && - schema.defines("additionalProperties") && - ((schema.at("additionalProperties").is_boolean() && - schema.at("additionalProperties").to_boolean()) || - (schema.at("additionalProperties").is_object() && - schema.at("additionalProperties").empty()))); + ONLY_CONTINUE_IF( + vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Applicator, + Vocabularies::Known::JSON_Schema_2019_09_Applicator, + Vocabularies::Known::JSON_Schema_Draft_7, + Vocabularies::Known::JSON_Schema_Draft_6, + Vocabularies::Known::JSON_Schema_Draft_4, + Vocabularies::Known::JSON_Schema_Draft_3, + Vocabularies::Known::JSON_Schema_Draft_2, + Vocabularies::Known::JSON_Schema_Draft_2_Hyper, + Vocabularies::Known::JSON_Schema_Draft_1, + Vocabularies::Known::JSON_Schema_Draft_1_Hyper}) && + schema.is_object() && schema.defines(KEYWORD) && + ((schema.at(KEYWORD).is_boolean() && schema.at(KEYWORD).to_boolean()) || + (schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()))); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"additionalProperties"}))); - return APPLIES_TO_KEYWORDS("additionalProperties"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("additionalProperties"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/content_schema_default.h b/vendor/core/src/extension/alterschema/linter/content_schema_default.h index b9795f3b..b4827230 100644 --- a/vendor/core/src/extension/alterschema/linter/content_schema_default.h +++ b/vendor/core/src/extension/alterschema/linter/content_schema_default.h @@ -1,4 +1,7 @@ class ContentSchemaDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"contentSchema"}; + public: ContentSchemaDefault() : SchemaTransformRule{ @@ -15,20 +18,19 @@ class ContentSchemaDefault final : public SchemaTransformRule { const sourcemeta::core::SchemaWalker &, const sourcemeta::core::SchemaResolver &) const -> sourcemeta::core::SchemaTransformRule::Result override { - ONLY_CONTINUE_IF(vocabularies.contains_any( - {Vocabularies::Known::JSON_Schema_2020_12_Content, - Vocabularies::Known::JSON_Schema_2019_09_Content}) && - schema.is_object() && schema.defines("contentSchema") && - ((schema.at("contentSchema").is_boolean() && - schema.at("contentSchema").to_boolean()) || - (schema.at("contentSchema").is_object() && - schema.at("contentSchema").empty()))); + ONLY_CONTINUE_IF( + vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Content, + Vocabularies::Known::JSON_Schema_2019_09_Content}) && + schema.is_object() && schema.defines(KEYWORD) && + ((schema.at(KEYWORD).is_boolean() && schema.at(KEYWORD).to_boolean()) || + (schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()))); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"contentSchema"}))); - return APPLIES_TO_KEYWORDS("contentSchema"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("contentSchema"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/dependencies_default.h b/vendor/core/src/extension/alterschema/linter/dependencies_default.h index 28dc2b08..cb84cfe3 100644 --- a/vendor/core/src/extension/alterschema/linter/dependencies_default.h +++ b/vendor/core/src/extension/alterschema/linter/dependencies_default.h @@ -1,4 +1,7 @@ class DependenciesDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"dependencies"}; + public: DependenciesDefault() : SchemaTransformRule{ @@ -20,15 +23,14 @@ class DependenciesDefault final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_6, Vocabularies::Known::JSON_Schema_Draft_4, Vocabularies::Known::JSON_Schema_Draft_3}) && - schema.is_object() && schema.defines("dependencies") && - schema.at("dependencies").is_object() && - schema.at("dependencies").empty()); + schema.is_object() && schema.defines(KEYWORD) && + schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"dependencies"}))); - return APPLIES_TO_KEYWORDS("dependencies"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("dependencies"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/items_schema_default.h b/vendor/core/src/extension/alterschema/linter/items_schema_default.h index cceff192..8d77ee8a 100644 --- a/vendor/core/src/extension/alterschema/linter/items_schema_default.h +++ b/vendor/core/src/extension/alterschema/linter/items_schema_default.h @@ -1,4 +1,7 @@ class ItemsSchemaDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"items"}; + public: ItemsSchemaDefault() : SchemaTransformRule{"items_schema_default", @@ -26,15 +29,15 @@ class ItemsSchemaDefault final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_2_Hyper, Vocabularies::Known::JSON_Schema_Draft_1, Vocabularies::Known::JSON_Schema_Draft_1_Hyper}) && - schema.is_object() && schema.defines("items") && - ((schema.at("items").is_boolean() && schema.at("items").to_boolean()) || - (schema.at("items").is_object() && schema.at("items").empty()))); - ONLY_CONTINUE_IF( - !frame.has_references_through(location.pointer.concat({"items"}))); - return APPLIES_TO_KEYWORDS("items"); + schema.is_object() && schema.defines(KEYWORD) && + ((schema.at(KEYWORD).is_boolean() && schema.at(KEYWORD).to_boolean()) || + (schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()))); + ONLY_CONTINUE_IF(!frame.has_references_through( + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("items"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/property_names_default.h b/vendor/core/src/extension/alterschema/linter/property_names_default.h index 17529406..49cd48f0 100644 --- a/vendor/core/src/extension/alterschema/linter/property_names_default.h +++ b/vendor/core/src/extension/alterschema/linter/property_names_default.h @@ -1,4 +1,7 @@ class PropertyNamesDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"propertyNames"}; + public: PropertyNamesDefault() : SchemaTransformRule{ @@ -20,15 +23,15 @@ class PropertyNamesDefault final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_2019_09_Applicator, Vocabularies::Known::JSON_Schema_Draft_7, Vocabularies::Known::JSON_Schema_Draft_6}) && - schema.is_object() && schema.defines("propertyNames") && - schema.at("propertyNames").is_object() && - schema.at("propertyNames").empty()); + schema.is_object() && schema.defines(KEYWORD) && + schema.at(KEYWORD).is_object() && + schema.at(KEYWORD).empty()); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"propertyNames"}))); - return APPLIES_TO_KEYWORDS("propertyNames"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("propertyNames"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/unevaluated_items_default.h b/vendor/core/src/extension/alterschema/linter/unevaluated_items_default.h index 543a6c49..7139b483 100644 --- a/vendor/core/src/extension/alterschema/linter/unevaluated_items_default.h +++ b/vendor/core/src/extension/alterschema/linter/unevaluated_items_default.h @@ -1,4 +1,7 @@ class UnevaluatedItemsDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"unevaluatedItems"}; + public: UnevaluatedItemsDefault() : SchemaTransformRule{ @@ -19,17 +22,15 @@ class UnevaluatedItemsDefault final : public SchemaTransformRule { vocabularies.contains_any( {Vocabularies::Known::JSON_Schema_2020_12_Unevaluated, Vocabularies::Known::JSON_Schema_2019_09_Applicator}) && - schema.is_object() && schema.defines("unevaluatedItems") && - ((schema.at("unevaluatedItems").is_boolean() && - schema.at("unevaluatedItems").to_boolean()) || - (schema.at("unevaluatedItems").is_object() && - schema.at("unevaluatedItems").empty()))); + schema.is_object() && schema.defines(KEYWORD) && + ((schema.at(KEYWORD).is_boolean() && schema.at(KEYWORD).to_boolean()) || + (schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()))); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"unevaluatedItems"}))); - return APPLIES_TO_KEYWORDS("unevaluatedItems"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("unevaluatedItems"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/alterschema/linter/unevaluated_properties_default.h b/vendor/core/src/extension/alterschema/linter/unevaluated_properties_default.h index 247bbf1c..f241c66f 100644 --- a/vendor/core/src/extension/alterschema/linter/unevaluated_properties_default.h +++ b/vendor/core/src/extension/alterschema/linter/unevaluated_properties_default.h @@ -1,4 +1,7 @@ class UnevaluatedPropertiesDefault final : public SchemaTransformRule { +private: + static inline const std::string KEYWORD{"unevaluatedProperties"}; + public: UnevaluatedPropertiesDefault() : SchemaTransformRule{ @@ -19,17 +22,15 @@ class UnevaluatedPropertiesDefault final : public SchemaTransformRule { vocabularies.contains_any( {Vocabularies::Known::JSON_Schema_2020_12_Unevaluated, Vocabularies::Known::JSON_Schema_2019_09_Applicator}) && - schema.is_object() && schema.defines("unevaluatedProperties") && - ((schema.at("unevaluatedProperties").is_boolean() && - schema.at("unevaluatedProperties").to_boolean()) || - (schema.at("unevaluatedProperties").is_object() && - schema.at("unevaluatedProperties").empty()))); + schema.is_object() && schema.defines(KEYWORD) && + ((schema.at(KEYWORD).is_boolean() && schema.at(KEYWORD).to_boolean()) || + (schema.at(KEYWORD).is_object() && schema.at(KEYWORD).empty()))); ONLY_CONTINUE_IF(!frame.has_references_through( - location.pointer.concat({"unevaluatedProperties"}))); - return APPLIES_TO_KEYWORDS("unevaluatedProperties"); + location.pointer, WeakPointer::Token{std::cref(KEYWORD)})); + return APPLIES_TO_KEYWORDS(KEYWORD); } auto transform(JSON &schema, const Result &) const -> void override { - schema.erase("unevaluatedProperties"); + schema.erase(KEYWORD); } }; diff --git a/vendor/core/src/extension/editorschema/editorschema.cc b/vendor/core/src/extension/editorschema/editorschema.cc index 43d6f57d..4080abaf 100644 --- a/vendor/core/src/extension/editorschema/editorschema.cc +++ b/vendor/core/src/extension/editorschema/editorschema.cc @@ -10,9 +10,11 @@ namespace { // See https://arxiv.org/abs/2503.11288 for an academic study of this topic auto top_dynamic_anchor_location( const sourcemeta::core::SchemaFrame &frame, - const sourcemeta::core::Pointer ¤t, const std::string_view fragment, + const sourcemeta::core::WeakPointer ¤t, + const std::string_view fragment, const sourcemeta::core::JSON::String &default_uri) - -> std::optional { + -> std::optional< + std::reference_wrapper> { // Get the location object of where we are at the moment const auto uri{frame.uri(current)}; assert(uri.has_value()); @@ -30,21 +32,21 @@ auto top_dynamic_anchor_location( if (location.parent.has_value()) { // If there is a parent resource, keep looking there, but update the default // if the current resource has the dynamic anchor we want - return top_dynamic_anchor_location( - frame, to_pointer(location.parent.value()), fragment, - anchor.has_value() ? anchor_uri : default_uri); + return top_dynamic_anchor_location(frame, location.parent.value(), fragment, + anchor.has_value() ? anchor_uri + : default_uri); // If we are at the top of the schema and it declares the dynamic anchor, we // should use that } else if (anchor.has_value()) { - return anchor.value().get().pointer; + return std::cref(anchor.value().get().pointer); // Otherwise, if we are at the top and the dynamic anchor is not there, use // the default we have so far } else { const auto default_location{frame.traverse(default_uri)}; assert(default_location.has_value()); - return default_location.value().get().pointer; + return std::cref(default_location.value().get().pointer); } } @@ -91,7 +93,7 @@ auto for_editor(JSON &schema, const SchemaWalker &walker, if (key.first == SchemaReferenceType::Dynamic) { if (reference.fragment.has_value()) { - auto destination{top_dynamic_anchor_location( + const auto destination{top_dynamic_anchor_location( frame, key.second, reference.fragment.value(), reference.destination)}; if (!destination.has_value()) { @@ -99,10 +101,11 @@ auto for_editor(JSON &schema, const SchemaWalker &walker, } reference_changes.push_back( - {key.second, to_uri(std::move(destination).value()).recompose(), - keyword, true}); + {to_pointer(key.second), + to_uri(destination.value().get()).recompose(), keyword, true}); } else { - reference_changes.push_back({key.second, "", keyword, true}); + reference_changes.push_back( + {to_pointer(key.second), "", keyword, true}); } } else { if (keyword == "$schema") { @@ -111,7 +114,7 @@ auto for_editor(JSON &schema, const SchemaWalker &walker, const auto origin{frame.traverse(uri.value().get())}; assert(origin.has_value()); reference_changes.push_back( - {key.second, + {to_pointer(key.second), JSON::String{to_string(origin.value().get().base_dialect)}, keyword, false}); continue; @@ -122,11 +125,12 @@ auto for_editor(JSON &schema, const SchemaWalker &walker, const bool should_rename = keyword == "$dynamicRef" || keyword == "$recursiveRef"; reference_changes.push_back( - {key.second, to_uri(result.value().get().pointer).recompose(), - keyword, should_rename}); + {to_pointer(key.second), + to_uri(result.value().get().pointer).recompose(), keyword, + should_rename}); } else { reference_changes.push_back( - {key.second, reference.destination, keyword, false}); + {to_pointer(key.second), reference.destination, keyword, false}); } } } @@ -148,7 +152,8 @@ auto for_editor(JSON &schema, const SchemaWalker &walker, const auto vocabularies{frame.vocabularies(entry.second, resolver)}; subschema_changes.push_back( - {entry.second.pointer, entry.second.base_dialect, add_schema, + {to_pointer(entry.second.pointer), entry.second.base_dialect, + add_schema, vocabularies.contains(Vocabularies::Known::JSON_Schema_2020_12_Core), vocabularies.contains( Vocabularies::Known::JSON_Schema_2019_09_Core)});