Skip to content

Commit c4f9fd1

Browse files
committed
Extend SchemaMapResolver::add with an optional callback (#1631)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent ca03804 commit c4f9fd1

File tree

5 files changed

+112
-56
lines changed

5 files changed

+112
-56
lines changed

src/core/jsonschema/frame.cc

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ enum class AnchorType : std::uint8_t { Static, Dynamic, All };
1414

1515
static auto find_anchors(const sourcemeta::core::JSON &schema,
1616
const sourcemeta::core::Vocabularies &vocabularies)
17-
-> std::map<std::string, AnchorType> {
18-
std::map<std::string, AnchorType> result;
17+
-> std::map<sourcemeta::core::JSON::String, AnchorType> {
18+
std::map<sourcemeta::core::JSON::String, AnchorType> result;
1919

2020
// 2020-12
2121
if (schema.is_object() &&
@@ -74,7 +74,8 @@ static auto find_anchors(const sourcemeta::core::JSON &schema,
7474
const sourcemeta::core::URI identifier(schema.at("$id").to_string());
7575
if (identifier.is_fragment_only()) {
7676
result.insert(
77-
{std::string{identifier.fragment().value()}, AnchorType::Static});
77+
{sourcemeta::core::JSON::String{identifier.fragment().value()},
78+
AnchorType::Static});
7879
}
7980
}
8081
}
@@ -88,7 +89,8 @@ static auto find_anchors(const sourcemeta::core::JSON &schema,
8889
const sourcemeta::core::URI identifier(schema.at("id").to_string());
8990
if (identifier.is_fragment_only()) {
9091
result.insert(
91-
{std::string{identifier.fragment().value()}, AnchorType::Static});
92+
{sourcemeta::core::JSON::String{identifier.fragment().value()},
93+
AnchorType::Static});
9294
}
9395
}
9496
}
@@ -97,10 +99,12 @@ static auto find_anchors(const sourcemeta::core::JSON &schema,
9799
}
98100

