Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/alterschema/canonicalizer/type_array_to_any_of.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,32 @@ class TypeArrayToAnyOf final : public SchemaTransformRule {
}

auto transform(JSON &schema, const Result &) const -> void override {
this->keyword_branch_index_.clear();
auto disjunctors{sourcemeta::core::JSON::make_array()};
std::size_t branch_index{0};
for (const auto &type : schema.at("type").as_array()) {
auto branch{sourcemeta::core::JSON::make_object()};
branch.assign("type", type);
const auto current_type_set{parse_schema_type(type)};
for (const auto &[keyword, instances] : this->keyword_instances_) {
if ((instances & current_type_set).any()) {
branch.assign(keyword, schema.at(keyword));
if (!this->keyword_branch_index_.contains(keyword)) {
this->keyword_branch_index_[keyword] = branch_index;
}
}
}

disjunctors.push_back(std::move(branch));
branch_index++;
}

for (const auto &[keyword, instances] : this->keyword_instances_) {
schema.erase(keyword);
}

static const std::string anyof_keyword{"anyOf"};
static const std::string allof_keyword{"allOf"};
if (schema.defines("anyOf")) {
auto first_branch{sourcemeta::core::JSON::make_object()};
first_branch.assign("anyOf", schema.at("anyOf"));
Expand All @@ -72,23 +80,48 @@ class TypeArrayToAnyOf final : public SchemaTransformRule {
schema.erase("anyOf");

if (schema.defines("allOf")) {
const auto allof_index{schema.at("allOf").size() + 1};
schema.at("allOf").push_back(std::move(first_branch));
schema.at("allOf").push_back(std::move(second_branch));
schema.erase("type");
this->disjunctors_prefix_ = {allof_keyword, allof_index, anyof_keyword};
} else {
auto allof_wrapper{sourcemeta::core::JSON::make_array()};
allof_wrapper.push_back(std::move(first_branch));
allof_wrapper.push_back(std::move(second_branch));
schema.at("type").into(std::move(allof_wrapper));
schema.rename("type", "allOf");
this->disjunctors_prefix_ = {allof_keyword, 1, anyof_keyword};
}
} else {
schema.at("type").into(std::move(disjunctors));
schema.rename("type", "anyOf");
this->disjunctors_prefix_ = {anyof_keyword};
}
}

[[nodiscard]] auto rereference(const std::string_view reference,
const Pointer &origin, const Pointer &target,
const Pointer &current) const
-> Pointer override {
const auto relative{target.resolve_from(current)};
assert(!relative.empty() && relative.at(0).is_property());
const auto &keyword{relative.at(0).to_property()};
const auto match{this->keyword_branch_index_.find(keyword)};
if (match == this->keyword_branch_index_.end()) {
return SchemaTransformRule::rereference(reference, origin, target,
current);
}

const Pointer old_prefix{current.concat({keyword})};
const Pointer new_prefix{current.concat(this->disjunctors_prefix_)
.concat({match->second, keyword})};
return target.rebase(old_prefix, new_prefix);
}

private:
mutable std::unordered_map<std::string, sourcemeta::core::JSON::TypeSet>
keyword_instances_;
mutable std::unordered_map<std::string, std::size_t> keyword_branch_index_;
mutable Pointer disjunctors_prefix_;
};
15 changes: 12 additions & 3 deletions src/alterschema/transformer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ auto SchemaTransformer::apply(core::JSON &schema,
core::Pointer origin;
core::JSON::String original;
core::JSON::String destination;
core::JSON::String fragment;
core::Pointer target_pointer;
std::size_t target_relative_pointer;
};
Expand Down Expand Up @@ -266,8 +267,9 @@ auto SchemaTransformer::apply(core::JSON &schema,
potentially_broken_references.push_back(
{core::to_pointer(reference.first.second),
core::JSON::String{reference.second.original},
reference.second.destination, core::to_pointer(target.pointer),
target.relative_pointer});
reference.second.destination,
core::JSON::String{reference.second.fragment.value()},
core::to_pointer(target.pointer), target.relative_pointer});
}

rule->transform(current, outcome);
Expand Down Expand Up @@ -305,12 +307,19 @@ auto SchemaTransformer::apply(core::JSON &schema,
continue;
}

const auto new_fragment{rule->rereference(
const auto new_relative{rule->rereference(
saved_reference.destination, saved_reference.origin,
saved_reference.target_pointer.slice(
saved_reference.target_relative_pointer),
entry_pointer.slice(
new_location.value().get().relative_pointer))};
const auto new_fragment{
saved_reference.fragment ==
core::to_string(saved_reference.target_pointer)
? saved_reference.target_pointer
.slice(0, saved_reference.target_relative_pointer)
.concat(new_relative)
: new_relative};

core::URI original{saved_reference.original};
original.fragment(core::to_string(new_fragment));
Expand Down
Loading
Loading