Skip to content
Draft
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
15 changes: 12 additions & 3 deletions src/core/jsonschema/include/sourcemeta/core/jsonschema.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,16 +422,25 @@ auto wrap(std::string_view identifier) -> JSON;
/// "items": { "type": "string" }
/// })JSON");
///
/// sourcemeta::core::SchemaFrame frame{
/// sourcemeta::core::SchemaFrame::Mode::References};
/// frame.analyse(document, sourcemeta::core::schema_walker,
/// sourcemeta::core::schema_resolver);
///
/// const auto location{frame.traverse(
/// sourcemeta::core::Pointer{"items"},
/// sourcemeta::core::SchemaFrame::LocationType::Subschema)};
///
/// const sourcemeta::core::JSON result =
/// sourcemeta::core::wrap(document, { "items" },
/// sourcemeta::core::wrap(document, frame, location.value().get(),
/// sourcemeta::core::schema_resolver);
///
/// sourcemeta::core::prettify(result, std::cerr);
/// std::cerr << "\n";
/// ```
SOURCEMETA_CORE_JSONSCHEMA_EXPORT
auto wrap(const JSON &schema, const Pointer &pointer,
const SchemaResolver &resolver, std::string_view default_dialect = "")
auto wrap(const JSON &schema, const SchemaFrame &frame,
const SchemaFrame::Location &location, const SchemaResolver &resolver)
-> JSON;

/// @ingroup jsonschema
Expand Down
40 changes: 27 additions & 13 deletions src/core/jsonschema/jsonschema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -590,24 +590,34 @@ auto sourcemeta::core::wrap(const std::string_view identifier)
return result;
}

auto sourcemeta::core::wrap(const sourcemeta::core::JSON &schema,
const sourcemeta::core::Pointer &pointer,
const sourcemeta::core::SchemaResolver &resolver,
std::string_view default_dialect)
auto sourcemeta::core::wrap(
const sourcemeta::core::JSON &schema,
const sourcemeta::core::SchemaFrame &frame,
const sourcemeta::core::SchemaFrame::Location &location,
const sourcemeta::core::SchemaResolver &resolver)
-> sourcemeta::core::JSON {
assert(location.type != SchemaFrame::LocationType::Pointer);
const auto &pointer{location.pointer};
assert(try_get(schema, pointer));
if (pointer.empty()) {
return schema;
}

auto copy = schema;
const auto effective_dialect{dialect(copy, default_dialect)};
if (!effective_dialect.empty()) {
copy.assign("$schema", JSON{effective_dialect});
} else {
throw SchemaUnknownBaseDialectError();
const auto has_internal_references{
std::any_of(frame.references().cbegin(), frame.references().cend(),
[&pointer](const auto &reference) {
return reference.first.second.starts_with(pointer);
})};

if (!has_internal_references) {
auto subschema{get(schema, pointer)};
subschema.assign("$schema", JSON{JSON::String{location.dialect}});
return subschema;
}

auto copy = schema;
copy.assign("$schema", JSON{JSON::String{location.dialect}});

auto result{JSON::make_object()};
// JSON Schema 2020-12 is the first dialect that truly supports
// cross-dialect references In practice, others do, but we can
Expand All @@ -622,13 +632,13 @@ auto sourcemeta::core::wrap(const sourcemeta::core::JSON &schema,
// other schemas whose top-level identifiers are relative URIs don't
// get affected. Otherwise, we would cause unintended base resolution.
constexpr std::string_view WRAPPER_IDENTIFIER{"__sourcemeta-core-wrap__"};
const auto maybe_id{identify(copy, resolver, default_dialect)};
const auto maybe_id{identify(copy, resolver, location.dialect)};
const auto id{maybe_id.empty() ? WRAPPER_IDENTIFIER : maybe_id};

URI uri{id};

try {
reidentify(copy, id, resolver, default_dialect);
reidentify(copy, id, resolver, location.dialect);

// Otherwise we will get an error with the `WRAPPER_IDENTIFIER`, which will
// be confusing to end users
Expand All @@ -647,9 +657,13 @@ auto sourcemeta::core::wrap(const sourcemeta::core::JSON &schema,
uri.fragment(to_string(pointer));
result.assign_assume_new("$ref", JSON{uri.recompose()});
} else {
static const JSON::String DEFS{"$defs"};
static const JSON::String SCHEMA{"schema"};
result.assign_assume_new(
"$ref",
JSON{to_uri(Pointer{"$defs", "schema"}.concat(pointer)).recompose()});
JSON{to_uri(WeakPointer{std::cref(DEFS), std::cref(SCHEMA)}.concat(
pointer))
.recompose()});
}

return result;
Expand Down
Loading