99101
static auto find_nearest_bases(
100-
const std::map<sourcemeta::core::Pointer, std::vector<std::string>> &bases,
102+
const std::map<sourcemeta::core::Pointer,
103+
std::vector<sourcemeta::core::JSON::String>> &bases,
101104
const sourcemeta::core::Pointer &pointer,
102-
const std::optional<std::string> &default_base)
103-
-> std::pair<std::vector<std::string>, sourcemeta::core::Pointer> {
105+
const std::optional<sourcemeta::core::JSON::String> &default_base)
106+
-> std::pair<std::vector<sourcemeta::core::JSON::String>,
107+
sourcemeta::core::Pointer> {
104108
for (const auto &subpointer : sourcemeta::core::SubPointerWalker{pointer}) {
105109
if (bases.contains(subpointer)) {
106110
return {bases.at(subpointer), subpointer};
@@ -115,10 +119,14 @@ static auto find_nearest_bases(
115119
}
116120

117121
static auto find_every_base(
118-
const std::map<sourcemeta::core::Pointer, std::vector<std::string>> &bases,
122+
const std::map<sourcemeta::core::Pointer,
123+
std::vector<sourcemeta::core::JSON::String>> &bases,
119124
const sourcemeta::core::Pointer &pointer)
120-
-> std::vector<std::pair<std::string, sourcemeta::core::Pointer>> {
121-
std::vector<std::pair<std::string, sourcemeta::core::Pointer>> result;
125+
-> std::vector<
126+
std::pair<sourcemeta::core::JSON::String, sourcemeta::core::Pointer>> {
127+
std::vector<
128+
std::pair<sourcemeta::core::JSON::String, sourcemeta::core::Pointer>>
129+
result;
122130

123131
for (const auto &subpointer : sourcemeta::core::SubPointerWalker{pointer}) {
124132
if (bases.contains(subpointer)) {
@@ -137,8 +145,8 @@ static auto find_every_base(
137145
return result;
138146
}
139147

140-
static auto ref_overrides_adjacent_keywords(const std::string &base_dialect)
141-
-> bool {
148+
static auto ref_overrides_adjacent_keywords(
149+
const sourcemeta::core::JSON::String &base_dialect) -> bool {
142150
// In older drafts, the presence of `$ref` would override any sibling
143151
// keywords
144152
// See
@@ -153,7 +161,9 @@ static auto ref_overrides_adjacent_keywords(const std::string &base_dialect)
153161
base_dialect == "http://json-schema.org/draft-03/hyper-schema#";
154162
}
155163

156-
static auto supports_id_anchors(const std::string &base_dialect) -> bool {
164+
static auto
165+
supports_id_anchors(const sourcemeta::core::JSON::String &base_dialect)
166+
-> bool {
157167
return base_dialect == "http://json-schema.org/draft-07/schema#" ||
158168
base_dialect == "http://json-schema.org/draft-07/hyper-schema#" ||
159169
base_dialect == "http://json-schema.org/draft-06/schema#" ||
@@ -163,10 +173,10 @@ static auto supports_id_anchors(const std::string &base_dialect) -> bool {
163173
}
164174

165175
static auto fragment_string(const sourcemeta::core::URI &uri)
166-
-> std::optional<std::string> {
176+
-> std::optional<sourcemeta::core::JSON::String> {
167177
const auto fragment{uri.fragment()};
168178
if (fragment.has_value()) {
169-
return std::string{fragment.value()};
179+
return sourcemeta::core::JSON::String{fragment.value()};
170180
}
171181

172182
return std::nullopt;
@@ -177,11 +187,13 @@ store(sourcemeta::core::SchemaFrame::Locations &frame,
177187
sourcemeta::core::SchemaFrame::Instances &instances,
178188
const sourcemeta::core::SchemaReferenceType type,
179189
const sourcemeta::core::SchemaFrame::LocationType entry_type,
180-
const std::string &uri, const std::optional<std::string> &root_id,
181-
const std::string &base_id,
190+
const sourcemeta::core::JSON::String &uri,
191+
const std::optional<sourcemeta::core::JSON::String> &root_id,
192+
const sourcemeta::core::JSON::String &base_id,
182193
const sourcemeta::core::Pointer &pointer_from_root,
183194
const sourcemeta::core::Pointer &pointer_from_base,
184-
const std::string &dialect, const std::string &base_dialect,
195+
const sourcemeta::core::JSON::String &dialect,
196+
const sourcemeta::core::JSON::String &base_dialect,
185197
const std::vector<sourcemeta::core::PointerTemplate> &instance_locations,
186198
const std::optional<sourcemeta::core::Pointer> &parent,
187199
const bool ignore_if_present = false) -> void {
@@ -206,7 +218,7 @@ store(sourcemeta::core::SchemaFrame::Locations &frame,
206218

207219
struct InternalEntry {
208220
const sourcemeta::core::SchemaIteratorEntry common;
209-
const std::optional<std::string> id;
221+
const std::optional<sourcemeta::core::JSON::String> id;
210222
};
211223

212224
static auto traverse_origin_instance_locations(
@@ -510,27 +522,27 @@ auto operator<<(std::ostream &stream, const SchemaFrame &frame)
510522

511523
auto SchemaFrame::analyse(const JSON &schema, const SchemaWalker &walker,
512524
const SchemaResolver &resolver,
513-
const std::optional<std::string> &default_dialect,
514-
const std::optional<std::string> &default_id)
525+
const std::optional<JSON::String> &default_dialect,
526+
const std::optional<JSON::String> &default_id)
515527
-> void {
516528
std::vector<InternalEntry> subschema_entries;
517529
std::map<Pointer, CacheSubschema> subschemas;
518-
std::map<sourcemeta::core::Pointer, std::vector<std::string>> base_uris;
519-
std::map<sourcemeta::core::Pointer, std::vector<std::string>> base_dialects;
530+
std::map<sourcemeta::core::Pointer, std::vector<JSON::String>> base_uris;
531+
std::map<sourcemeta::core::Pointer, std::vector<JSON::String>> base_dialects;
520532

521-
const std::optional<std::string> root_base_dialect{
533+
const std::optional<JSON::String> root_base_dialect{
522534
sourcemeta::core::base_dialect(schema, resolver, default_dialect)};
523535
if (!root_base_dialect.has_value()) {
524536
throw SchemaError("Could not determine the base dialect of the schema");
525537
}
526538

527-
std::optional<std::string> root_id{sourcemeta::core::identify(
539+
std::optional<JSON::String> root_id{sourcemeta::core::identify(
528540
schema, root_base_dialect.value(), default_id)};
529541
if (root_id.has_value()) {
530542
root_id = URI{root_id.value()}.canonicalize().recompose();
531543
}
532544

533-
const std::optional<std::string> root_dialect{
545+
const std::optional<JSON::String> root_dialect{
534546
sourcemeta::core::dialect(schema, default_dialect)};
535547
assert(root_dialect.has_value());
536548

@@ -568,7 +580,7 @@ auto SchemaFrame::analyse(const JSON &schema, const SchemaWalker &walker,
568580
assert(entry.base_dialect.has_value());
569581

570582
// Schema identifier
571-
std::optional<std::string> id{sourcemeta::core::identify(
583+
std::optional<JSON::String> id{sourcemeta::core::identify(
572584
entry.subschema.get(), entry.base_dialect.value(),
573585
entry.pointer.empty() ? default_id : std::nullopt)};
574586

@@ -611,7 +623,7 @@ auto SchemaFrame::analyse(const JSON &schema, const SchemaWalker &walker,
611623

612624
const bool maybe_relative_is_absolute{maybe_relative.is_absolute()};
613625
maybe_relative.try_resolve_from(base).canonicalize();
614-
const std::string new_id{maybe_relative.recompose()};
626+
const JSON::String new_id{maybe_relative.recompose()};
615627

616628
if (!maybe_relative_is_absolute ||
617629
!this->locations_.contains(
@@ -667,7 +679,7 @@ auto SchemaFrame::analyse(const JSON &schema, const SchemaWalker &walker,
667679
}
668680

669681
metaschema.canonicalize();
670-
const std::string destination{metaschema.recompose()};
682+
const JSON::String destination{metaschema.recompose()};
671683
assert(entry.common.subschema.get().defines("$schema"));
672684
this->references_.insert_or_assign(
673685
{SchemaReferenceType::Static,
@@ -971,17 +983,17 @@ auto SchemaFrame::analyse(const JSON &schema, const SchemaWalker &walker,
971983

972984
if (standalone) {
973985
// Find all dynamic anchors
974-
std::map<std::string, std::vector<std::string>> dynamic_anchors;
986+
std::map<JSON::String, std::vector<JSON::String>> dynamic_anchors;
975987
for (const auto &entry : this->locations_) {
976988
if (entry.first.first != SchemaReferenceType::Dynamic ||
977989
entry.second.type != SchemaFrame::LocationType::Anchor) {
978990
continue;
979991
}
980992

981993
const URI anchor_uri{entry.first.second};
982-
const std::string fragment{anchor_uri.fragment().value_or("")};
994+
const JSON::String fragment{anchor_uri.fragment().value_or("")};
983995
if (!dynamic_anchors.contains(fragment)) {
984-
dynamic_anchors.emplace(fragment, std::vector<std::string>{});
996+
dynamic_anchors.emplace(fragment, std::vector<JSON::String>{});
985997
}
986998

987999
dynamic_anchors[fragment].push_back(entry.first.second);
@@ -1072,7 +1084,7 @@ auto SchemaFrame::vocabularies(const Location &location,
10721084

10731085
auto SchemaFrame::uri(const Location &location,
10741086
const Pointer &relative_schema_location) const
1075-
-> std::string {
1087+
-> JSON::String {
10761088
return to_uri(location.relative_pointer.concat(relative_schema_location),
10771089
location.base)
10781090
.recompose();
@@ -1094,7 +1106,7 @@ auto SchemaFrame::traverse(const Location &location,
10941106
return dynamic_match->second;
10951107
}
10961108

1097-
auto SchemaFrame::traverse(const std::string &uri) const
1109+
auto SchemaFrame::traverse(const JSON::String &uri) const
10981110
-> std::optional<std::reference_wrapper<const Location>> {
10991111
const auto static_result{
11001112
this->locations_.find({SchemaReferenceType::Static, uri})};

src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include <map> // std::map
1818
#include <optional> // std::optional
1919
#include <ostream> // std::ostream
20-
#include <string> // std::string
2120
#include <tuple> // std::tuple
2221
#include <unordered_set> // std::set
2322
#include <utility> // std::pair
@@ -115,11 +114,11 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
115114

116115
/// A single entry in a JSON Schema reference map
117116
struct ReferencesEntry {
118-
std::string destination;
117+
JSON::String destination;
119118
// TODO: This string can be a `string_view` over the `destination`
120-
std::optional<std::string> base;
119+
std::optional<JSON::String> base;
121120
// TODO: This string can be a `string_view` over the `destination`
122-
std::optional<std::string> fragment;
121+
std::optional<JSON::String> fragment;
123122
};
124123

125124
/// A JSON Schema reference map is a mapping of a JSON Pointer
@@ -156,14 +155,14 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
156155
// TODO: Turn this into a weak pointer
157156
std::optional<Pointer> parent;
158157
LocationType type;
159-
std::optional<std::string> root;
160-
std::string base;
158+
std::optional<JSON::String> root;
159+
JSON::String base;
161160
// TODO: Turn this into a weak pointer
162161
Pointer pointer;
163162
// TODO: Turn this into a weak pointer
164163
Pointer relative_pointer;
165-
std::string dialect;
166-
std::string base_dialect;
164+
JSON::String dialect;
165+
JSON::String base_dialect;
167166
};
168167

169168
// TODO: Indexing locations by reference type is wrong. We can index by just
@@ -172,7 +171,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
172171
/// JSON Pointers within the schema, and subschemas dialects. We call it
173172
/// reference frame as this mapping is essential for resolving references.
174173
using Locations =
175-
std::map<std::pair<SchemaReferenceType, std::string>, Location>;
174+
std::map<std::pair<SchemaReferenceType, JSON::String>, Location>;
176175

177176
// TODO: Turn the mapped value into a proper set
178177
/// A set of unresolved instance locations
@@ -182,11 +181,11 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
182181
auto to_json() const -> JSON;
183182

184183
/// Analyse a given schema
185-
auto analyse(const JSON &schema, const SchemaWalker &walker,
186-
const SchemaResolver &resolver,
187-
const std::optional<std::string> &default_dialect = std::nullopt,
188-
const std::optional<std::string> &default_id = std::nullopt)
189-
-> void;
184+
auto
185+
analyse(const JSON &schema, const SchemaWalker &walker,
186+
const SchemaResolver &resolver,
187+
const std::optional<JSON::String> &default_dialect = std::nullopt,
188+
const std::optional<JSON::String> &default_id = std::nullopt) -> void;
190189

191190
/// Access the analysed schema locations
192191
auto locations() const noexcept -> const Locations &;
@@ -201,15 +200,15 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
201200
/// Get the URI associated with a location entry
202201
auto uri(const Location &location,
203202
const Pointer &relative_schema_location = empty_pointer) const
204-
-> std::string;
203+
-> JSON::String;
205204

206205
/// Get the location associated by traversing a pointer from another location
207206
auto traverse(const Location &location,
208207
const Pointer &relative_schema_location) const
209208
-> const Location &;
210209

211210
/// Get the location associated with a given URI
212-
auto traverse(const std::string &uri) const
211+
auto traverse(const JSON::String &uri) const
213212
-> std::optional<std::reference_wrapper<const Location>>;
214213

215214
/// Try to dereference a reference location into its destination location
@@ -265,7 +264,7 @@ struct SchemaUnevaluatedEntry {
265264

266265
/// @ingroup jsonschema
267266
/// The flattened set of unevaluated cases in the schema by absolute URI
268-
using SchemaUnevaluatedEntries = std::map<std::string, SchemaUnevaluatedEntry>;
267+
using SchemaUnevaluatedEntries = std::map<JSON::String, SchemaUnevaluatedEntry>;
269268

270269
/// @ingroup jsonschema
271270
///

src/core/jsonschema/include/sourcemeta/core/jsonschema_resolver.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaMapResolver {
5353
/// schema was imported into the resolver
5454
auto add(const JSON &schema,
5555
const std::optional<std::string> &default_dialect = std::nullopt,
56-
const std::optional<std::string> &default_id = std::nullopt) -> bool;
56+
const std::optional<std::string> &default_id = std::nullopt,
57+
const std::function<void(const JSON::String &)> &callback = nullptr)
58+
-> bool;
5759

5860
/// Attempt to resolve a schema
5961
auto operator()(std::string_view identifier) const -> std::optional<JSON>;

src/core/jsonschema/resolver.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ SchemaMapResolver::SchemaMapResolver() {}
1212
SchemaMapResolver::SchemaMapResolver(const SchemaResolver &resolver)
1313
: default_resolver{resolver} {}
1414

15-
auto SchemaMapResolver::add(const JSON &schema,
16-
const std::optional<std::string> &default_dialect,
17-
const std::optional<std::string> &default_id)
18-
-> bool {
15+
auto SchemaMapResolver::add(
16+
const JSON &schema, const std::optional<std::string> &default_dialect,
17+
const std::optional<std::string> &default_id,
18+
const std::function<void(const JSON::String &)> &callback) -> bool {
1919
assert(sourcemeta::core::is_schema(schema));
2020

2121
// Registering the top-level schema is not enough. We need to check
@@ -60,6 +60,10 @@ auto SchemaMapResolver::add(const JSON &schema,
6060
throw SchemaError(error.str());
6161
}
6262

63+
if (callback) {
64+
callback(key.second);
65+
}
66+
6367
added_any_schema = true;
6468
}
6569

0 commit comments

Comments
 (0)