diff --git a/DEPENDENCIES b/DEPENDENCIES index 2ee6cf8c..0a8150d0 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,5 +1,5 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 9985d96665aae0a8dfabf63f59b0732b92c64771 +core https://github.com/sourcemeta/core 94ff49bd58ca63c5e5a7fb2435b3bc72872517c4 jsonbinpack https://github.com/sourcemeta/jsonbinpack 8fae212dc7ec02af4bb0cd4e7fccd42a2471f1c1 blaze https://github.com/sourcemeta/blaze 8dba65f8aebfe1ac976168b76e01c20dd406c517 hydra https://github.com/sourcemeta/hydra af9f2c54709d620872ead0c3f8f683c15a0fa702 diff --git a/src/command_inspect.cc b/src/command_inspect.cc index 4ded4e10..fea4188e 100644 --- a/src/command_inspect.cc +++ b/src/command_inspect.cc @@ -94,19 +94,6 @@ auto print_frame(std::ostream &stream, stream << " Parent : \n"; } - const auto &instance_locations{frame.instance_locations(location.second)}; - if (!instance_locations.empty()) { - for (const auto &instance_location : instance_locations) { - if (instance_location.empty()) { - stream << " Instance Location :\n"; - } else { - stream << " Instance Location : "; - sourcemeta::core::stringify(instance_location, stream); - stream << "\n"; - } - } - } - if (std::next(iterator) != frame.locations().cend()) { stream << "\n"; } @@ -162,7 +149,7 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options) const auto dialect{default_dialect(options, configuration)}; sourcemeta::core::SchemaFrame frame{ - sourcemeta::core::SchemaFrame::Mode::Instances}; + sourcemeta::core::SchemaFrame::Mode::References}; try { const auto &custom_resolver{ diff --git a/test/inspect/pass.sh b/test/inspect/pass.sh index f4f26176..b4c7d643 100755 --- a/test/inspect/pass.sh +++ b/test/inspect/pass.sh @@ -33,7 +33,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -56,7 +55,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_custom_extension_json.sh b/test/inspect/pass_custom_extension_json.sh index c6c130d8..cdb1f52d 100755 --- a/test/inspect/pass_custom_extension_json.sh +++ b/test/inspect/pass_custom_extension_json.sh @@ -28,7 +28,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$id Type : Static diff --git a/test/inspect/pass_custom_extension_yaml.sh b/test/inspect/pass_custom_extension_yaml.sh index 60e9a2da..a5d12013 100755 --- a/test/inspect/pass_custom_extension_yaml.sh +++ b/test/inspect/pass_custom_extension_yaml.sh @@ -26,7 +26,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$id Type : Static diff --git a/test/inspect/pass_default_dialect.sh b/test/inspect/pass_default_dialect.sh index 77091060..1b39d717 100755 --- a/test/inspect/pass_default_dialect.sh +++ b/test/inspect/pass_default_dialect.sh @@ -31,7 +31,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -54,7 +53,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_default_dialect_config.sh b/test/inspect/pass_default_dialect_config.sh index d09365b9..79ddef46 100755 --- a/test/inspect/pass_default_dialect_config.sh +++ b/test/inspect/pass_default_dialect_config.sh @@ -36,7 +36,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -59,7 +58,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_default_dialect_config_relative.sh b/test/inspect/pass_default_dialect_config_relative.sh index 838460a8..b7dfe79a 100755 --- a/test/inspect/pass_default_dialect_config_relative.sh +++ b/test/inspect/pass_default_dialect_config_relative.sh @@ -38,7 +38,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -61,7 +60,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_default_dialect_option_config.sh b/test/inspect/pass_default_dialect_option_config.sh index 284dd7d2..ea325795 100755 --- a/test/inspect/pass_default_dialect_option_config.sh +++ b/test/inspect/pass_default_dialect_option_config.sh @@ -37,7 +37,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -60,7 +59,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_json_output.sh b/test/inspect/pass_json_output.sh index 65b31bef..270c9c57 100755 --- a/test/inspect/pass_json_output.sh +++ b/test/inspect/pass_json_output.sh @@ -146,11 +146,7 @@ cat << 'EOF' > "$TMP/expected_json.txt" "base": "https://json-schema.org/draft/2020-12/schema", "fragment": null } - ], - "instances": { - "": [ "" ], - "/$defs/string": [ "" ] - } + ] } EOF diff --git a/test/inspect/pass_no_identifier.sh b/test/inspect/pass_no_identifier.sh index c270caa2..fe23891e 100755 --- a/test/inspect/pass_no_identifier.sh +++ b/test/inspect/pass_no_identifier.sh @@ -34,7 +34,6 @@ cat << EOF > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: file://$(realpath "$TMP")/schema.json#/\$defs Type : Static @@ -57,7 +56,6 @@ cat << EOF > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: file://$(realpath "$TMP")/schema.json#/\$defs/string/type Type : Static diff --git a/test/inspect/pass_without_extension_json.sh b/test/inspect/pass_without_extension_json.sh index 83fc19c0..b00f047d 100755 --- a/test/inspect/pass_without_extension_json.sh +++ b/test/inspect/pass_without_extension_json.sh @@ -33,7 +33,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -56,7 +55,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_without_extension_yaml.sh b/test/inspect/pass_without_extension_yaml.sh index 94a7b197..2bbc85ee 100755 --- a/test/inspect/pass_without_extension_yaml.sh +++ b/test/inspect/pass_without_extension_yaml.sh @@ -28,7 +28,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -51,7 +50,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/test/inspect/pass_yaml.sh b/test/inspect/pass_yaml.sh index 0ed67dee..8bc2b154 100755 --- a/test/inspect/pass_yaml.sh +++ b/test/inspect/pass_yaml.sh @@ -28,7 +28,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs Type : Static @@ -51,7 +50,6 @@ cat << 'EOF' > "$TMP/expected.txt" Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema Parent : - Instance Location : (POINTER) URI: https://example.com#/$defs/string/type Type : Static diff --git a/vendor/core/src/core/json/include/sourcemeta/core/json_hash.h b/vendor/core/src/core/json/include/sourcemeta/core/json_hash.h index 5818b842..49ee1066 100644 --- a/vendor/core/src/core/json/include/sourcemeta/core/json_hash.h +++ b/vendor/core/src/core/json/include/sourcemeta/core/json_hash.h @@ -1,17 +1,23 @@ #ifndef SOURCEMETA_CORE_JSON_HASH_H_ #define SOURCEMETA_CORE_JSON_HASH_H_ -#include // assert -#include // std::uint64_t -#include // std::memcpy +#include // assert +#include // std::uint64_t +#include // std::memcpy +#include // std::reference_wrapper namespace sourcemeta::core { /// @ingroup json template struct HashJSON { using hash_type = std::uint64_t; + inline auto operator()(const T &value) const noexcept -> hash_type { - return value.fast_hash(); + if constexpr (requires { value.get().fast_hash(); }) { + return value.get().fast_hash(); + } else { + return value.fast_hash(); + } } [[nodiscard]] @@ -145,6 +151,21 @@ template struct PropertyHashJSON { } }; +/// @ingroup json +/// Until C++26, `std::reference_wrapper` does not overload `operator==`, +/// so we need custom comparisons for use in i.e. `unordered_set` +/// See +/// https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper/operator_cmp.html +template struct EqualJSON { + inline auto operator()(const T &left, const T &right) const -> bool { + if constexpr (requires { left.get() == right.get(); }) { + return left.get() == right.get(); + } else { + return left == right; + } + } +}; + } // namespace sourcemeta::core #endif diff --git a/vendor/core/src/core/jsonpointer/CMakeLists.txt b/vendor/core/src/core/jsonpointer/CMakeLists.txt index 2cb95a38..20300551 100644 --- a/vendor/core/src/core/jsonpointer/CMakeLists.txt +++ b/vendor/core/src/core/jsonpointer/CMakeLists.txt @@ -1,6 +1,6 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME jsonpointer PRIVATE_HEADERS pointer.h position.h error.h token.h - walker.h template.h + walker.h SOURCES jsonpointer.cc stringify.h parser.h grammar.h position.cc mangle.cc) if(SOURCEMETA_CORE_INSTALL) 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 7354ca70..61da59fc 100644 --- a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h +++ b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer.h @@ -12,7 +12,6 @@ #include #include #include -#include #include // NOLINTEND(misc-include-cleaner) @@ -50,10 +49,6 @@ const Pointer empty_pointer; /// A global constant instance of the empty JSON WeakPointer. const WeakPointer empty_weak_pointer; -/// @ingroup jsonpointer -/// A JSON Pointer with unresolved wildcards -using PointerTemplate = GenericPointerTemplate; - /// @ingroup jsonpointer /// Get a value from a JSON document using a JSON Pointer (`const` overload). /// @@ -466,27 +461,6 @@ auto stringify(const WeakPointer &pointer, std::basic_ostream &stream) -> void; -/// @ingroup jsonpointer -/// -/// Stringify the input JSON Pointer template into a given C++ standard output -/// stream. For example: -/// -/// ```cpp -/// #include -/// #include -/// #include -/// -/// const sourcemeta::core::Pointer base{"foo", "bar"}; -/// const sourcemeta::core::PointerTemplate pointer{base}; -/// std::ostringstream stream; -/// sourcemeta::core::stringify(pointer, stream); -/// std::cout << stream.str() << std::endl; -/// ``` -SOURCEMETA_CORE_JSONPOINTER_EXPORT -auto stringify(const PointerTemplate &pointer, - std::basic_ostream &stream) - -> void; - /// @ingroup jsonpointer /// /// Stringify the input JSON Pointer into a C++ standard string. For example: @@ -525,27 +499,6 @@ auto to_string(const WeakPointer &pointer) -> std::basic_string>; -/// @ingroup jsonpointer -/// -/// Stringify the input JSON Pointer template into a C++ standard string. For -/// example: -/// -/// ```cpp -/// #include -/// #include -/// #include -/// -/// sourcemeta::core::PointerTemplate pointer; -/// pointer.emplace_back(sourcemeta::core::Pointer::Token{"foo"}); -/// pointer.emplace_back(sourcemeta::core::PointerTemplate::Wildcard::Property); -/// const std::string result{sourcemeta::core::to_string(pointer)}; -/// std::cout << result << '\n'; -/// ``` -SOURCEMETA_CORE_JSONPOINTER_EXPORT -auto to_string(const PointerTemplate &pointer) - -> std::basic_string>; - /// @ingroup jsonpointer /// /// Mangle a JSON Pointer template and prefix into a collision-free identifier. @@ -574,13 +527,12 @@ auto to_string(const PointerTemplate &pointer) /// #include /// #include /// -/// const sourcemeta::core::PointerTemplate pointer{"foo", "bar"}; +/// const sourcemeta::core::Pointer pointer{"foo", "bar"}; /// const auto result{sourcemeta::core::mangle(pointer, "schema")}; /// assert(result == "Schema_Foo_Bar"); /// ``` SOURCEMETA_CORE_JSONPOINTER_EXPORT -auto mangle(const PointerTemplate &pointer, std::string_view prefix) - -> std::string; +auto mangle(const Pointer &pointer, std::string_view prefix) -> std::string; /// @ingroup jsonpointer /// @@ -717,41 +669,6 @@ struct hash -struct hash> { - auto operator()(const sourcemeta::core::GenericPointerTemplate - &pointer) const noexcept -> std::size_t { - const auto size{pointer.size()}; - if (size == 0) { - return size; - } - - auto hash_element = - [](const typename sourcemeta::core::GenericPointerTemplate< - PointerT>::value_type &element) -> std::size_t { - using Template = sourcemeta::core::GenericPointerTemplate; - const auto *token{std::get_if(&element)}; - if (token) { - return token->is_property() - ? static_cast(token->property_hash().a) - : token->to_index(); - } else { - return element.index(); - } - }; - - const auto &first{*pointer.cbegin()}; - const auto &middle{ - *(pointer.cbegin() + - static_cast::difference_type>(size / 2))}; - const auto &last{*(pointer.cend() - 1)}; - - return size + hash_element(first) + hash_element(middle) + - hash_element(last); - } -}; } // namespace std #endif diff --git a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h b/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h deleted file mode 100644 index 0fdfcc7f..00000000 --- a/vendor/core/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h +++ /dev/null @@ -1,383 +0,0 @@ -#ifndef SOURCEMETA_CORE_JSONPOINTER_TEMPLATE_H_ -#define SOURCEMETA_CORE_JSONPOINTER_TEMPLATE_H_ - -#include - -#include // std::copy, std::all_of -#include // assert -#include // std::uint8_t -#include // std::initializer_list -#include // std::back_inserter -#include // std::optional, std::nullopt -#include // std::is_convertible_v, std::is_null_pointer_v -#include // std::forward -#include // std::variant, std::holds_alternative, std::get -#include // std::vector - -namespace sourcemeta::core { - -/// @ingroup jsonpointer -template class GenericPointerTemplate { -public: - enum class Wildcard : std::uint8_t { Property, Item, Key }; - struct Condition { - auto operator==(const Condition &) const noexcept -> bool = default; - auto operator<(const Condition &) const noexcept -> bool { return false; } - std::optional suffix = std::nullopt; - }; - struct Negation { - auto operator==(const Negation &) const noexcept -> bool = default; - auto operator<(const Negation &) const noexcept -> bool { return false; } - }; - using Regex = typename PointerT::Value::String; - using Token = typename PointerT::Token; - using Container = - std::vector>; - - /// This constructor creates an empty JSON Pointer template. For example: - /// - /// ```cpp - /// #include - /// - /// const sourcemeta::core::PointerTemplate pointer; - /// ``` - GenericPointerTemplate() : data{} {} - - /// This constructor is the preferred way of creating a pointer template. - /// For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::PointerTemplate pointer{ - /// "foo", - /// sourcemeta::core::PointerTemplate::Wildcard::Property}; - /// ``` - GenericPointerTemplate( - std::initializer_list tokens) - : data{std::move(tokens)} {} - - /// This constructor creates a JSON Pointer template from properties or - /// indexes. For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::PointerTemplate pointer_1{"foo", "bar", "baz"}; - /// assert(pointer_1.size() == 3); - /// const sourcemeta::core::PointerTemplate pointer_2{"foo", 1, "bar"}; - /// assert(pointer_2.size() == 3); - /// ``` - template - requires(sizeof...(Args) > 0 && - ((!std::is_null_pointer_v> && - (std::is_convertible_v || - std::is_integral_v>)) && - ...)) - GenericPointerTemplate(Args &&...args) - : data{Token{std::forward(args)}...} {} - - /// This constructor creates a JSON Pointer template from an existing JSON - /// Pointer. For example: - /// - /// ```cpp - /// #include - /// - /// const sourcemeta::core::Pointer base{"foo", "bar"}; - /// const sourcemeta::core::PointerTemplate pointer{base}; - /// ``` - GenericPointerTemplate(const PointerT &other) { this->push_back(other); } - - // Member types - using value_type = typename Container::value_type; - using allocator_type = typename Container::allocator_type; - using size_type = typename Container::size_type; - using difference_type = typename Container::difference_type; - using reference = typename Container::reference; - using const_reference = typename Container::const_reference; - using pointer = typename Container::pointer; - using const_pointer = typename Container::const_pointer; - using iterator = typename Container::iterator; - using const_iterator = typename Container::const_iterator; - using reverse_iterator = typename Container::reverse_iterator; - using const_reverse_iterator = typename Container::const_reverse_iterator; - - /// Get a mutable begin iterator on the pointer - auto begin() noexcept -> iterator { return this->data.begin(); } - /// Get a mutable end iterator on the pointer - auto end() noexcept -> iterator { return this->data.end(); } - /// Get a constant begin iterator on the pointer - [[nodiscard]] auto begin() const noexcept -> const_iterator { - return this->data.begin(); - } - /// Get a constant end iterator on the pointer - [[nodiscard]] auto end() const noexcept -> const_iterator { - return this->data.end(); - } - /// Get a constant begin iterator on the pointer - [[nodiscard]] auto cbegin() const noexcept -> const_iterator { - return this->data.cbegin(); - } - /// Get a constant end iterator on the pointer - [[nodiscard]] auto cend() const noexcept -> const_iterator { - return this->data.cend(); - } - /// Get a mutable reverse begin iterator on the pointer - auto rbegin() noexcept -> reverse_iterator { return this->data.rbegin(); } - /// Get a mutable reverse end iterator on the pointer - auto rend() noexcept -> reverse_iterator { return this->data.rend(); } - /// Get a constant reverse begin iterator on the pointer - [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { - return this->data.rbegin(); - } - /// Get a constant reverse end iterator on the pointer - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { - return this->data.rend(); - } - /// Get a constant reverse begin iterator on the pointer - [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { - return this->data.crbegin(); - } - /// Get a constant reverse end iterator on the pointer - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { - return this->data.crend(); - } - - /// Emplace a token or wildcard into the back of a JSON Pointer template. For - /// example: - /// - /// ```cpp - /// #include - /// - /// sourcemeta::core::PointerTemplate pointer; - /// pointer.emplace_back(sourcemeta::core::PointerTemplate::Wildcard::Property); - /// ``` - template auto emplace_back(Args &&...args) -> reference { - return this->data.emplace_back(std::forward(args)...); - } - - /// Push a copy of a JSON Pointer into the back of a JSON Pointer template. - /// For example: - /// - /// ```cpp - /// #include - /// - /// sourcemeta::core::PointerTemplate result; - /// const sourcemeta::core::Pointer pointer{"bar", "baz"}; - /// result.push_back(pointer); - /// ``` - auto push_back(const PointerT &other) -> void { - this->data.reserve(this->data.size() + other.size()); - std::copy(other.cbegin(), other.cend(), std::back_inserter(this->data)); - } - - /// Remove the last token of a JSON Pointer template. For example: - /// - /// ```cpp - /// #include - /// - /// const sourcemeta::core::Pointer base{"bar", "baz"}; - /// sourcemeta::core::PointerTemplate pointer{base}; - /// pointer.pop_back(); - /// ``` - auto pop_back() -> void { - assert(!this->empty()); - this->data.pop_back(); - } - - /// Concatenate a JSON Pointer template with another JSON Pointer template, - /// getting a new pointer template as a result. For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::Pointer pointer_left{"foo"}; - /// const sourcemeta::core::Pointer pointer_right{"bar", "baz"}; - /// const sourcemeta::core::Pointer pointer_expected{"foo", "bar", "baz"}; - /// - /// const sourcemeta::core::PointerTemplate left{pointer_left}; - /// const sourcemeta::core::PointerTemplate right{pointer_right}; - /// const sourcemeta::core::PointerTemplate expected{pointer_expected}; - /// - /// assert(left.concat(right) == expected); - /// ``` - [[nodiscard]] auto - concat(const GenericPointerTemplate &&other) const - -> GenericPointerTemplate { - GenericPointerTemplate result{*this}; - result.data.reserve(result.data.size() + other.data.size()); - for (auto &&token : other) { - result.emplace_back(std::move(token)); - } - - return result; - } - - /// Check if a JSON Pointer template is empty. - /// For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::PointerTemplate empty_pointer; - /// assert(empty_pointer.empty()); - /// ``` - [[nodiscard]] auto empty() const noexcept -> bool { - return this->data.empty(); - } - - /// Get the size of the JSON Pointer template. For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::Pointer base{"foo", "bar"}; - /// const sourcemeta::core::PointerTemplate pointer{base}; - /// assert(pointer.size() == 2); - /// ``` - [[nodiscard]] auto size() const noexcept -> size_type { - return this->data.size(); - } - - /// Check if a JSON Pointer template only consists in normal non-templated - /// tokens. For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// sourcemeta::core::PointerTemplate pointer; - /// pointer.emplace_back(sourcemeta::core::PointerTemplate::Wildcard::Property); - /// pointer.emplace_back(sourcemeta::core::Pointer::Token{"foo"}); - /// assert(!pointer.trivial()); - /// ``` - [[nodiscard]] auto trivial() const noexcept -> bool { - return std::all_of( - this->data.cbegin(), this->data.cend(), - [](const auto &token) { return std::holds_alternative(token); }); - } - - /// Check if a JSON Pointer template matches another JSON Pointer template. - /// For example: - /// - /// ```cpp - /// #include - /// #include - /// - /// const sourcemeta::core::PointerTemplate left{ - /// sourcemeta::core::PointerTemplate::Condition{}, - /// sourcemeta::core::Pointer::Token{"foo"}}; - /// const sourcemeta::core::PointerTemplate right{ - /// sourcemeta::core::Pointer::Token{"foo"}}; - /// - /// assert(left.matches(right)); - /// assert(right.matches(left)); - /// ``` - [[nodiscard]] auto - matches(const GenericPointerTemplate &other) const noexcept - -> bool { - // TODO: Find a way to simplify this long method - auto iterator_this = this->data.cbegin(); - auto iterator_that = other.data.cbegin(); - - while (iterator_this != this->data.cend() && - iterator_that != other.data.cend()) { - while (iterator_this != this->data.cend() && - std::holds_alternative(*iterator_this)) { - iterator_this += 1; - } - - while (iterator_that != other.data.cend() && - std::holds_alternative(*iterator_that)) { - iterator_that += 1; - } - - if (iterator_this == this->data.cend() || - iterator_that == other.data.cend()) { - break; - } else if (*iterator_this != *iterator_that) { - // Handle regular expressions - if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - const auto &token{std::get(*iterator_this)}; - if (!token.is_property() || - !sourcemeta::core::matches_if_valid( - std::get(*iterator_that), token.to_property())) { - return false; - } - } else if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - const auto &token{std::get(*iterator_that)}; - if (!token.is_property() || - !sourcemeta::core::matches_if_valid( - std::get(*iterator_this), token.to_property())) { - return false; - } - - // Handle wildcards - } else if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - const auto &token{std::get(*iterator_that)}; - const auto wildcard{std::get(*iterator_this)}; - if (wildcard == Wildcard::Key || - (wildcard == Wildcard::Property && !token.is_property()) || - (wildcard == Wildcard::Item && !token.is_index())) { - return false; - } - } else if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - const auto &token{std::get(*iterator_this)}; - const auto wildcard{std::get(*iterator_that)}; - if (wildcard == Wildcard::Key || - (wildcard == Wildcard::Property && !token.is_property()) || - (wildcard == Wildcard::Item && !token.is_index())) { - return false; - } - } else if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - if (std::get(*iterator_that) != Wildcard::Property) { - return false; - } - } else if (std::holds_alternative(*iterator_this) && - std::holds_alternative(*iterator_that)) { - if (std::get(*iterator_this) != Wildcard::Property) { - return false; - } - } else { - return false; - } - } - - iterator_this += 1; - iterator_that += 1; - } - - return iterator_this == this->data.cend() && - iterator_that == other.data.cend(); - } - - /// Compare JSON Pointer template instances - auto operator==(const GenericPointerTemplate &other) const noexcept - -> bool { - return this->data == other.data; - } - - /// Overload to support ordering of JSON Pointer templates. Typically for - /// sorting reasons. - auto operator<(const GenericPointerTemplate &other) const noexcept - -> bool { - return this->data < other.data; - } - -private: - Container data; -}; - -} // namespace sourcemeta::core - -#endif diff --git a/vendor/core/src/core/jsonpointer/jsonpointer.cc b/vendor/core/src/core/jsonpointer/jsonpointer.cc index 330afee4..8a645a7f 100644 --- a/vendor/core/src/core/jsonpointer/jsonpointer.cc +++ b/vendor/core/src/core/jsonpointer/jsonpointer.cc @@ -347,12 +347,6 @@ auto stringify(const WeakPointer &pointer, stringify(pointer, stream); } -auto stringify(const PointerTemplate &pointer, - std::basic_ostream &stream) - -> void { - stringify(pointer, stream); -} - auto to_string(const Pointer &pointer) -> std::basic_string> { @@ -373,16 +367,6 @@ auto to_string(const WeakPointer &pointer) return result.str(); } -auto to_string(const PointerTemplate &pointer) - -> std::basic_string> { - std::basic_ostringstream> - result; - stringify(pointer, result); - return result.str(); -} - auto to_uri(const Pointer &pointer) -> URI { std::basic_ostringstream> diff --git a/vendor/core/src/core/jsonpointer/mangle.cc b/vendor/core/src/core/jsonpointer/mangle.cc index eaab7be9..64cfb317 100644 --- a/vendor/core/src/core/jsonpointer/mangle.cc +++ b/vendor/core/src/core/jsonpointer/mangle.cc @@ -4,7 +4,6 @@ #include // std::setfill, std::setw #include // std::ostringstream #include // std::string_view -#include // std::visit namespace { @@ -24,12 +23,7 @@ constexpr auto RESERVED_Z_LOWER = 'z'; // Special token markers constexpr std::string_view TOKEN_EMPTY = "ZEmpty"; -constexpr std::string_view TOKEN_WILDCARD_PROPERTY = "ZAnyProperty"; -constexpr std::string_view TOKEN_WILDCARD_ITEM = "ZAnyItem"; -constexpr std::string_view TOKEN_WILDCARD_KEY = "ZAnyKey"; -constexpr std::string_view TOKEN_CONDITION = "ZMaybe"; -constexpr std::string_view TOKEN_NEGATION = "ZNot"; -constexpr std::string_view TOKEN_REGEX = "ZRegex"; +constexpr std::string_view TOKEN_INDEX = "ZIndex"; constexpr auto ASCII_MAX = static_cast(0x80); @@ -152,69 +146,22 @@ inline auto encode_string_or_empty(std::ostringstream &output, } } -class TokenVisitor { -public: - explicit TokenVisitor(std::ostringstream &output) noexcept - : output_{output} {} - - auto operator()(const sourcemeta::core::Pointer::Token &token) const noexcept - -> void { - this->output_ << SEPARATOR; - encode_string_or_empty(this->output_, token.to_property()); - } - - auto operator()(const sourcemeta::core::PointerTemplate::Wildcard &wildcard) - const noexcept -> void { - this->output_ << SEPARATOR; - switch (wildcard) { - case sourcemeta::core::PointerTemplate::Wildcard::Property: - this->output_ << TOKEN_WILDCARD_PROPERTY; - break; - case sourcemeta::core::PointerTemplate::Wildcard::Item: - this->output_ << TOKEN_WILDCARD_ITEM; - break; - case sourcemeta::core::PointerTemplate::Wildcard::Key: - this->output_ << TOKEN_WILDCARD_KEY; - break; - } - } - - auto operator()(const sourcemeta::core::PointerTemplate::Condition &condition) - const noexcept -> void { - this->output_ << SEPARATOR << TOKEN_CONDITION; - if (condition.suffix.has_value()) { - encode_string_or_empty(this->output_, condition.suffix.value()); - } - } - - auto - operator()(const sourcemeta::core::PointerTemplate::Negation &) const noexcept - -> void { - this->output_ << SEPARATOR << TOKEN_NEGATION; - } - - auto operator()(const sourcemeta::core::PointerTemplate::Regex ®ex) - const noexcept -> void { - this->output_ << SEPARATOR << TOKEN_REGEX; - encode_string_or_empty(this->output_, regex); - } - -private: - // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) - std::ostringstream &output_; -}; - } // namespace namespace sourcemeta::core { -auto mangle(const PointerTemplate &pointer, const std::string_view prefix) +auto mangle(const Pointer &pointer, const std::string_view prefix) -> std::string { assert(!prefix.empty()); std::ostringstream output; encode_prefix(output, prefix); for (const auto &token : pointer) { - std::visit(TokenVisitor{output}, token); + output << SEPARATOR; + if (token.is_property()) { + encode_string_or_empty(output, token.to_property()); + } else { + output << TOKEN_INDEX << token.to_index(); + } } return output.str(); } diff --git a/vendor/core/src/core/jsonschema/frame.cc b/vendor/core/src/core/jsonschema/frame.cc index 9f9bf66c..9cfb1af5 100644 --- a/vendor/core/src/core/jsonschema/frame.cc +++ b/vendor/core/src/core/jsonschema/frame.cc @@ -219,7 +219,6 @@ auto throw_already_exists(const sourcemeta::core::JSON::String &uri) -> void { } auto store(sourcemeta::core::SchemaFrame::Locations &frame, - sourcemeta::core::SchemaFrame::Instances &instances, const sourcemeta::core::SchemaReferenceType type, const sourcemeta::core::SchemaFrame::LocationType entry_type, const sourcemeta::core::JSON::String &uri, @@ -229,7 +228,6 @@ auto store(sourcemeta::core::SchemaFrame::Locations &frame, const sourcemeta::core::Pointer &pointer_from_base, const sourcemeta::core::JSON::String &dialect, const sourcemeta::core::JSON::String &base_dialect, - std::vector instance_locations, const std::optional &parent, const bool ignore_if_present = false, const bool already_canonical = false) -> void { @@ -249,11 +247,6 @@ auto store(sourcemeta::core::SchemaFrame::Locations &frame, if (!ignore_if_present && !inserted) { throw_already_exists(canonical); } - - if (!instance_locations.empty()) { - instances.insert_or_assign(pointer_from_root, - std::move(instance_locations)); - } } // Check misunderstood struct to be a function @@ -263,114 +256,13 @@ struct InternalEntry { std::optional id; }; -auto traverse_origin_instance_locations( - const sourcemeta::core::SchemaFrame &frame, - const sourcemeta::core::SchemaFrame::Instances &instances, - const sourcemeta::core::Pointer ¤t, - const std::optional &accumulator, - sourcemeta::core::SchemaFrame::Instances::mapped_type &destination, - std::unordered_set< - const sourcemeta::core::SchemaFrame::References::value_type *> &visited) - -> void { - if (accumulator.has_value() && - std::ranges::find(destination, accumulator.value()) == - destination.cend()) { - destination.push_back(accumulator.value()); - } - - for (const auto &reference : frame.references_to(current)) { - if (visited.contains(&reference.get())) { - continue; - } - - visited.insert(&reference.get()); - - const auto subschema_pointer{reference.get().first.second.initial()}; - const auto match{instances.find(subschema_pointer)}; - if (match != instances.cend()) { - for (const auto &instance_location : match->second) { - traverse_origin_instance_locations(frame, instances, subschema_pointer, - instance_location, destination, - visited); - } - } else { - // Even if the parent doesn't have instance locations yet, - // recurse to find the origin of the reference chain - traverse_origin_instance_locations(frame, instances, subschema_pointer, - std::nullopt, destination, visited); - } - } -} - // Check misunderstood struct to be a function // NOLINTNEXTLINE(bugprone-exception-escape) struct CacheSubschema { - sourcemeta::core::PointerTemplate instance_location{}; - sourcemeta::core::PointerTemplate relative_instance_location{}; bool orphan{}; std::optional parent{}; }; -auto is_definition_entry(const sourcemeta::core::Pointer &pointer) -> bool { - if (pointer.size() < 2) { - return false; - } - - const auto &container{pointer.at(pointer.size() - 2)}; - return container.is_property() && (container.to_property() == "$defs" || - container.to_property() == "definitions"); -} - -auto repopulate_instance_locations( - const sourcemeta::core::SchemaFrame &frame, - const sourcemeta::core::SchemaFrame::Instances &instances, - const std::unordered_map &cache, - const sourcemeta::core::Pointer &pointer, const CacheSubschema &cache_entry, - sourcemeta::core::SchemaFrame::Instances::mapped_type &destination, - const std::optional &accumulator) - -> void { - // Definition entries should not inherit instance locations from their parent - // container. They only get instance locations if something references them. - // However, children of definitions should still inherit from their definition - // parent - if (cache_entry.orphan && is_definition_entry(pointer)) { - return; - } - - if (cache_entry.parent.has_value() && - // Don't consider bases from the root subschema, as if that - // subschema has any instance location other than "", then it - // indicates a recursive reference - !cache_entry.parent.value().empty()) { - const auto match{instances.find(cache_entry.parent.value())}; - if (match == instances.cend()) { - return; - } - - for (const auto &parent_instance_location : match->second) { - auto new_accumulator = cache_entry.relative_instance_location; - if (accumulator.has_value()) { - for (const auto &token : accumulator.value()) { - new_accumulator.emplace_back(token); - } - } - - auto result = parent_instance_location; - for (const auto &token : new_accumulator) { - result.emplace_back(token); - } - - if (std::ranges::find(destination, result) == destination.cend()) { - destination.push_back(result); - } - - repopulate_instance_locations( - frame, instances, cache, cache_entry.parent.value(), - cache.at(cache_entry.parent.value()), destination, new_accumulator); - } - } -} - } // namespace namespace sourcemeta::core { @@ -471,24 +363,6 @@ auto SchemaFrame::to_json( root.at("references").push_back(std::move(entry)); } - root.assign_assume_new("instances", JSON::make_object()); - for (const auto &instance : this->instances_) { - if (instance.second.empty()) { - continue; - } - - auto entry{JSON::make_array()}; - for (const auto &pointer : instance.second) { - // TODO: Overload .to_string() for PointerTemplate - std::ostringstream result; - sourcemeta::core::stringify(pointer, result); - entry.push_back(sourcemeta::core::to_json(result.str())); - } - - root.at("instances") - .assign_assume_new(to_string(instance.first), std::move(entry)); - } - return root; } @@ -541,19 +415,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, root_id.value() != default_id.value()}; if (has_explicit_different_id) { const auto default_id_canonical{URI::canonicalize(default_id.value())}; - if (this->mode_ == SchemaFrame::Mode::Instances) { - store(this->locations_, this->instances_, SchemaReferenceType::Static, - SchemaFrame::LocationType::Resource, default_id_canonical, - root_id, root_id.value(), path, sourcemeta::core::empty_pointer, - root_dialect.value(), root_base_dialect.value(), {{}}, - std::nullopt); - } else { - store(this->locations_, this->instances_, SchemaReferenceType::Static, - SchemaFrame::LocationType::Resource, default_id_canonical, - root_id, root_id.value(), path, sourcemeta::core::empty_pointer, - root_dialect.value(), root_base_dialect.value(), {}, - std::nullopt); - } + store(this->locations_, SchemaReferenceType::Static, + SchemaFrame::LocationType::Resource, default_id_canonical, root_id, + root_id.value(), path, sourcemeta::core::empty_pointer, + root_dialect.value(), root_base_dialect.value(), std::nullopt); base_uris.insert({path, {root_id.value(), default_id_canonical}}); } @@ -581,13 +446,8 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, entry.pointer.empty() ? root_id : std::nullopt)}; // Store information - subschemas.emplace( - entry.pointer, - CacheSubschema{.instance_location = entry.instance_location, - .relative_instance_location = - entry.relative_instance_location, - .orphan = entry.orphan, - .parent = entry.parent}); + subschemas.emplace(entry.pointer, CacheSubschema{.orphan = entry.orphan, + .parent = entry.parent}); subschema_entries.emplace_back( InternalEntry{.common = std::move(entry), .id = std::move(id)}); current_subschema_entries.emplace_back(subschema_entries.size() - 1); @@ -644,26 +504,12 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, maybe_match == this->locations_.cend()) { assert(entry.common.base_dialect.has_value()); - if (!(entry.common.orphan) && - this->mode_ == SchemaFrame::Mode::Instances) { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, - SchemaFrame::LocationType::Resource, new_id, root_id, - new_id, entry.common.pointer, - sourcemeta::core::empty_pointer, - entry.common.dialect.value(), - entry.common.base_dialect.value(), - {entry.common.instance_location}, entry.common.parent); - } else { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, - SchemaFrame::LocationType::Resource, new_id, root_id, - new_id, entry.common.pointer, - sourcemeta::core::empty_pointer, - entry.common.dialect.value(), - entry.common.base_dialect.value(), {}, - entry.common.parent); - } + store(this->locations_, SchemaReferenceType::Static, + SchemaFrame::LocationType::Resource, new_id, root_id, + new_id, entry.common.pointer, + sourcemeta::core::empty_pointer, + entry.common.dialect.value(), + entry.common.base_dialect.value(), entry.common.parent); } auto base_uri_match{base_uris.find(entry.common.pointer)}; @@ -712,46 +558,37 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, const auto bases{ find_nearest_bases(base_uris, entry.common.pointer, entry.id)}; - std::vector instance_locations; - if (!entry.common.orphan && - this->mode_ == SchemaFrame::Mode::Instances) { - instance_locations.push_back(entry.common.instance_location); - } - if (bases.first.empty()) { const auto anchor_uri{sourcemeta::core::URI::from_fragment(name)}; const auto relative_anchor_uri{anchor_uri.recompose()}; if (type == AnchorType::Static || type == AnchorType::All) { - store( - this->locations_, this->instances_, SchemaReferenceType::Static, - SchemaFrame::LocationType::Anchor, relative_anchor_uri, root_id, - "", entry.common.pointer, - entry.common.pointer.resolve_from(bases.second), - entry.common.dialect.value(), entry.common.base_dialect.value(), - instance_locations, entry.common.parent); + store(this->locations_, SchemaReferenceType::Static, + SchemaFrame::LocationType::Anchor, relative_anchor_uri, + root_id, "", entry.common.pointer, + entry.common.pointer.resolve_from(bases.second), + entry.common.dialect.value(), + entry.common.base_dialect.value(), entry.common.parent); } if (type == AnchorType::Dynamic || type == AnchorType::All) { - store( - this->locations_, this->instances_, - SchemaReferenceType::Dynamic, SchemaFrame::LocationType::Anchor, - relative_anchor_uri, root_id, "", entry.common.pointer, - entry.common.pointer.resolve_from(bases.second), - entry.common.dialect.value(), entry.common.base_dialect.value(), - instance_locations, entry.common.parent); + store(this->locations_, SchemaReferenceType::Dynamic, + SchemaFrame::LocationType::Anchor, relative_anchor_uri, + root_id, "", entry.common.pointer, + entry.common.pointer.resolve_from(bases.second), + entry.common.dialect.value(), + entry.common.base_dialect.value(), entry.common.parent); // Register a dynamic anchor as a static anchor if possible too if (entry.common.vocabularies.contains( Vocabularies::Known::JSON_Schema_2020_12_Core)) { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, + store(this->locations_, SchemaReferenceType::Static, SchemaFrame::LocationType::Anchor, relative_anchor_uri, root_id, "", entry.common.pointer, entry.common.pointer.resolve_from(bases.second), entry.common.dialect.value(), - entry.common.base_dialect.value(), instance_locations, - entry.common.parent, true); + entry.common.base_dialect.value(), entry.common.parent, + true); } } } else { @@ -768,37 +605,35 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, } if (type == AnchorType::Static || type == AnchorType::All) { - store(this->locations_, this->instances_, + store(this->locations_, sourcemeta::core::SchemaReferenceType::Static, SchemaFrame::LocationType::Anchor, anchor_uri, root_id, base_string, entry.common.pointer, entry.common.pointer.resolve_from(bases.second), entry.common.dialect.value(), - entry.common.base_dialect.value(), instance_locations, - entry.common.parent); + entry.common.base_dialect.value(), entry.common.parent); } if (type == AnchorType::Dynamic || type == AnchorType::All) { - store(this->locations_, this->instances_, + store(this->locations_, sourcemeta::core::SchemaReferenceType::Dynamic, SchemaFrame::LocationType::Anchor, anchor_uri, root_id, base_string, entry.common.pointer, entry.common.pointer.resolve_from(bases.second), entry.common.dialect.value(), - entry.common.base_dialect.value(), instance_locations, - entry.common.parent); + entry.common.base_dialect.value(), entry.common.parent); // Register a dynamic anchor as a static anchor if possible too if (entry.common.vocabularies.contains( Vocabularies::Known::JSON_Schema_2020_12_Core)) { - store(this->locations_, this->instances_, + store(this->locations_, sourcemeta::core::SchemaReferenceType::Static, SchemaFrame::LocationType::Anchor, anchor_uri, root_id, base_string, entry.common.pointer, entry.common.pointer.resolve_from(bases.second), entry.common.dialect.value(), - entry.common.base_dialect.value(), instance_locations, - entry.common.parent, true); + entry.common.base_dialect.value(), entry.common.parent, + true); } } @@ -856,34 +691,19 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, const auto subschema{subschemas.find(pointer)}; if (subschema != subschemas.cend()) { - // Handle orphan schemas - if (!(subschema->second.orphan) && - this->mode_ == SchemaFrame::Mode::Instances) { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, - SchemaFrame::LocationType::Subschema, result, root_id, - current_base, pointer, - pointer.resolve_from(nearest_bases.second), - dialects.first.front(), current_base_dialect, - {subschema->second.instance_location}, - subschema->second.parent, false, true); - } else { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, - SchemaFrame::LocationType::Subschema, result, root_id, - current_base, pointer, - pointer.resolve_from(nearest_bases.second), - dialects.first.front(), current_base_dialect, {}, - subschema->second.parent, false, true); - } + store(this->locations_, SchemaReferenceType::Static, + SchemaFrame::LocationType::Subschema, result, root_id, + current_base, pointer, + pointer.resolve_from(nearest_bases.second), + dialects.first.front(), current_base_dialect, + subschema->second.parent, false, true); } else { - store(this->locations_, this->instances_, - SchemaReferenceType::Static, + store(this->locations_, SchemaReferenceType::Static, SchemaFrame::LocationType::Pointer, result, root_id, current_base, pointer, pointer.resolve_from(nearest_bases.second), - dialects.first.front(), current_base_dialect, {}, - dialects.second, false, true); + dialects.first.front(), current_base_dialect, dialects.second, + false, true); } } } @@ -1055,49 +875,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, this->references_.emplace(std::move(entry)); } } - - if (this->mode_ == sourcemeta::core::SchemaFrame::Mode::Instances) { - // First pass: trace through references to find instance locations. - // This handles definitions that are referenced - for (auto &entry : this->locations_) { - if (entry.second.type == SchemaFrame::LocationType::Pointer) { - continue; - } - - std::unordered_set visited; - traverse_origin_instance_locations( - *this, this->instances_, entry.second.pointer, std::nullopt, - this->instances_[entry.second.pointer], visited); - } - - // Second pass: inherit instance locations from parents (top-down). - // This handles applicator children inheriting from their parent schema - for (auto &entry : this->locations_) { - if (entry.second.type == SchemaFrame::LocationType::Pointer) { - continue; - } - - const auto subschema{subschemas.find(entry.second.pointer)}; - repopulate_instance_locations(*this, this->instances_, subschemas, - subschema->first, subschema->second, - this->instances_[entry.second.pointer], - std::nullopt); - } - - // Third pass: trace references again. Now that inheritance has run, - // schemas from definitions can trace to applicator children that now have - // instance locations from inheritance - for (auto &entry : this->locations_) { - if (entry.second.type == SchemaFrame::LocationType::Pointer) { - continue; - } - - std::unordered_set visited; - traverse_origin_instance_locations( - *this, this->instances_, entry.second.pointer, std::nullopt, - this->instances_[entry.second.pointer], visited); - } - } } auto SchemaFrame::locations() const noexcept -> const Locations & { @@ -1210,17 +987,6 @@ auto SchemaFrame::dereference(const Location &location, return {SchemaReferenceType::Static, destination->second}; } -auto SchemaFrame::instance_locations(const Location &location) const -> const - typename Instances::mapped_type & { - const auto match{this->instances_.find(location.pointer)}; - if (match == this->instances_.cend()) { - static const typename Instances::mapped_type fallback; - return fallback; - } - - return match->second; -} - auto SchemaFrame::references_to(const Pointer &pointer) const -> std::vector< std::reference_wrapper> { std::vector> 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 df933326..ed2ac645 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 @@ -104,7 +104,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { public: /// The mode of framing. More extensive analysis can be compute and memory /// intensive - enum class Mode : std::uint8_t { Locations, References, Instances }; + enum class Mode : std::uint8_t { Locations, References }; SchemaFrame(const Mode mode) : mode_{mode} {} @@ -176,10 +176,6 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { // point to different places. std::map, Location>; - // TODO: Turn the mapped value into a proper set - /// A set of unresolved instance locations - using Instances = std::map>; - /// A set of paths to frame within a schema wrapper using Paths = std::set; @@ -237,10 +233,6 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { -> std::pair>>; - /// Get the unresolved instance locations associated with a location entry - [[nodiscard]] auto instance_locations(const Location &location) const -> const - typename Instances::mapped_type &; - /// Find all references to a given location pointer [[nodiscard]] auto references_to(const Pointer &pointer) const -> std::vector< std::reference_wrapper>; @@ -255,7 +247,6 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { #endif Locations locations_; References references_; - Instances instances_; #if defined(_MSC_VER) #pragma warning(default : 4251 4275) #endif diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h index 8f2845bc..4267240e 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h @@ -214,14 +214,6 @@ struct SchemaIteratorEntry { // TODO: Use "known" enum classes for base dialects std::optional base_dialect; std::reference_wrapper subschema; - - // TODO: These two pointer templates contain some overlap. - // Instead, have a `base_instance_location` and a `relative_instance_location` - // that when concatenated, represent the full `instance_location` - // TODO: Make these WeakPointerTemplate - PointerTemplate instance_location; - PointerTemplate relative_instance_location; - bool orphan; }; diff --git a/vendor/core/src/core/jsonschema/walker.cc b/vendor/core/src/core/jsonschema/walker.cc index 67353224..78706955 100644 --- a/vendor/core/src/core/jsonschema/walker.cc +++ b/vendor/core/src/core/jsonschema/walker.cc @@ -23,8 +23,6 @@ auto ref_overrides_adjacent_keywords(const std::string &base_dialect) -> bool { auto walk(const std::optional &parent, const sourcemeta::core::Pointer &pointer, - const sourcemeta::core::PointerTemplate &instance_location, - const sourcemeta::core::PointerTemplate &relative_instance_location, std::vector &subschemas, const sourcemeta::core::JSON &subschema, const sourcemeta::core::SchemaWalker &walker, @@ -77,16 +75,14 @@ auto walk(const std::optional &parent, resolver, current_base_dialect, current_dialect)}; if (type == SchemaWalkerType_t::Deep || level > 0) { - sourcemeta::core::SchemaIteratorEntry entry{ - .parent = parent, - .pointer = pointer, - .dialect = current_dialect, - .vocabularies = vocabularies, - .base_dialect = current_base_dialect, - .subschema = subschema, - .instance_location = instance_location, - .relative_instance_location = relative_instance_location, - .orphan = orphan}; + sourcemeta::core::SchemaIteratorEntry entry{.parent = parent, + .pointer = pointer, + .dialect = current_dialect, + .vocabularies = vocabularies, + .base_dialect = + current_base_dialect, + .subschema = subschema, + .orphan = orphan}; subschemas.push_back(std::move(entry)); } @@ -116,100 +112,60 @@ auto walk(const std::optional &parent, ApplicatorValueTraverseSomeProperty: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Wildcard::Property); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}, - sourcemeta::core::PointerTemplate::Wildcard::Property}, - subschemas, pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType:: ApplicatorValueTraverseAnyPropertyKey: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Wildcard::Key); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Wildcard::Key}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType:: ApplicatorValueTraverseAnyItem: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Wildcard::Item); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Wildcard::Item}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType:: ApplicatorValueTraverseSomeItem: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Wildcard::Item); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}, - sourcemeta::core::PointerTemplate::Wildcard::Item}, - subschemas, pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType::ApplicatorValueTraverseParent: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.pop_back(); - walk(pointer, new_pointer, new_instance_location, {}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType::ApplicatorValueInPlaceOther: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - walk(pointer, new_pointer, instance_location, {}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType::ApplicatorValueInPlaceNegate: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Negation{}); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Negation{}}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType::ApplicatorValueInPlaceMaybe: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}}, - subschemas, pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; case sourcemeta::core::SchemaKeywordType::ApplicatorElementsTraverseItem: @@ -218,10 +174,7 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back(new_pointer.back()); - walk(pointer, new_pointer, new_instance_location, - {new_pointer.back()}, subschemas, pair.second.at(index), + walk(pointer, new_pointer, subschemas, pair.second.at(index), walker, resolver, current_dialect, current_base_dialect, type, level + 1, orphan); } @@ -235,9 +188,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - walk(pointer, new_pointer, instance_location, {}, subschemas, - pair.second.at(index), walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second.at(index), + walker, resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } @@ -249,19 +202,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{ - std::to_string(index)}); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}, - sourcemeta::core::PointerTemplate::Condition{ - std::to_string(index)}}, - subschemas, pair.second.at(index), walker, resolver, - current_dialect, current_base_dialect, type, level + 1, - orphan); + walk(pointer, new_pointer, subschemas, pair.second.at(index), + walker, resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } @@ -274,22 +217,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{ - std::to_string(index)}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Negation{}); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}, - sourcemeta::core::PointerTemplate::Condition{ - std::to_string(index)}, - sourcemeta::core::PointerTemplate::Negation{}}, - subschemas, pair.second.at(index), walker, resolver, - current_dialect, current_base_dialect, type, level + 1, - orphan); + walk(pointer, new_pointer, subschemas, pair.second.at(index), + walker, resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } @@ -302,10 +232,7 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(subpair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back(new_pointer.back()); - walk(pointer, new_pointer, new_instance_location, - {new_pointer.back()}, subschemas, subpair.second, walker, + walk(pointer, new_pointer, subschemas, subpair.second, walker, resolver, current_dialect, current_base_dialect, type, level + 1, orphan); } @@ -320,11 +247,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(subpair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back(subpair.first); - walk(pointer, new_pointer, new_instance_location, {subpair.first}, - subschemas, subpair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, subpair.second, walker, + resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } @@ -336,16 +261,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(subpair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{pair.first}); - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Condition{subpair.first}); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Condition{pair.first}, - sourcemeta::core::PointerTemplate::Condition{subpair.first}}, - subschemas, subpair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, subpair.second, walker, + resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } @@ -357,9 +275,9 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(subpair.first); - walk(pointer, new_pointer, instance_location, {}, subschemas, - subpair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, true); + walk(pointer, new_pointer, subschemas, subpair.second, walker, + resolver, current_dialect, current_base_dialect, type, + level + 1, true); } } @@ -372,23 +290,15 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back(new_pointer.back()); - walk(pointer, new_pointer, new_instance_location, - {new_pointer.back()}, subschemas, pair.second.at(index), + walk(pointer, new_pointer, subschemas, pair.second.at(index), walker, resolver, current_dialect, current_base_dialect, type, level + 1, orphan); } } else { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - auto new_instance_location{instance_location}; - new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Wildcard::Item); - walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Wildcard::Item}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; @@ -400,16 +310,15 @@ auto walk(const std::optional &parent, sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); new_pointer.emplace_back(index); - walk(pointer, new_pointer, instance_location, {}, subschemas, - pair.second.at(index), walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second.at(index), + walker, resolver, current_dialect, current_base_dialect, type, + level + 1, orphan); } } else { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - walk(pointer, new_pointer, instance_location, {}, subschemas, - pair.second, walker, resolver, current_dialect, - current_base_dialect, type, level + 1, orphan); + walk(pointer, new_pointer, subschemas, pair.second, walker, resolver, + current_dialect, current_base_dialect, type, level + 1, orphan); } break; @@ -437,31 +346,25 @@ sourcemeta::core::SchemaIterator::SchemaIterator( sourcemeta::core::dialect(schema, default_dialect)}; sourcemeta::core::Pointer pointer; - sourcemeta::core::PointerTemplate instance_location; // If the given schema declares no dialect and the user didn't // not pass a default, then there is nothing we can do. We know // the current schema is a subschema, but cannot walk any further. if (!dialect.has_value()) { - sourcemeta::core::SchemaIteratorEntry entry{ - .parent = std::nullopt, - .pointer = pointer, - .dialect = std::nullopt, - .vocabularies = {}, - .base_dialect = std::nullopt, - .subschema = schema, - // TODO: Only compute these if needed, i.e. when framing with instance - // locations - .instance_location = instance_location, - .relative_instance_location = instance_location, - .orphan = false}; + sourcemeta::core::SchemaIteratorEntry entry{.parent = std::nullopt, + .pointer = pointer, + .dialect = std::nullopt, + .vocabularies = {}, + .base_dialect = std::nullopt, + .subschema = schema, + .orphan = false}; this->subschemas.push_back(std::move(entry)); } else { const auto base_dialect{ sourcemeta::core::base_dialect(schema, resolver, dialect)}; assert(base_dialect.has_value()); - walk(std::nullopt, pointer, instance_location, instance_location, - this->subschemas, schema, walker, resolver, dialect.value(), - base_dialect.value(), SchemaWalkerType_t::Deep, 0, false); + walk(std::nullopt, pointer, this->subschemas, schema, walker, resolver, + dialect.value(), base_dialect.value(), SchemaWalkerType_t::Deep, 0, + false); } } @@ -474,13 +377,12 @@ sourcemeta::core::SchemaIteratorFlat::SchemaIteratorFlat( sourcemeta::core::dialect(schema, default_dialect)}; if (dialect.has_value()) { sourcemeta::core::Pointer pointer; - sourcemeta::core::PointerTemplate instance_location; const auto base_dialect{ sourcemeta::core::base_dialect(schema, resolver, dialect)}; assert(base_dialect.has_value()); - walk(std::nullopt, pointer, instance_location, instance_location, - this->subschemas, schema, walker, resolver, dialect.value(), - base_dialect.value(), SchemaWalkerType_t::Flat, 0, false); + walk(std::nullopt, pointer, this->subschemas, schema, walker, resolver, + dialect.value(), base_dialect.value(), SchemaWalkerType_t::Flat, 0, + false); } } @@ -513,10 +415,6 @@ sourcemeta::core::SchemaKeywordIterator::SchemaKeywordIterator( .vocabularies = vocabularies, .base_dialect = base_dialect, .subschema = entry.second, - // TODO: Only compute these if needed, i.e. when framing with instance - // locations - .instance_location = {}, - .relative_instance_location = {}, .orphan = false}; this->entries.push_back(std::move(subschema_entry)); } diff --git a/vendor/core/src/extension/alterschema/common/duplicate_allof_branches.h b/vendor/core/src/extension/alterschema/common/duplicate_allof_branches.h index 0747e016..687a8835 100644 --- a/vendor/core/src/extension/alterschema/common/duplicate_allof_branches.h +++ b/vendor/core/src/extension/alterschema/common/duplicate_allof_branches.h @@ -31,11 +31,44 @@ class DuplicateAllOfBranches final : public SchemaTransformRule { } auto transform(JSON &schema, const Result &) const -> void override { - auto collection = schema.at("allOf"); - std::sort(collection.as_array().begin(), collection.as_array().end()); - auto last = - std::unique(collection.as_array().begin(), collection.as_array().end()); - collection.erase(last, collection.as_array().end()); - schema.at("allOf").into(std::move(collection)); + this->index_mapping_.clear(); + const auto &original{schema.at("allOf")}; + + std::unordered_map, std::size_t, + HashJSON>, + EqualJSON>> + seen; + auto result{JSON::make_array()}; + + for (std::size_t index = 0; index < original.size(); ++index) { + const auto &value{original.at(index)}; + const auto match{seen.find(std::cref(value))}; + + if (match == seen.end()) { + this->index_mapping_[index] = seen.size(); + seen.emplace(std::cref(value), seen.size()); + result.push_back(value); + } else { + this->index_mapping_[index] = match->second; + } + } + + schema.assign("allOf", std::move(result)); } + + [[nodiscard]] auto rereference(const std::string &, const Pointer &, + const Pointer &target, + const Pointer ¤t) const + -> Pointer override { + const auto allof_prefix{current.concat({"allOf"})}; + const auto relative{target.resolve_from(allof_prefix)}; + const auto old_index{relative.at(0).to_index()}; + const auto new_index{this->index_mapping_.at(old_index)}; + const Pointer old_prefix{allof_prefix.concat({old_index})}; + const Pointer new_prefix{allof_prefix.concat({new_index})}; + return target.rebase(old_prefix, new_prefix); + } + +private: + mutable std::unordered_map index_mapping_; }; diff --git a/vendor/core/src/extension/alterschema/common/duplicate_anyof_branches.h b/vendor/core/src/extension/alterschema/common/duplicate_anyof_branches.h index 512f6f96..c0959146 100644 --- a/vendor/core/src/extension/alterschema/common/duplicate_anyof_branches.h +++ b/vendor/core/src/extension/alterschema/common/duplicate_anyof_branches.h @@ -31,11 +31,44 @@ class DuplicateAnyOfBranches final : public SchemaTransformRule { } auto transform(JSON &schema, const Result &) const -> void override { - auto collection = schema.at("anyOf"); - std::sort(collection.as_array().begin(), collection.as_array().end()); - auto last = - std::unique(collection.as_array().begin(), collection.as_array().end()); - collection.erase(last, collection.as_array().end()); - schema.at("anyOf").into(std::move(collection)); + this->index_mapping_.clear(); + const auto &original{schema.at("anyOf")}; + + std::unordered_map, std::size_t, + HashJSON>, + EqualJSON>> + seen; + auto result{JSON::make_array()}; + + for (std::size_t index = 0; index < original.size(); ++index) { + const auto &value{original.at(index)}; + const auto match{seen.find(std::cref(value))}; + + if (match == seen.end()) { + this->index_mapping_[index] = seen.size(); + seen.emplace(std::cref(value), seen.size()); + result.push_back(value); + } else { + this->index_mapping_[index] = match->second; + } + } + + schema.assign("anyOf", std::move(result)); } + + [[nodiscard]] auto rereference(const std::string &, const Pointer &, + const Pointer &target, + const Pointer ¤t) const + -> Pointer override { + const auto anyof_prefix{current.concat({"anyOf"})}; + const auto relative{target.resolve_from(anyof_prefix)}; + const auto old_index{relative.at(0).to_index()}; + const auto new_index{this->index_mapping_.at(old_index)}; + const Pointer old_prefix{anyof_prefix.concat({old_index})}; + const Pointer new_prefix{anyof_prefix.concat({new_index})}; + return target.rebase(old_prefix, new_prefix); + } + +private: + mutable std::unordered_map index_mapping_; }; 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 6f9b668e..4541a864 100644 --- a/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h +++ b/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h @@ -7,7 +7,7 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { [[nodiscard]] auto condition(const JSON &schema, const JSON &, const Vocabularies &vocabularies, - const SchemaFrame &, const SchemaFrame::Location &, + const SchemaFrame &frame, const SchemaFrame::Location &location, const SchemaWalker &walker, const SchemaResolver &) const -> SchemaTransformRule::Result override { ONLY_CONTINUE_IF(vocabularies.contains_any( @@ -51,6 +51,13 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { continue; } + // Skip entries that have direct references pointing to them + const auto entry_pointer{ + location.relative_pointer.concat({"allOf", index - 1})}; + if (!frame.references_to(entry_pointer).empty()) { + continue; + } + // Skip entries that define their own identity, as elevating keywords // from them could break references that target those anchors if (!this->is_anonymous(entry, vocabularies)) { @@ -111,6 +118,19 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { } } + [[nodiscard]] auto rereference(const std::string &, const Pointer &, + const Pointer &target, + const Pointer ¤t) const + -> Pointer override { + // The rule moves keywords from /allOf// to / + const auto allof_prefix{current.concat({"allOf"})}; + 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})}; + const Pointer new_prefix{current.concat({keyword})}; + return target.rebase(old_prefix, new_prefix); + } + private: // TODO: Ideally we this information from the frame out of the box [[nodiscard]] auto is_anonymous(const JSON &entry,