@@ -9,7 +9,7 @@ namespace {
9
9
auto embed_schema (sourcemeta::core::JSON &root,
10
10
const sourcemeta::core::Pointer &container,
11
11
const std::string &identifier,
12
- const sourcemeta::core::JSON &target) -> void {
12
+ sourcemeta::core::JSON && target) -> std::string {
13
13
auto *current{&root};
14
14
for (const auto &token : container) {
15
15
if (token.is_property ()) {
@@ -34,13 +34,15 @@ auto embed_schema(sourcemeta::core::JSON &root,
34
34
key << " /x" ;
35
35
}
36
36
37
- current->assign (key.str (), target);
37
+ current->assign (key.str (), std::move (target));
38
+ return key.str ();
38
39
}
39
40
40
41
auto is_official_metaschema_reference (const sourcemeta::core::Pointer &pointer,
41
42
const std::string &destination) -> bool {
42
- return !pointer.empty () && pointer.back ().is_property () &&
43
- pointer.back ().to_property () == " $schema" &&
43
+ assert (!pointer.empty ());
44
+ assert (pointer.back ().is_property ());
45
+ return pointer.back ().to_property () == " $schema" &&
44
46
sourcemeta::core::schema_official_resolver (destination).has_value ();
45
47
}
46
48
@@ -53,6 +55,7 @@ auto bundle_schema(sourcemeta::core::JSON &root,
53
55
const std::optional<std::string> &default_dialect,
54
56
const std::optional<std::string> &default_id,
55
57
const sourcemeta::core::SchemaFrame::Paths &paths,
58
+ const sourcemeta::core::BundleCallback &callback,
56
59
const std::size_t depth = 0 ) -> void {
57
60
// Keep in mind that the resulting frame does miss some information. For
58
61
// example, when we recurse to framing embedded schemas, we will frame them
@@ -65,12 +68,12 @@ auto bundle_schema(sourcemeta::core::JSON &root,
65
68
// We only want to frame in "wrapper" mode for the top level object
66
69
paths);
67
70
} else {
68
- // Note that we only apply the default identifier to the top-level frame
69
- frame.analyse (subschema, walker, resolver, default_dialect);
71
+ frame.analyse (subschema, walker, resolver, default_dialect, default_id);
70
72
}
71
73
72
74
// Otherwise, given recursion, we would be modifying the
73
75
// references list *while* looping on it
76
+ // TODO: How can we avoid this very expensive copy?
74
77
const auto references_copy = frame.references ();
75
78
for (const auto &[key, reference] : references_copy) {
76
79
if (frame.traverse (reference.destination ).has_value () ||
@@ -97,8 +100,8 @@ auto bundle_schema(sourcemeta::core::JSON &root,
97
100
}
98
101
99
102
assert (reference.base .has_value ());
100
- const auto identifier{reference.base .value ()};
101
- const auto remote{resolver (identifier)};
103
+ const auto & identifier{reference.base .value ()};
104
+ auto remote{resolver (identifier)};
102
105
if (!remote.has_value ()) {
103
106
if (frame.traverse (identifier).has_value ()) {
104
107
throw sourcemeta::core::SchemaReferenceError (
@@ -110,53 +113,60 @@ auto bundle_schema(sourcemeta::core::JSON &root,
110
113
identifier, " Could not resolve the reference to an external schema" );
111
114
}
112
115
113
- // Otherwise, if the target schema does not declare an inline identifier,
114
- // references to that identifier from the outer schema won't resolve.
115
- sourcemeta::core::JSON copy{remote.value ()};
116
-
117
- if (!sourcemeta::core::is_schema (copy)) {
116
+ if (!sourcemeta::core::is_schema (remote.value ())) {
118
117
throw sourcemeta::core::SchemaReferenceError (
119
118
identifier, key.second ,
120
119
" The JSON document is not a valid JSON Schema" );
121
120
}
122
121
123
- const auto dialect{sourcemeta::core::dialect (copy, default_dialect)};
124
- if (!dialect.has_value ()) {
122
+ const auto base_dialect{sourcemeta::core::base_dialect (
123
+ remote.value (), resolver, default_dialect)};
124
+ if (!base_dialect.has_value ()) {
125
125
throw sourcemeta::core::SchemaReferenceError (
126
126
identifier, key.second ,
127
127
" The JSON document is not a valid JSON Schema" );
128
128
}
129
129
130
- if (copy .is_object ()) {
130
+ if (remote. value () .is_object ()) {
131
131
// Always insert an identifier, as a schema might refer to another schema
132
132
// using another URI (i.e. due to relying on HTTP re-directions, etc)
133
- sourcemeta::core::reidentify (copy, identifier, resolver, default_dialect);
133
+ sourcemeta::core::reidentify (remote.value (), identifier,
134
+ base_dialect.value ());
134
135
}
135
136
136
- embed_schema (root, container, identifier, copy);
137
- bundle_schema (root, container, copy, frame, walker, resolver,
138
- default_dialect, default_id, paths, depth + 1 );
137
+ bundle_schema (root, container, remote.value (), frame, walker, resolver,
138
+ default_dialect, identifier, paths, callback, depth + 1 );
139
+ auto embed_key{
140
+ embed_schema (root, container, identifier, std::move (remote).value ())};
141
+
142
+ if (callback) {
143
+ const auto origin{sourcemeta::core::identify (
144
+ subschema, resolver,
145
+ sourcemeta::core::SchemaIdentificationStrategy::Strict,
146
+ default_dialect, default_id)};
147
+ callback (origin, key.second , identifier, container.concat ({embed_key}));
148
+ }
139
149
}
140
150
}
141
151
142
152
} // namespace
143
153
144
154
namespace sourcemeta ::core {
145
155
146
- auto bundle (sourcemeta::core:: JSON &schema, const SchemaWalker &walker,
156
+ auto bundle (JSON &schema, const SchemaWalker &walker,
147
157
const SchemaResolver &resolver,
148
158
const std::optional<std::string> &default_dialect,
149
159
const std::optional<std::string> &default_id,
150
160
const std::optional<Pointer> &default_container,
151
- const SchemaFrame::Paths &paths) -> void {
152
- sourcemeta::core::SchemaFrame frame {
153
- sourcemeta::core:: SchemaFrame::Mode::References};
161
+ const SchemaFrame::Paths &paths, const BundleCallback &callback)
162
+ -> void {
163
+ SchemaFrame frame{ SchemaFrame::Mode::References};
154
164
155
165
if (default_container.has_value ()) {
156
166
// This is undefined behavior
157
167
assert (!default_container.value ().empty ());
158
168
bundle_schema (schema, default_container.value (), schema, frame, walker,
159
- resolver, default_dialect, default_id, paths);
169
+ resolver, default_dialect, default_id, paths, callback );
160
170
return ;
161
171
}
162
172
@@ -167,7 +177,7 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker,
167
177
vocabularies.contains (
168
178
" https://json-schema.org/draft/2019-09/vocab/core" )) {
169
179
bundle_schema (schema, {" $defs" }, schema, frame, walker, resolver,
170
- default_dialect, default_id, paths);
180
+ default_dialect, default_id, paths, callback );
171
181
return ;
172
182
} else if (vocabularies.contains (" http://json-schema.org/draft-07/schema#" ) ||
173
183
vocabularies.contains (
@@ -179,7 +189,7 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker,
179
189
vocabularies.contains (
180
190
" http://json-schema.org/draft-04/hyper-schema#" )) {
181
191
bundle_schema (schema, {" definitions" }, schema, frame, walker, resolver,
182
- default_dialect, default_id, paths);
192
+ default_dialect, default_id, paths, callback );
183
193
return ;
184
194
} else if (vocabularies.contains (
185
195
" http://json-schema.org/draft-03/hyper-schema#" ) ||
@@ -201,19 +211,20 @@ auto bundle(sourcemeta::core::JSON &schema, const SchemaWalker &walker,
201
211
202
212
// We don't attempt to bundle on dialects where we
203
213
// don't know where to put the embedded schemas
204
- throw sourcemeta::core:: SchemaError (
214
+ throw SchemaError (
205
215
" Could not determine how to perform bundling in this dialect" );
206
216
}
207
217
208
- auto bundle (const sourcemeta::core:: JSON &schema, const SchemaWalker &walker,
218
+ auto bundle (const JSON &schema, const SchemaWalker &walker,
209
219
const SchemaResolver &resolver,
210
220
const std::optional<std::string> &default_dialect,
211
221
const std::optional<std::string> &default_id,
212
222
const std::optional<Pointer> &default_container,
213
- const SchemaFrame::Paths &paths) -> sourcemeta::core::JSON {
214
- sourcemeta::core::JSON copy = schema;
223
+ const SchemaFrame::Paths &paths, const BundleCallback &callback)
224
+ -> JSON {
225
+ JSON copy = schema;
215
226
bundle (copy, walker, resolver, default_dialect, default_id, default_container,
216
- paths);
227
+ paths, callback );
217
228
return copy;
218
229
}
219
230
0 commit comments