corvus_json_schema 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +13 -12
- data/README.md +12 -1
- data/THIRD-PARTY-NOTICES.txt +96 -0
- data/VERSIONHISTORY.md +33 -0
- data/ext/corvus_json_schema/Cargo.toml +2 -2
- data/ext/corvus_json_schema/clippy.toml +3 -4
- data/ext/corvus_json_schema/src/lib.rs +70 -20
- data/ext/corvus_json_schema/vendor/corvus-json-schema/Cargo.toml +2 -2
- data/ext/corvus_json_schema/vendor/corvus-json-schema/README.md +68 -4
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/compiler.rs +91 -97
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/document.rs +2 -2
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan/fused.rs +13 -9
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan.rs +12 -11
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval.rs +105 -73
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/formats.rs +102 -7
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/lib.rs +165 -30
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/loader.rs +19 -21
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/numbers.rs +1 -1
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/options.rs +211 -4
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/pattern.rs +635 -586
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/uri.rs +4 -4
- data/lib/corvus_json_schema/version.rb +1 -1
- data/lib/corvus_json_schema.rb +18 -3
- metadata +3 -2
|
@@ -271,12 +271,11 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
271
271
|
(r.dialect, r.vocabularies)
|
|
272
272
|
};
|
|
273
273
|
let legacy = dialect.is_legacy();
|
|
274
|
+
let grammar = crate::pattern::Grammar::of(dialect, self.options);
|
|
274
275
|
|
|
275
|
-
if legacy {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return self.compile_ref(id, target, r);
|
|
279
|
-
}
|
|
276
|
+
if legacy && let Some(Value::String(r)) = e.get("$ref") {
|
|
277
|
+
// In draft 7 and earlier, $ref replaces every sibling keyword.
|
|
278
|
+
return self.compile_ref(id, target, r);
|
|
280
279
|
}
|
|
281
280
|
|
|
282
281
|
let applicator = legacy || voc & vocab::APPLICATOR != 0;
|
|
@@ -295,15 +294,15 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
295
294
|
if let Some(Value::String(r)) = e.get("$ref") {
|
|
296
295
|
self.compile_ref(id, target, r)?;
|
|
297
296
|
}
|
|
298
|
-
if dialect >= Dialect::Draft202012
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
if dialect >= Dialect::Draft202012
|
|
298
|
+
&& let Some(Value::String(r)) = e.get("$dynamicRef")
|
|
299
|
+
{
|
|
300
|
+
self.compile_dynamic_ref(id, target, r, false)?;
|
|
302
301
|
}
|
|
303
|
-
if dialect == Dialect::Draft201909
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
if dialect == Dialect::Draft201909
|
|
303
|
+
&& let Some(Value::String(r)) = e.get("$recursiveRef")
|
|
304
|
+
{
|
|
305
|
+
self.compile_dynamic_ref(id, target, r, true)?;
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
// Applicators.
|
|
@@ -321,18 +320,18 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
321
320
|
let c = self.child(target, v, "/not");
|
|
322
321
|
self.nodes[id as usize].not = Some(c);
|
|
323
322
|
}
|
|
324
|
-
if dialect >= Dialect::Draft7
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
323
|
+
if dialect >= Dialect::Draft7
|
|
324
|
+
&& let Some(v) = e.get("if")
|
|
325
|
+
{
|
|
326
|
+
let c = self.child(target, v, "/if");
|
|
327
|
+
self.nodes[id as usize].if_ = Some(c);
|
|
328
|
+
if let Some(v) = e.get("then") {
|
|
329
|
+
let c = self.child(target, v, "/then");
|
|
330
|
+
self.nodes[id as usize].then = Some(c);
|
|
331
|
+
}
|
|
332
|
+
if let Some(v) = e.get("else") {
|
|
333
|
+
let c = self.child(target, v, "/else");
|
|
334
|
+
self.nodes[id as usize].else_ = Some(c);
|
|
336
335
|
}
|
|
337
336
|
}
|
|
338
337
|
if let Some(Value::Object(props)) = e.get("properties") {
|
|
@@ -346,11 +345,8 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
346
345
|
if let Some(Value::Object(pp)) = e.get("patternProperties") {
|
|
347
346
|
let mut list = Vec::with_capacity(pp.len());
|
|
348
347
|
for (pattern, v) in pp {
|
|
349
|
-
let compiled =
|
|
350
|
-
|
|
351
|
-
"Invalid regular expression '{pattern}' in patternProperties."
|
|
352
|
-
))
|
|
353
|
-
})?;
|
|
348
|
+
let compiled =
|
|
349
|
+
crate::pattern::compile(pattern, grammar).map_err(|e| e.of(pattern, "patternProperties"))?;
|
|
354
350
|
let c = self.child(target, v, &format!("/patternProperties/{}", escape_pointer_token(pattern)));
|
|
355
351
|
list.push(PatternProperty { pattern: compiled, node: c });
|
|
356
352
|
}
|
|
@@ -384,11 +380,11 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
384
380
|
self.nodes[id as usize].prefix_items =
|
|
385
381
|
self.child_array(target, e.get("prefixItems"), "prefixItems");
|
|
386
382
|
}
|
|
387
|
-
if let Some(v) = e.get("items")
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
383
|
+
if let Some(v) = e.get("items")
|
|
384
|
+
&& !v.is_array()
|
|
385
|
+
{
|
|
386
|
+
let c = self.child(target, v, &kw("items"));
|
|
387
|
+
self.nodes[id as usize].items = Some(c);
|
|
392
388
|
}
|
|
393
389
|
} else if let Some(v) = e.get("items") {
|
|
394
390
|
if v.is_array() {
|
|
@@ -431,10 +427,10 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
431
427
|
n.type_mask = mask;
|
|
432
428
|
n.has_type = true;
|
|
433
429
|
}
|
|
434
|
-
if dialect >= Dialect::Draft6
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
430
|
+
if dialect >= Dialect::Draft6
|
|
431
|
+
&& let Some(c) = e.get("const")
|
|
432
|
+
{
|
|
433
|
+
n.const_value = Some(c.clone());
|
|
438
434
|
}
|
|
439
435
|
if let Some(Value::Array(values)) = e.get("enum") {
|
|
440
436
|
n.enum_values = Some(values.clone());
|
|
@@ -450,18 +446,18 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
450
446
|
n.required_list = Some(list);
|
|
451
447
|
n.required = Some(dedup);
|
|
452
448
|
}
|
|
453
|
-
if dialect >= Dialect::Draft201909
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
449
|
+
if dialect >= Dialect::Draft201909
|
|
450
|
+
&& let Some(Value::Object(dr)) = e.get("dependentRequired")
|
|
451
|
+
{
|
|
452
|
+
let deps = dependencies.get_or_insert_with(Vec::new);
|
|
453
|
+
for (name, v) in dr {
|
|
454
|
+
if let Value::Array(list) = v {
|
|
455
|
+
deps.push(DependencyEntry {
|
|
456
|
+
keyword: DependencyKeyword::DependentRequired,
|
|
457
|
+
name: name.clone(),
|
|
458
|
+
required: Some(strings(list)),
|
|
459
|
+
schema: None,
|
|
460
|
+
});
|
|
465
461
|
}
|
|
466
462
|
}
|
|
467
463
|
}
|
|
@@ -473,9 +469,7 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
473
469
|
n.min_length = get_uint(e.get("minLength"));
|
|
474
470
|
n.max_length = get_uint(e.get("maxLength"));
|
|
475
471
|
if let Some(Value::String(p)) = e.get("pattern") {
|
|
476
|
-
n.pattern = Some(crate::pattern::compile(p).
|
|
477
|
-
SchemaCompilationError::new(format!("Invalid regular expression '{p}' in pattern."))
|
|
478
|
-
})?);
|
|
472
|
+
n.pattern = Some(crate::pattern::compile(p, grammar).map_err(|e| e.of(p, "pattern"))?);
|
|
479
473
|
}
|
|
480
474
|
if let Some(Value::Number(m)) = e.get("multipleOf") {
|
|
481
475
|
n.multiple_of = Some(m.clone());
|
|
@@ -518,7 +512,7 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
518
512
|
|
|
519
513
|
if let Some(Value::String(f)) = e.get("format") {
|
|
520
514
|
n.format = Some(f.clone());
|
|
521
|
-
n.format_kind = FormatKind::of(f, dialect);
|
|
515
|
+
n.format_kind = FormatKind::of(f, dialect, grammar);
|
|
522
516
|
n.assert_format = format_assert;
|
|
523
517
|
}
|
|
524
518
|
|
|
@@ -577,17 +571,17 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
577
571
|
}
|
|
578
572
|
}
|
|
579
573
|
}
|
|
580
|
-
if dialect >= Dialect::Draft201909
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
574
|
+
if dialect >= Dialect::Draft201909
|
|
575
|
+
&& let Some(Value::Object(ds)) = e.get("dependentSchemas")
|
|
576
|
+
{
|
|
577
|
+
for (name, v) in ds {
|
|
578
|
+
let c = self.child(target, v, &format!("/dependentSchemas/{}", escape_pointer_token(name)));
|
|
579
|
+
out.push(DependencyEntry {
|
|
580
|
+
keyword: DependencyKeyword::DependentSchemas,
|
|
581
|
+
name: name.clone(),
|
|
582
|
+
required: None,
|
|
583
|
+
schema: Some(c),
|
|
584
|
+
});
|
|
591
585
|
}
|
|
592
586
|
}
|
|
593
587
|
out
|
|
@@ -906,15 +900,15 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
906
900
|
|
|
907
901
|
fn compute_discriminators(&mut self) {
|
|
908
902
|
for i in 0..self.nodes.len() {
|
|
909
|
-
if let Some(branches) = self.nodes[i].one_of.clone()
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
903
|
+
if let Some(branches) = self.nodes[i].one_of.clone()
|
|
904
|
+
&& branches.len() > 1
|
|
905
|
+
{
|
|
906
|
+
self.nodes[i].one_of_discriminator = self.build_discriminator(&branches).map(Box::new);
|
|
913
907
|
}
|
|
914
|
-
if let Some(branches) = self.nodes[i].any_of.clone()
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
908
|
+
if let Some(branches) = self.nodes[i].any_of.clone()
|
|
909
|
+
&& branches.len() > 1
|
|
910
|
+
{
|
|
911
|
+
self.nodes[i].any_of_discriminator = self.build_discriminator(&branches).map(Box::new);
|
|
918
912
|
}
|
|
919
913
|
}
|
|
920
914
|
}
|
|
@@ -986,32 +980,32 @@ impl<'l, 'o> SchemaCompiler<'l, 'o> {
|
|
|
986
980
|
return Class::Wildcard;
|
|
987
981
|
};
|
|
988
982
|
let p = self.effective_node(child);
|
|
989
|
-
if let Some(c) = &p.const_value
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
983
|
+
if let Some(c) = &p.const_value
|
|
984
|
+
&& let Some(v) = primitive(c)
|
|
985
|
+
{
|
|
986
|
+
return Class::Positive(vec![v]);
|
|
993
987
|
}
|
|
994
|
-
if let Some(values) = &p.enum_values
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
988
|
+
if let Some(values) = &p.enum_values
|
|
989
|
+
&& !values.is_empty()
|
|
990
|
+
{
|
|
991
|
+
let prims: Vec<DiscriminatorValue> = values.iter().filter_map(primitive).collect();
|
|
992
|
+
if prims.len() == values.len() {
|
|
993
|
+
return Class::Positive(prims);
|
|
1000
994
|
}
|
|
1001
995
|
}
|
|
1002
|
-
if let Some(not) = p.not
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
996
|
+
if let Some(not) = p.not
|
|
997
|
+
&& p.has_type
|
|
998
|
+
&& p.type_mask == type_mask::STRING
|
|
999
|
+
{
|
|
1000
|
+
let not = self.effective_node(not);
|
|
1001
|
+
if let Some(values) = ¬.enum_values
|
|
1002
|
+
&& values.iter().all(Value::is_string)
|
|
1003
|
+
&& !not.has_type
|
|
1004
|
+
&& not.const_value.is_none()
|
|
1005
|
+
&& !not.has_string_keywords()
|
|
1006
|
+
&& !not.has_in_place_applicators()
|
|
1007
|
+
{
|
|
1008
|
+
return Class::Negative(values.iter().filter_map(primitive).collect());
|
|
1015
1009
|
}
|
|
1016
1010
|
}
|
|
1017
1011
|
Class::Wildcard
|
|
@@ -552,9 +552,9 @@ impl<'s> Parser<'s> {
|
|
|
552
552
|
return;
|
|
553
553
|
}
|
|
554
554
|
let mut kept: Vec<Node> = Vec::with_capacity(pairs.len());
|
|
555
|
-
for p in pairs.
|
|
555
|
+
for p in pairs.as_chunks::<2>().0 {
|
|
556
556
|
let name = self.key_str(&p[0]);
|
|
557
|
-
match kept.
|
|
557
|
+
match kept.as_chunks::<2>().0.iter().position(|q| self.key_str(&q[0]) == name) {
|
|
558
558
|
Some(at) => kept[2 * at + 1] = p[1],
|
|
559
559
|
None => kept.extend_from_slice(p),
|
|
560
560
|
}
|
|
@@ -26,7 +26,7 @@ use crate::eval::{json_equal, matches_type};
|
|
|
26
26
|
use crate::instance::{Instance, ObjectView, View};
|
|
27
27
|
use crate::node::*;
|
|
28
28
|
use crate::numbers::Num;
|
|
29
|
-
use crate::pattern::Pattern;
|
|
29
|
+
use crate::pattern::{Pattern, PatternBudget};
|
|
30
30
|
|
|
31
31
|
const MAX_NAMES: usize = 256;
|
|
32
32
|
const MAX_CONDITIONS: usize = 64;
|
|
@@ -132,11 +132,11 @@ enum TestKind {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
impl ValueTest {
|
|
135
|
-
fn holds<'x, I: Instance<'x>>(&self, v: I) -> bool {
|
|
135
|
+
fn holds<'x, I: Instance<'x>>(&self, v: I, budget: &mut PatternBudget) -> bool {
|
|
136
136
|
match &self.kind {
|
|
137
137
|
TestKind::Allowed(values) => values.iter().any(|a| json_equal(a, v)),
|
|
138
138
|
TestKind::Pattern { pattern, requires_string } => match v.view() {
|
|
139
|
-
View::String(s) => pattern.is_match(s),
|
|
139
|
+
View::String(s) => pattern.is_match(s, budget),
|
|
140
140
|
_ => !requires_string,
|
|
141
141
|
},
|
|
142
142
|
}
|
|
@@ -696,7 +696,7 @@ pub(super) fn try_fuse(
|
|
|
696
696
|
// A known name matching an absent pattern fails its condition whatever its value: no constant is allowed.
|
|
697
697
|
for (e, name) in names.iter().enumerate() {
|
|
698
698
|
for (condition, pattern) in &absent {
|
|
699
|
-
if pattern.is_match(name) {
|
|
699
|
+
if pattern.is_match(name, &mut p.compile_budget.lock().unwrap_or_else(|e| e.into_inner())) {
|
|
700
700
|
tests[e].push(ValueTest { condition: *condition, kind: TestKind::Allowed(Box::new([])) });
|
|
701
701
|
}
|
|
702
702
|
}
|
|
@@ -714,7 +714,7 @@ pub(super) fn try_fuse(
|
|
|
714
714
|
apps.push((c as u16, app_child(schema), Some(p.fast_target[schema as usize])));
|
|
715
715
|
}
|
|
716
716
|
for pp in b.pattern_properties.iter().flatten() {
|
|
717
|
-
if pp.pattern.is_match(name) {
|
|
717
|
+
if pp.pattern.is_match(name, &mut p.compile_budget.lock().unwrap_or_else(|e| e.into_inner())) {
|
|
718
718
|
matched = true;
|
|
719
719
|
apps.push((c as u16, app_child(pp.node), Some(p.fast_target[pp.node as usize])));
|
|
720
720
|
}
|
|
@@ -942,7 +942,7 @@ impl Evaluator<'_, '_> {
|
|
|
942
942
|
fn resolve_unknown<'x, I: Instance<'x>>(&mut self, c: &Contributor, name: &str, v: I) -> Option<bool> {
|
|
943
943
|
let mut matched = false;
|
|
944
944
|
for (pattern, child) in c.patterns.iter() {
|
|
945
|
-
if pattern.is_match(name) {
|
|
945
|
+
if pattern.is_match(name, &mut self.budget) {
|
|
946
946
|
matched = true;
|
|
947
947
|
if !self.apply(*child, v) {
|
|
948
948
|
return None;
|
|
@@ -965,7 +965,11 @@ impl Evaluator<'_, '_> {
|
|
|
965
965
|
Some(merged) => {
|
|
966
966
|
let allowed = merged.allowed(v);
|
|
967
967
|
for (t, test) in entry.tests.iter().enumerate() {
|
|
968
|
-
let holds = if merged.keyed & (1 << t) != 0 {
|
|
968
|
+
let holds = if merged.keyed & (1 << t) != 0 {
|
|
969
|
+
allowed & (1 << t) != 0
|
|
970
|
+
} else {
|
|
971
|
+
test.holds(v, &mut self.budget)
|
|
972
|
+
};
|
|
969
973
|
if !holds {
|
|
970
974
|
pass.failed |= 1 << test.condition;
|
|
971
975
|
}
|
|
@@ -973,7 +977,7 @@ impl Evaluator<'_, '_> {
|
|
|
973
977
|
}
|
|
974
978
|
None => {
|
|
975
979
|
for test in entry.tests.iter() {
|
|
976
|
-
if !test.holds(v) {
|
|
980
|
+
if !test.holds(v, &mut self.budget) {
|
|
977
981
|
pass.failed |= 1 << test.condition;
|
|
978
982
|
}
|
|
979
983
|
}
|
|
@@ -1004,7 +1008,7 @@ impl Evaluator<'_, '_> {
|
|
|
1004
1008
|
return Outcome::Done { cover, defer };
|
|
1005
1009
|
}
|
|
1006
1010
|
for (c, pattern) in f.absent.iter() {
|
|
1007
|
-
if pass.failed & (1 << c) == 0 && pattern.is_match(name) {
|
|
1011
|
+
if pass.failed & (1 << c) == 0 && pattern.is_match(name, &mut self.budget) {
|
|
1008
1012
|
pass.failed |= 1 << c;
|
|
1009
1013
|
}
|
|
1010
1014
|
}
|
|
@@ -21,7 +21,7 @@ use crate::instance::{ArrayView, Instance, Kind, ObjectView, View, str_eq};
|
|
|
21
21
|
use crate::node::*;
|
|
22
22
|
use crate::numbers::{Divisor, Num, cmp};
|
|
23
23
|
use crate::options::FormatValidator;
|
|
24
|
-
use crate::pattern::Pattern;
|
|
24
|
+
use crate::pattern::{Pattern, PatternBudget};
|
|
25
25
|
|
|
26
26
|
/// Every JSON type (a node without `type`).
|
|
27
27
|
const ANY: u8 = 0x7f;
|
|
@@ -790,7 +790,8 @@ fn plan_node(p: &Program, id: NodeId, n: &SchemaNode, target: &dyn Fn(NodeId) ->
|
|
|
790
790
|
.iter()
|
|
791
791
|
.map(|name| {
|
|
792
792
|
let patterns = n.pattern_properties.iter().flatten().enumerate();
|
|
793
|
-
|
|
793
|
+
let mut budget = p.compile_budget.lock().unwrap_or_else(|e| e.into_inner());
|
|
794
|
+
patterns.filter(|(_, pp)| pp.pattern.is_match(name, &mut budget)).map(|(j, _)| j as u16).collect()
|
|
794
795
|
})
|
|
795
796
|
.collect(),
|
|
796
797
|
additional: n.additional_properties.map(child),
|
|
@@ -1086,13 +1087,13 @@ impl Evaluator<'_, '_> {
|
|
|
1086
1087
|
#[inline(always)]
|
|
1087
1088
|
fn enter<'x, I: Instance<'x>>(&mut self, shape: Shape, b: &Body, x: I) -> bool {
|
|
1088
1089
|
match shape {
|
|
1089
|
-
Shape::Leaf => run_leaf(b, x),
|
|
1090
|
+
Shape::Leaf => run_leaf(b, x, &mut self.budget),
|
|
1090
1091
|
Shape::StringEnum => match (&b.values[0], x.view()) {
|
|
1091
1092
|
(Op::EnumStrings(values), View::String(s)) => values.contains(s),
|
|
1092
1093
|
_ => false,
|
|
1093
1094
|
},
|
|
1094
1095
|
Shape::Strings => match x.view() {
|
|
1095
|
-
View::String(s) => run_string(&b.string, s),
|
|
1096
|
+
View::String(s) => run_string(&b.string, s, &mut self.budget),
|
|
1096
1097
|
_ => true,
|
|
1097
1098
|
},
|
|
1098
1099
|
Shape::Object => match x.view() {
|
|
@@ -1219,7 +1220,7 @@ impl Evaluator<'_, '_> {
|
|
|
1219
1220
|
}
|
|
1220
1221
|
let ok = match view {
|
|
1221
1222
|
View::Number(n) => b.number.is_empty() || run_number(&b.number, &n),
|
|
1222
|
-
View::String(s) => b.string.is_empty() || run_string(&b.string, s),
|
|
1223
|
+
View::String(s) => b.string.is_empty() || run_string(&b.string, s, &mut self.budget),
|
|
1223
1224
|
View::Object(o) => b.object.as_ref().is_none_or(|plan| self.run_object(plan, o, x)),
|
|
1224
1225
|
View::Array(a) => {
|
|
1225
1226
|
b.array.as_ref().is_none_or(|plan| self.run_array::<I>(plan, a))
|
|
@@ -1423,7 +1424,7 @@ impl Evaluator<'_, '_> {
|
|
|
1423
1424
|
fn visit_pattern<'x, I: Instance<'x>>(&mut self, plan: &ObjectPlan, o: I::Object) -> bool {
|
|
1424
1425
|
let (pattern, c) = &plan.patterns[0];
|
|
1425
1426
|
for (k, v) in o.iter() {
|
|
1426
|
-
let applies = if pattern.is_match(k) { Some(*c) } else { plan.additional };
|
|
1427
|
+
let applies = if pattern.is_match(k, &mut self.budget) { Some(*c) } else { plan.additional };
|
|
1427
1428
|
if let Some(c) = applies
|
|
1428
1429
|
&& !self.run_child(c, v)
|
|
1429
1430
|
{
|
|
@@ -1455,7 +1456,7 @@ impl Evaluator<'_, '_> {
|
|
|
1455
1456
|
}
|
|
1456
1457
|
} else {
|
|
1457
1458
|
for (pattern, c) in plan.patterns.iter() {
|
|
1458
|
-
if pattern.is_match(k) {
|
|
1459
|
+
if pattern.is_match(k, &mut self.budget) {
|
|
1459
1460
|
matched = true;
|
|
1460
1461
|
if !self.run_child(*c, v) {
|
|
1461
1462
|
return None;
|
|
@@ -1557,7 +1558,7 @@ fn all_of_type<'x, I: Instance<'x>>(mut items: impl Iterator<Item = I>, types: u
|
|
|
1557
1558
|
|
|
1558
1559
|
/// A leaf's keywords: its value constraints, then those for the instance's type.
|
|
1559
1560
|
#[inline]
|
|
1560
|
-
fn run_leaf<'x, I: Instance<'x>>(b: &Body, x: I) -> bool {
|
|
1561
|
+
fn run_leaf<'x, I: Instance<'x>>(b: &Body, x: I, budget: &mut PatternBudget) -> bool {
|
|
1561
1562
|
for op in b.values.iter() {
|
|
1562
1563
|
let ok = match op {
|
|
1563
1564
|
Op::Const(c) => json_equal(x, c),
|
|
@@ -1571,7 +1572,7 @@ fn run_leaf<'x, I: Instance<'x>>(b: &Body, x: I) -> bool {
|
|
|
1571
1572
|
}
|
|
1572
1573
|
match x.view() {
|
|
1573
1574
|
View::Number(n) => b.number.is_empty() || run_number(&b.number, &n),
|
|
1574
|
-
View::String(s) => b.string.is_empty() || run_string(&b.string, s),
|
|
1575
|
+
View::String(s) => b.string.is_empty() || run_string(&b.string, s, budget),
|
|
1575
1576
|
_ => true,
|
|
1576
1577
|
}
|
|
1577
1578
|
}
|
|
@@ -1590,10 +1591,10 @@ fn run_number(ops: &[NumberOp], n: &Number) -> bool {
|
|
|
1590
1591
|
}
|
|
1591
1592
|
|
|
1592
1593
|
#[inline(never)]
|
|
1593
|
-
fn run_string(ops: &[StringOp], s: &str) -> bool {
|
|
1594
|
+
fn run_string(ops: &[StringOp], s: &str, budget: &mut PatternBudget) -> bool {
|
|
1594
1595
|
ops.iter().all(|op| match op {
|
|
1595
1596
|
StringOp::Length { min, max } => length_ok(s, *min, *max),
|
|
1596
|
-
StringOp::Pattern(p) => p.is_match(s),
|
|
1597
|
+
StringOp::Pattern(p) => p.is_match(s, budget),
|
|
1597
1598
|
StringOp::Format(f) => f.string(s),
|
|
1598
1599
|
StringOp::Content(kind) => content_ok(s, *kind),
|
|
1599
1600
|
})
|