Skip to main content

shape/
validate.rs

1//! The walk: a node against a value, producing in place.
2
3use crate::context::{Context, PathPart, State, Update, UpdateErr};
4use crate::error::*;
5use crate::node::{Kind, ListMode, Node, Validator};
6use crate::stringify::stringify_node;
7use crate::value::{is_integer, Map, Value};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11/// A cursor onto the value under validation: mutable when producing, shared
12/// when only a verdict is wanted. Writes through a shared cursor are dropped,
13/// so one walk serves both. A container's cursor is typed by its map or
14/// vector once its kind is known.
15pub(crate) enum Cur<'a, T = Value> {
16    Mut(&'a mut T),
17    Ref(&'a T),
18}
19
20impl<'a, T> Cur<'a, T> {
21    fn get(&self) -> &T {
22        match self {
23            Cur::Mut(v) => v,
24            Cur::Ref(v) => v,
25        }
26    }
27
28    fn set(&mut self, nv: T) {
29        if let Cur::Mut(v) = self {
30            **v = nv;
31        }
32    }
33
34    fn reborrow(&mut self) -> Cur<'_, T> {
35        match self {
36            Cur::Mut(v) => Cur::Mut(v),
37            Cur::Ref(v) => Cur::Ref(v),
38        }
39    }
40}
41
42/// What a walk carries besides the value.
43pub(crate) struct Walk<'c> {
44    pub ctx: &'c mut Context,
45    /// The schema's own definitions, borrowed for the walk.
46    pub defs: &'c HashMap<String, Arc<Node>>,
47    pub is_match: bool,
48    /// The path from the root, the current key last.
49    pub path: Vec<PathPart>,
50    /// Whether the path is kept: a terse walk of a schema with no
51    /// validators has nothing that reads it.
52    pub paths: bool,
53}
54
55impl<'c> Walk<'c> {
56    #[inline]
57    fn push(&mut self, part: PathPart) {
58        if self.paths {
59            self.path.push(part);
60        }
61    }
62
63    #[inline]
64    fn pop(&mut self) {
65        if self.paths {
66            self.path.pop();
67        }
68    }
69}
70
71/// The digits of an index, written into a stack buffer: an element's key,
72/// without an allocation per element.
73fn index_key(i: usize, buf: &mut [u8; 20]) -> &str {
74    let mut n = i;
75    let mut pos = buf.len();
76    loop {
77        pos -= 1;
78        buf[pos] = b'0' + (n % 10) as u8;
79        n /= 10;
80        if n == 0 {
81            break;
82        }
83    }
84    // SAFETY: the buffer holds ASCII digits only.
85    unsafe { std::str::from_utf8_unchecked(&buf[pos..]) }
86}
87
88pub(crate) fn required_mark_for(k: Kind) -> i64 {
89    match k {
90        Kind::Object => MARK_OBJECT_REQUIRED,
91        Kind::Array => MARK_ARRAY_REQUIRED,
92        _ => MARK_SCALAR_REQUIRED,
93    }
94}
95
96pub(crate) fn type_mark_for(k: Kind) -> i64 {
97    match k {
98        Kind::Object => MARK_OBJECT_TYPE,
99        Kind::Array => MARK_ARRAY_TYPE,
100        Kind::Check => MARK_CHECK_TYPE,
101        _ => MARK_SCALAR_TYPE,
102    }
103}
104
105/// Validate a node against the value at `cur`, writing the produced value
106/// through it. Returns false when the slot should be left out of the
107/// produced container: nothing was there and nothing was injected.
108pub(crate) fn validate_node(
109    n: &Node,
110    cur: Cur<'_>,
111    key: &str,
112    parent_is_array: bool,
113    w: &mut Walk<'_>,
114    verr: &mut ValidationError,
115) -> bool {
116    validate_node_with(
117        n,
118        &n.befores,
119        &n.afters,
120        n.silent,
121        cur,
122        key,
123        parent_is_array,
124        w,
125        verr,
126    )
127}
128
129/// `validate_node` with the checks and the silence given apart from the
130/// node: an Ignore lifts the silence of the node it probes, and a Catch or
131/// Transform runs the node with the checks it took inside.
132#[allow(clippy::too_many_arguments)]
133pub(crate) fn validate_node_with(
134    n: &Node,
135    befores: &[Validator],
136    afters: &[Validator],
137    silent: bool,
138    mut cur: Cur<'_>,
139    key: &str,
140    parent_is_array: bool,
141    w: &mut Walk<'_>,
142    verr: &mut ValidationError,
143) -> bool {
144    let absent = cur.get().is_undefined();
145    // A before may replace the value or the node; a replacement value under a
146    // shared cursor lives here.
147    let mut over: Option<Value> = None;
148    let mut node_over: Option<Arc<Node>> = None;
149    let mut done = false;
150
151    for b in befores {
152        let cur_node: &Node = node_over.as_deref().unwrap_or(n);
153        let mut update = Update::default();
154        let ok = {
155            let value: &Value = over.as_ref().unwrap_or_else(|| cur.get());
156            let mut state = State {
157                path_arr: &w.path,
158                key,
159                value,
160                node: cur_node,
161                parent_is_array,
162                is_match: w.is_match,
163                ctx: w.ctx,
164                absent,
165                check_name: &b.name,
166                defs: w.defs,
167            };
168            (b.func)(&mut state, &mut update)
169        };
170        if let Some(v) = update.val.take() {
171            match &mut cur {
172                Cur::Mut(_) => cur.set(v),
173                Cur::Ref(_) => over = Some(v),
174            }
175        }
176        if let Some(nn) = update.node.take() {
177            node_over = Some(nn);
178        }
179        let cur_node: &Node = node_over.as_deref().unwrap_or(n);
180        let cur_silent = if node_over.is_some() {
181            cur_node.silent
182        } else {
183            silent
184        };
185        let value_now: &Value = over.as_ref().unwrap_or_else(|| cur.get());
186        if !ok && absent_skips(cur_node, value_now, absent, &update) {
187            continue;
188        }
189        if !ok {
190            let at = at_of(w, key, cur_node, value_now, parent_is_array, &b.name);
191            emit_update_errors(&at, cur_node, cur_silent, &update, verr);
192        }
193        if !ok || update.done {
194            done = true;
195        }
196    }
197
198    let n: &Node = node_over.as_deref().unwrap_or(n);
199    let silent = if node_over.is_some() {
200        n.silent
201    } else {
202        silent
203    };
204    let mut kept = true;
205    if !done {
206        let inner = match over.as_mut() {
207            Some(v) => Cur::Mut(v),
208            None => cur.reborrow(),
209        };
210        kept = validate_structure(n, silent, inner, key, parent_is_array, absent, w, verr);
211    }
212
213    // Afters run whatever the structural check reported.
214    for a in afters {
215        let mut update = Update::default();
216        let ok = {
217            let value: &Value = over.as_ref().unwrap_or_else(|| cur.get());
218            let mut state = State {
219                path_arr: &w.path,
220                key,
221                value,
222                node: n,
223                parent_is_array,
224                is_match: w.is_match,
225                ctx: w.ctx,
226                absent,
227                check_name: &a.name,
228                defs: w.defs,
229            };
230            (a.func)(&mut state, &mut update)
231        };
232        if let Some(v) = update.val.take() {
233            kept = true;
234            match &mut cur {
235                Cur::Mut(_) => cur.set(v),
236                Cur::Ref(_) => over = Some(v),
237            }
238        }
239        let value_now: &Value = over.as_ref().unwrap_or_else(|| cur.get());
240        if !ok && absent_skips(n, value_now, absent, &update) {
241            continue;
242        }
243        if !ok {
244            let at = at_of(w, key, n, value_now, parent_is_array, &a.name);
245            emit_update_errors(&at, n, silent, &update, verr);
246        }
247    }
248
249    kept
250}
251
252/// A failed check against an absent value on a node that does not require
253/// one raises nothing, unless the check insisted with `done`.
254fn absent_skips(n: &Node, value: &Value, absent: bool, update: &Update) -> bool {
255    absent && value.is_undefined() && (n.skippable || !n.required) && !update.done
256}
257
258/// The location of an error. A value renders as "undefined" when it is
259/// undefined at the time of the error, as the canonical `s.val` does: an
260/// injected default is the value a later check reports.
261pub(crate) fn at_of<'a>(
262    w: &'a Walk<'_>,
263    key: &'a str,
264    n: &Node,
265    value: &'a Value,
266    parent_is_array: bool,
267    check: &'a str,
268) -> At<'a> {
269    At {
270        path: &w.path,
271        key,
272        kind: n.kind,
273        value,
274        parent_arr: parent_is_array,
275        absent: value.is_undefined(),
276        check,
277        // Rendered only when the error will be: a terse one is counted.
278        regexp_src: if w.ctx.terse {
279            None
280        } else {
281            n.regexp.as_ref().map(|_| format!("/{}/", n.regexp_src))
282        },
283        terse: w.ctx.terse,
284    }
285}
286
287fn emit_update_errors(
288    at: &At<'_>,
289    n: &Node,
290    silent: bool,
291    update: &Update,
292    verr: &mut ValidationError,
293) {
294    if silent {
295        return;
296    }
297    let why = update.why.as_deref().unwrap_or("");
298    match &update.err {
299        None => {
300            let why = if why.is_empty() { WHY_CHECK } else { why };
301            let mark = if update.mark == 0 {
302                MARK_CUSTOM_CHECK_ERR
303            } else {
304                update.mark
305            };
306            let mut err = make_err(at, why, mark, "");
307            if let Some(f) = &n.fault_msg {
308                if !err.terse {
309                    err.text = expand_err_text(f, &err.path, at.value, at.absent);
310                }
311            }
312            verr.add(err);
313        }
314        Some(UpdateErr::Text(t)) => {
315            let why = if why.is_empty() { WHY_CHECK } else { why };
316            let mark = if update.mark == 0 {
317                MARK_CUSTOM_CHECK_TEXT
318            } else {
319                update.mark
320            };
321            verr.add(make_err(at, why, mark, t));
322        }
323        Some(UpdateErr::Field(e)) => {
324            let mut e = (**e).clone();
325            fill_field(&mut e, at);
326            verr.add(e);
327        }
328        Some(UpdateErr::Fields(es)) => {
329            for e in es {
330                let mut e = e.clone();
331                fill_field(&mut e, at);
332                verr.add(e);
333            }
334        }
335    }
336}
337
338fn fill_field(e: &mut FieldError, at: &At<'_>) {
339    if e.path.is_empty() {
340        e.path = crate::context::join_path(at.path);
341    }
342    if e.mark == 0 {
343        e.mark = MARK_CUSTOM_CHECK_TEXT;
344    }
345}
346
347fn add_structural(
348    at: &At<'_>,
349    n: &Node,
350    silent: bool,
351    why: &str,
352    mark: i64,
353    verr: &mut ValidationError,
354) {
355    let mut err = make_err(at, why, mark, "");
356    if let Some(f) = &n.fault_msg {
357        if !err.terse {
358            err.text = expand_err_text(f, &err.path, at.value, at.absent);
359        }
360    }
361    if !silent {
362        verr.add(err);
363    }
364}
365
366pub(crate) fn emit_type_err(
367    w: &Walk<'_>,
368    key: &str,
369    n: &Node,
370    silent: bool,
371    value: &Value,
372    parent_is_array: bool,
373    verr: &mut ValidationError,
374) {
375    let at = at_of(w, key, n, value, parent_is_array, "");
376    add_structural(&at, n, silent, WHY_TYPE, type_mark_for(n.kind), verr);
377}
378
379/// The structural checks: composition, nullable, never, the missing-value
380/// rules and the kind check.
381#[allow(clippy::too_many_arguments)]
382fn validate_structure(
383    n: &Node,
384    silent: bool,
385    mut cur: Cur<'_>,
386    key: &str,
387    parent_is_array: bool,
388    absent: bool,
389    w: &mut Walk<'_>,
390    verr: &mut ValidationError,
391) -> bool {
392    if n.kind == Kind::List && n.list_mode != ListMode::None && (n.required || !absent) {
393        let before = verr.count();
394        let kept = evaluate_list(
395            n,
396            silent,
397            cur.reborrow(),
398            key,
399            parent_is_array,
400            absent,
401            w,
402            verr,
403        );
404        // A required composition that passed but left an absent subject
405        // absent (All produces nothing from it) reports the missing value,
406        // as TypeScript's required check does after the validators.
407        if absent && n.required && verr.count() == before && cur.get().is_undefined() {
408            let at = at_of(w, key, n, cur.get(), parent_is_array, "");
409            add_structural(
410                &at,
411                n,
412                silent,
413                WHY_REQUIRED,
414                required_mark_for(n.kind),
415                verr,
416            );
417            return false;
418        }
419        return kept;
420    }
421
422    // Nullable: an explicit null is the value.
423    if cur.get().is_null() && n.nullable {
424        return true;
425    }
426
427    // Never rejects a value present or absent.
428    if n.kind == Kind::Never {
429        let at = at_of(w, key, n, cur.get(), parent_is_array, "");
430        add_structural(&at, n, silent, WHY_NEVER, MARK_NEVER, verr);
431        return !absent;
432    }
433
434    // A regexp never reports "required": an absent value is a non-string.
435    if n.kind == Kind::Regexp && absent && !n.required {
436        return false;
437    }
438
439    // Missing: required error, skip, inject the default, or descend into an
440    // empty container so that required descendants still raise.
441    if absent && n.kind != Kind::Regexp {
442        if n.required {
443            let at = at_of(w, key, n, cur.get(), parent_is_array, "");
444            add_structural(
445                &at,
446                n,
447                silent,
448                WHY_REQUIRED,
449                required_mark_for(n.kind),
450                verr,
451            );
452            return false;
453        }
454        if n.skippable {
455            return false;
456        }
457        if !n.has_default && (n.kind == Kind::Object || n.kind == Kind::Array) {
458            cur.set(if n.kind == Kind::Array {
459                Value::Arr(Vec::new())
460            } else {
461                Value::Obj(Default::default())
462            });
463            if matches!(cur, Cur::Ref(_)) {
464                // Nothing to write into: probe a scratch container so that
465                // the descendants are still judged.
466                let mut scratch = if n.kind == Kind::Array {
467                    Value::Arr(Vec::new())
468                } else {
469                    Value::Obj(Default::default())
470                };
471                return validate_structure(
472                    n,
473                    silent,
474                    Cur::Mut(&mut scratch),
475                    key,
476                    parent_is_array,
477                    false,
478                    w,
479                    verr,
480                );
481            }
482        } else {
483            if n.default.is_undefined() {
484                // Nothing to inject: the slot stays empty.
485                return false;
486            }
487            cur.set(n.default.clone());
488            return true;
489        }
490    }
491
492    let value = cur.get();
493    match n.kind {
494        Kind::Any | Kind::Check => {}
495        Kind::Regexp => {
496            let Some(s) = value.as_str() else {
497                let at = at_of(w, key, n, value, parent_is_array, "");
498                let mut err = make_err(&at, WHY_TYPE, MARK_SCALAR_TYPE, "");
499                err.kind = Kind::String;
500                if !err.terse {
501                    err.text = default_err_text(&err);
502                    if let Some(f) = &n.fault_msg {
503                        err.text = expand_err_text(f, &err.path, value, absent);
504                    }
505                }
506                if !silent {
507                    verr.add(err);
508                }
509                return true;
510            };
511            let matched = n.regexp.as_ref().is_some_and(|re| re.is_match(s));
512            if !matched {
513                let at = at_of(w, key, n, value, parent_is_array, "");
514                add_structural(&at, n, silent, WHY_REGEXP, MARK_REGEXP, verr);
515            }
516        }
517        Kind::String => match value {
518            Value::Str(s) => {
519                if s.is_empty() && !n.empty {
520                    let at = at_of(w, key, n, value, parent_is_array, "");
521                    add_structural(&at, n, silent, WHY_REQUIRED, MARK_UNDEF_REQUIRED, verr);
522                }
523            }
524            _ => emit_type_err(w, key, n, silent, value, parent_is_array, verr),
525        },
526        Kind::Number => match value {
527            Value::Num(x) if !x.is_nan() => {}
528            _ => emit_type_err(w, key, n, silent, value, parent_is_array, verr),
529        },
530        Kind::Integer => match value {
531            Value::Num(x) if is_integer(*x) => {}
532            _ => emit_type_err(w, key, n, silent, value, parent_is_array, verr),
533        },
534        Kind::Boolean => {
535            if !matches!(value, Value::Bool(_)) {
536                emit_type_err(w, key, n, silent, value, parent_is_array, verr);
537            }
538        }
539        Kind::Date => {
540            if !matches!(value, Value::Date(_)) {
541                emit_type_err(w, key, n, silent, value, parent_is_array, verr);
542            }
543        }
544        Kind::BigInt => {
545            if !matches!(value, Value::BigInt(_)) {
546                emit_type_err(w, key, n, silent, value, parent_is_array, verr);
547            }
548        }
549        Kind::NaN => match value {
550            Value::Num(x) if x.is_nan() => {}
551            _ => emit_type_err(w, key, n, silent, value, parent_is_array, verr),
552        },
553        Kind::Null => {
554            if !value.is_null() {
555                emit_type_err(w, key, n, silent, value, parent_is_array, verr);
556            }
557        }
558        Kind::Function => {
559            if !matches!(value, Value::Func(_)) {
560                emit_type_err(w, key, n, silent, value, parent_is_array, verr);
561            }
562        }
563        Kind::Array => return validate_array(n, silent, cur, key, parent_is_array, w, verr),
564        Kind::Object => return validate_object(n, silent, cur, key, parent_is_array, w, verr),
565        Kind::Never | Kind::List => {}
566    }
567    true
568}
569
570/// Whether a present value passes a plain scalar node with nothing left to
571/// do: the structural check alone, with no error to make. Anything else
572/// takes the full walk, which reports it.
573#[inline]
574fn plain_scalar_passes(n: &Node, v: &Value) -> bool {
575    match (n.kind, v) {
576        (Kind::String, Value::Str(s)) => n.empty || !s.is_empty(),
577        (Kind::Number, Value::Num(x)) => !x.is_nan(),
578        (Kind::Integer, Value::Num(x)) => is_integer(*x),
579        (Kind::Boolean, Value::Bool(_)) => true,
580        _ => false,
581    }
582}
583
584/// The key of the child at `idx`, shared with the node once it is prepared;
585/// a node that never was allocates it.
586fn child_key(n: &Node, idx: usize, k: &str) -> Arc<str> {
587    match n.obj_keys.get(idx) {
588        Some(key) => Arc::clone(key),
589        None => Arc::from(k),
590    }
591}
592
593/// Whether the input's keys are the declared keys in declaration order, or
594/// a prefix of them: then none is unknown, and each child sits at its own
595/// index. The usual case for JSON written from the same shape, and a
596/// string comparison per key rather than a hash lookup.
597fn aligned(n: &Node, map: &Map) -> bool {
598    map.len() <= n.obj_keys.len()
599        && map
600            .keys()
601            .zip(&n.obj_keys)
602            .all(|(k, dk)| k.as_str() == &**dk)
603}
604
605/// The child at `k`, guessed at its declared position first: the input
606/// written from the same shape has it there, and the guess is a short
607/// comparison rather than a hash lookup. Any other order looks it up.
608fn guess<'m>(map: &'m Map, idx: usize, k: &str) -> Option<&'m Value> {
609    match map.get_index(idx) {
610        Some((mk, v)) if mk == k => Some(v),
611        _ => map.get(k),
612    }
613}
614
615/// The index of the child at `k`, guessed as `guess` does.
616fn guess_index(map: &Map, idx: usize, k: &str) -> Option<usize> {
617    match map.get_index(idx) {
618        Some((mk, _)) if mk == k => Some(idx),
619        _ => map.get_index_of(k),
620    }
621}
622
623/// The keys of a closed object it does not consume, in the input's order.
624fn unknown_keys(n: &Node, map: &Map) -> Vec<String> {
625    map.iter()
626        .filter(|(k, v)| !v.is_undefined() && !n.consumed.contains(*k))
627        .map(|(k, _)| k.clone())
628        .collect()
629}
630
631fn validate_object(
632    n: &Node,
633    silent: bool,
634    cur: Cur<'_>,
635    key: &str,
636    parent_is_array: bool,
637    w: &mut Walk<'_>,
638    verr: &mut ValidationError,
639) -> bool {
640    // Unknown keys are reported before descending, in one message, in the
641    // input's order.
642    let aligned = cur.get().as_obj().is_some_and(|map| aligned(n, map));
643    if let Some(map) = cur.get().as_obj() {
644        if !n.is_open() && !aligned {
645            let unknown = unknown_keys(n, map);
646            if !unknown.is_empty() {
647                let value = cur.get();
648                let joined = unknown.join(", ");
649                let at = at_of(w, &joined, n, value, parent_is_array, "");
650                let mut err = make_err(&at, WHY_CLOSED, MARK_OBJECT_CLOSED, "");
651                err.plural = unknown.len() > 1;
652                if !err.terse {
653                    err.text = default_err_text(&err);
654                    if let Some(f) = &n.fault_msg {
655                        err.text = expand_err_text(f, &err.path, value, false);
656                    }
657                }
658                if !silent {
659                    verr.add(err);
660                }
661            }
662        }
663    }
664
665    // The container is the wrong type: its declared keys are meaningless,
666    // so no descent.
667    let mut oc: Cur<'_, Map> = match cur {
668        Cur::Mut(Value::Obj(m)) => Cur::Mut(m),
669        Cur::Ref(Value::Obj(m)) => Cur::Ref(m),
670        other => {
671            emit_type_err(w, key, n, silent, other.get(), parent_is_array, verr);
672            return true;
673        }
674    };
675
676    for (idx, (k, cn)) in n.obj_children.iter().enumerate() {
677        // A plain child with a present value of its kind: a scalar is judged
678        // here, and a read-only walk descends into a container directly,
679        // without the general walk's bookkeeping.
680        if cn.plain {
681            let found = match &oc {
682                Cur::Ref(map) if aligned => map.get_index(idx).map(|(_, v)| v),
683                Cur::Ref(map) => guess(map, idx, k),
684                Cur::Mut(map) => guess(map, idx, k),
685            };
686            if let Some(v) = found {
687                if plain_scalar_passes(cn, v) {
688                    continue;
689                }
690                if let Cur::Ref(_) = &oc {
691                    let container = matches!(
692                        (cn.kind, v),
693                        (Kind::Object, Value::Obj(_)) | (Kind::Array, Value::Arr(_))
694                    );
695                    if container {
696                        if w.paths {
697                            w.push(PathPart::Key(child_key(n, idx, k)));
698                        }
699                        if cn.kind == Kind::Object {
700                            validate_object(cn, cn.silent, Cur::Ref(v), k, false, w, verr);
701                        } else {
702                            validate_array(cn, cn.silent, Cur::Ref(v), k, false, w, verr);
703                        }
704                        w.pop();
705                        continue;
706                    }
707                }
708            }
709        }
710
711        // A rename's claim: the value is missing and a claimed source has
712        // it, so it is picked up from there.
713        let mut claimed: Option<&Value> = None;
714        if cn.rename_to.is_some() && !cn.rename_claim.is_empty() {
715            let present = oc.get().get(k).map(|v| !v.is_undefined()).unwrap_or(false);
716            if !present {
717                let src = cn
718                    .rename_claim
719                    .iter()
720                    .find(|src| oc.get().contains_key(*src))
721                    .cloned();
722                if let Some(src) = src {
723                    match &mut oc {
724                        Cur::Mut(map) => {
725                            let v = if cn.rename_keep {
726                                map.get(&src).cloned().unwrap()
727                            } else {
728                                map.shift_remove(&src).unwrap()
729                            };
730                            map.insert(k.clone(), v);
731                        }
732                        Cur::Ref(map) => claimed = map.get(&src),
733                    }
734                }
735            }
736        }
737
738        // The shared key is only taken when the path is kept.
739        if w.paths {
740            w.push(PathPart::Key(child_key(n, idx, k)));
741        }
742        // The scratch slot of an absent child on a read-only walk, made
743        // only on that path.
744        let mut scratch: Value;
745        let (child, was_absent) = match &mut oc {
746            Cur::Mut(map) => match guess_index(map, idx, k) {
747                // The key is cloned only when it is absent and must be made.
748                Some(pos) => (Cur::Mut(&mut map[pos]), false),
749                None => (
750                    Cur::Mut(map.entry(k.clone()).or_insert(Value::Undefined)),
751                    true,
752                ),
753            },
754            Cur::Ref(map) => {
755                // A read-only walk of an aligned input finds the child by
756                // index; the map cannot have changed under it.
757                let found = if aligned {
758                    map.get_index(idx).map(|(_, v)| v)
759                } else {
760                    claimed.or_else(|| guess(map, idx, k))
761                };
762                match found {
763                    Some(child) if !child.is_undefined() => (Cur::Ref(child), false),
764                    _ => {
765                        scratch = Value::Undefined;
766                        (Cur::Mut(&mut scratch), true)
767                    }
768                }
769            }
770        };
771        let keep = if cn.is_ignore() {
772            validate_ignored(cn, child, k, false, w, verr)
773        } else {
774            validate_node(cn, child, k, false, w, verr)
775        };
776        w.pop();
777        if let Cur::Mut(m) = &mut oc {
778            if !keep {
779                let empty = m.get(k).map(|x| x.is_undefined()).unwrap_or(false);
780                if was_absent || empty || cn.is_ignore() {
781                    m.shift_remove(k);
782                }
783            } else if let Some(to) = &cn.rename_to {
784                // The produced value moves under the new name.
785                if to != k {
786                    let v = if cn.rename_keep {
787                        m.get(k).cloned()
788                    } else {
789                        m.shift_remove(k)
790                    };
791                    if let Some(v) = v {
792                        m.insert(to.clone(), v);
793                    }
794                }
795            }
796        }
797    }
798
799    // An aligned read-only walk has nothing left over.
800    let rest_keys = !(aligned && matches!(oc, Cur::Ref(_)));
801    if let Some(rest) = n.obj_rest.as_deref().filter(|_| rest_keys) {
802        let extra: Vec<String> = oc
803            .get()
804            .iter()
805            .filter(|(k, v)| !v.is_undefined() && !n.obj_children.contains_key(*k))
806            .map(|(k, _)| k.clone())
807            .collect();
808        for k in extra {
809            w.push(PathPart::Key(Arc::from(k.as_str())));
810            let child = match &mut oc {
811                Cur::Mut(map) => Cur::Mut(map.get_mut(&k).unwrap()),
812                Cur::Ref(map) => Cur::Ref(map.get(&k).unwrap()),
813            };
814            let keep = if rest.is_ignore() {
815                validate_ignored(rest, child, &k, false, w, verr)
816            } else {
817                validate_node(rest, child, &k, false, w, verr)
818            };
819            w.pop();
820            if !keep {
821                if let Cur::Mut(m) = &mut oc {
822                    m.shift_remove(&k);
823                }
824            }
825        }
826    }
827    true
828}
829
830fn validate_array(
831    n: &Node,
832    silent: bool,
833    cur: Cur<'_>,
834    key: &str,
835    parent_is_array: bool,
836    w: &mut Walk<'_>,
837    verr: &mut ValidationError,
838) -> bool {
839    let fixed = n.arr_children.len();
840    let has_child = n.arr_child.is_some();
841
842    // A closed tuple with extra elements: one error naming the first index
843    // beyond the tuple, and no element is validated.
844    if let Some(arr) = cur.get().as_arr() {
845        if fixed > 0 && arr.len() > fixed && !has_child && n.arr_rest.is_none() {
846            let value = cur.get();
847            let k = fixed.to_string();
848            let at = at_of(w, &k, n, value, parent_is_array, "");
849            let mut err = make_err(&at, WHY_CLOSED, MARK_ARRAY_CLOSED, "");
850            if !err.terse {
851                if let Some(f) = &n.fault_msg {
852                    err.text = expand_err_text(f, &err.path, value, false);
853                }
854            }
855            if !silent {
856                verr.add(err);
857            }
858            return true;
859        }
860    }
861
862    let mut ac: Cur<'_, Vec<Value>> = match cur {
863        Cur::Mut(Value::Arr(a)) => Cur::Mut(a),
864        Cur::Ref(Value::Arr(a)) => Cur::Ref(a),
865        other => {
866            emit_type_err(w, key, n, silent, other.get(), parent_is_array, verr);
867            return true;
868        }
869    };
870    let len = ac.get().len();
871
872    let mut buf = [0u8; 20];
873    let mut i = 0usize;
874    for cn in &n.arr_children {
875        let k = index_key(i, &mut buf);
876        w.push(PathPart::Index(i));
877        let mut scratch = Value::Undefined;
878        let (child, appended) = match &mut ac {
879            Cur::Mut(a) => {
880                let appended = i >= a.len();
881                if appended {
882                    a.push(Value::Undefined);
883                }
884                (Cur::Mut(&mut a[i]), appended)
885            }
886            Cur::Ref(a) => match a.get(i) {
887                Some(e) if !e.is_undefined() => (Cur::Ref(e), false),
888                _ => (Cur::Mut(&mut scratch), true),
889            },
890        };
891        let keep = if cn.is_ignore() {
892            validate_ignored(cn, child, k, true, w, verr)
893        } else {
894            validate_node(cn, child, k, true, w, verr)
895        };
896        w.pop();
897        if !keep && !appended && cn.is_ignore() {
898            if let Cur::Mut(a) = &mut ac {
899                a[i] = Value::Undefined;
900            }
901        }
902        i += 1;
903    }
904    // Positions appended for missing tuple slots that produced nothing are
905    // holes at the end; drop them.
906    if let Cur::Mut(a) = &mut ac {
907        while a.len() > len && a.last().map(|x| x.is_undefined()).unwrap_or(false) {
908            a.pop();
909        }
910    }
911
912    if let Some(child_shape) = n.arr_child.as_deref().or(n.arr_rest.as_deref()) {
913        let mut drop_at: Vec<usize> = Vec::new();
914        while i < len {
915            // A plain element shape: a scalar element of its kind is judged
916            // here on either walk (it is its own produced value), and a
917            // read-only walk descends into an object directly.
918            if child_shape.plain {
919                if plain_scalar_passes(child_shape, &ac.get()[i]) {
920                    i += 1;
921                    continue;
922                }
923                if let Cur::Ref(a) = &ac {
924                    let v = &a[i];
925                    if child_shape.kind == Kind::Object && matches!(v, Value::Obj(_)) {
926                        let k = if w.paths { index_key(i, &mut buf) } else { "" };
927                        w.push(PathPart::Index(i));
928                        validate_object(
929                            child_shape,
930                            child_shape.silent,
931                            Cur::Ref(v),
932                            k,
933                            true,
934                            w,
935                            verr,
936                        );
937                        w.pop();
938                        i += 1;
939                        continue;
940                    }
941                }
942            }
943            let k = index_key(i, &mut buf);
944            w.push(PathPart::Index(i));
945            let child = match &mut ac {
946                Cur::Mut(a) => Cur::Mut(&mut a[i]),
947                Cur::Ref(a) => Cur::Ref(&a[i]),
948            };
949            let keep = if child_shape.is_ignore() {
950                validate_ignored(child_shape, child, k, true, w, verr)
951            } else {
952                validate_node(child_shape, child, k, true, w, verr)
953            };
954            w.pop();
955            if !keep && child_shape.is_ignore() {
956                drop_at.push(i);
957            }
958            i += 1;
959        }
960        if let Cur::Mut(a) = &mut ac {
961            for idx in drop_at {
962                a[idx] = Value::Undefined;
963            }
964        }
965    }
966    true
967}
968
969/// An `Ignore` node: run it with its silence lifted into a collector of its
970/// own, and drop the value when anything failed.
971pub(crate) fn validate_ignored(
972    n: &Node,
973    cur: Cur<'_>,
974    key: &str,
975    parent_is_array: bool,
976    w: &mut Walk<'_>,
977    _verr: &mut ValidationError,
978) -> bool {
979    let mut sub = ValidationError::default();
980    let kept = validate_node_with(
981        n,
982        &n.befores,
983        &n.afters,
984        false,
985        cur,
986        key,
987        parent_is_array,
988        w,
989        &mut sub,
990    );
991    !sub.has_any() && kept
992}
993
994/// Whether a branch accepts the value, without producing or rendering.
995fn branch_passes(
996    sn: &Node,
997    value: &Value,
998    key: &str,
999    parent_is_array: bool,
1000    w: &mut Walk<'_>,
1001) -> bool {
1002    let saved = (w.is_match, w.ctx.terse);
1003    w.is_match = true;
1004    w.ctx.terse = true;
1005    let mut sub = ValidationError {
1006        terse: true,
1007        ..Default::default()
1008    };
1009    validate_node(sn, Cur::Ref(value), key, parent_is_array, w, &mut sub);
1010    w.is_match = saved.0;
1011    w.ctx.terse = saved.1;
1012    !sub.has_any()
1013}
1014
1015#[allow(clippy::too_many_arguments)]
1016fn list_error(
1017    n: &Node,
1018    silent: bool,
1019    value: &Value,
1020    key: &str,
1021    parent_is_array: bool,
1022    w: &Walk<'_>,
1023    verr: &mut ValidationError,
1024    why: &str,
1025    mark: i64,
1026    words: &str,
1027    fault_applies: bool,
1028) {
1029    let names: Vec<String> = n.list.iter().map(|sn| stringify_node(sn, true)).collect();
1030    let at = at_of(w, key, n, value, parent_is_array, "");
1031    let text = format!(
1032        "Value \"$VALUE\" for property \"$PATH\" does not satisfy {}: {}",
1033        words,
1034        names.join(", ")
1035    );
1036    let mut err = make_err(&at, why, mark, &text);
1037    if fault_applies && !err.terse {
1038        if let Some(f) = &n.fault_msg {
1039            err.text = expand_err_text(f, &err.path, value, at.absent);
1040        }
1041    }
1042    if !silent {
1043        verr.add(err);
1044    }
1045}
1046
1047/// One, Some and All. The branches see the value as the parent saw it: an
1048/// absent value stays absent, so a branch that does not require one can
1049/// still match and supply its default.
1050#[allow(clippy::too_many_arguments)]
1051fn evaluate_list(
1052    n: &Node,
1053    silent: bool,
1054    mut cur: Cur<'_>,
1055    key: &str,
1056    parent_is_array: bool,
1057    _absent: bool,
1058    w: &mut Walk<'_>,
1059    verr: &mut ValidationError,
1060) -> bool {
1061    match n.list_mode {
1062        ListMode::One => {
1063            let winner = n
1064                .list
1065                .iter()
1066                .position(|sn| branch_passes(sn, cur.get(), key, parent_is_array, w));
1067            match winner {
1068                Some(i) => {
1069                    // The value is produced by the branch that took it.
1070                    let mut sub = ValidationError::default();
1071                    validate_node(
1072                        &n.list[i],
1073                        cur.reborrow(),
1074                        key,
1075                        parent_is_array,
1076                        w,
1077                        &mut sub,
1078                    )
1079                }
1080                None => {
1081                    list_error(
1082                        n,
1083                        silent,
1084                        cur.get(),
1085                        key,
1086                        parent_is_array,
1087                        w,
1088                        verr,
1089                        "One",
1090                        4030,
1091                        "one of",
1092                        true,
1093                    );
1094                    true
1095                }
1096            }
1097        }
1098        ListMode::Some => {
1099            // Every branch sees the value as it was given, never one another
1100            // branch changed: each matching branch produces from its own copy
1101            // of the original, and the last one's result stands.
1102            let original = cur.get().clone();
1103            let mut matched = false;
1104            let mut kept = true;
1105            for sn in &n.list {
1106                if !branch_passes(sn, &original, key, parent_is_array, w) {
1107                    continue;
1108                }
1109                matched = true;
1110                cur.set(original.clone());
1111                let mut sub = ValidationError::default();
1112                kept = validate_node(sn, cur.reborrow(), key, parent_is_array, w, &mut sub);
1113            }
1114            if !matched {
1115                list_error(
1116                    n,
1117                    silent,
1118                    &original,
1119                    key,
1120                    parent_is_array,
1121                    w,
1122                    verr,
1123                    "Some",
1124                    4031,
1125                    "any of",
1126                    true,
1127                );
1128                return true;
1129            }
1130            kept
1131        }
1132        _ => {
1133            // All threads the value through its branches; a branch that
1134            // fails leaves the value as the one before it produced.
1135            let original = cur.get().clone();
1136            let mut pass_all = true;
1137            let mut kept = true;
1138            for sn in &n.list {
1139                let before = cur.get().clone();
1140                let mut sub = ValidationError::default();
1141                let k = validate_node(sn, cur.reborrow(), key, parent_is_array, w, &mut sub);
1142                if sub.has_any() {
1143                    pass_all = false;
1144                    cur.set(before);
1145                } else {
1146                    kept = k;
1147                }
1148            }
1149            if !pass_all {
1150                cur.set(original.clone());
1151                list_error(
1152                    n,
1153                    silent,
1154                    &original,
1155                    key,
1156                    parent_is_array,
1157                    w,
1158                    verr,
1159                    "All",
1160                    4032,
1161                    "all of",
1162                    false,
1163                );
1164                return true;
1165            }
1166            // An absent subject stays absent, for the required check to
1167            // see, as in TypeScript.
1168            if original.is_undefined() {
1169                cur.set(original);
1170            }
1171            kept
1172        }
1173    }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178    use super::*;
1179    use crate::builders::*;
1180    use crate::node::{Token, Validator};
1181    use crate::spec::{arr, obj, Spec};
1182    use crate::Schema;
1183    use regex::Regex;
1184    use std::sync::Arc;
1185
1186    fn chk(
1187        name: &str,
1188        f: impl Fn(&mut State<'_>, &mut Update) -> bool + Send + Sync + 'static,
1189    ) -> Validator {
1190        Validator {
1191            name: name.into(),
1192            func: Arc::new(f),
1193            args: vec![],
1194            suffix: None,
1195            inner: None,
1196        }
1197    }
1198
1199    fn j(s: &str) -> Value {
1200        Value::from(serde_json::from_str::<serde_json::Value>(s).unwrap())
1201    }
1202
1203    fn run(s: &Schema, input: &str) -> String {
1204        match s.validate(j(input)) {
1205            Ok(v) => serde_json::Value::from(v).to_string(),
1206            Err(e) => format!("ERR {}", e),
1207        }
1208    }
1209
1210    fn before(mut n: Node, v: Validator) -> Node {
1211        n.befores.push(v);
1212        n
1213    }
1214
1215    fn after(mut n: Node, v: Validator) -> Node {
1216        n.afters.push(v);
1217        n
1218    }
1219
1220    fn at_a(n: impl Into<Spec>) -> Schema {
1221        Schema::new(obj([("a", n.into())]))
1222    }
1223
1224    fn fail() -> Validator {
1225        chk("Odd", |_, _| false)
1226    }
1227
1228    #[test]
1229    fn index_keys_need_no_allocation() {
1230        let mut buf = [0u8; 20];
1231        assert_eq!(index_key(0, &mut buf), "0");
1232        assert_eq!(index_key(7, &mut buf), "7");
1233        assert_eq!(index_key(1234567890123, &mut buf), "1234567890123");
1234    }
1235
1236    #[test]
1237    fn plain_nodes_take_the_direct_path() {
1238        // Scalars, nested objects and arrays of objects with no validators
1239        // are judged in place on a read-only walk, with the paths intact.
1240        let s = Schema::new(obj([
1241            ("a", obj([("b", Spec::from(Token::Number))])),
1242            ("c", arr([obj([("d", Spec::from(Token::String))])])),
1243            ("e", arr([Spec::from(Token::Integer)])),
1244            ("f", Spec::from(Token::Boolean)),
1245        ]));
1246        assert!(s.valid(&j(
1247            r#"{"a":{"b":1},"c":[{"d":"x"},{"d":"y"}],"e":[1,2],"f":true}"#
1248        )));
1249        let bad = j(r#"{"a":{"b":"x"},"c":[{"d":"x"},{"d":2}],"e":[1,2.5],"f":1}"#);
1250        assert!(!s.valid(&bad));
1251        let paths: Vec<String> = s.error(&bad).iter().map(|e| e.path.clone()).collect();
1252        assert_eq!(paths, vec!["a.b", "c.1.d", "e.1", "f"]);
1253        // A produced walk takes the same shortcut for a passing scalar.
1254        assert_eq!(
1255            run(&s, r#"{"a":{"b":1},"c":[],"e":[],"f":false}"#),
1256            r#"{"a":{"b":1},"c":[],"e":[],"f":false}"#
1257        );
1258        // An empty string is judged by the node's Empty flag.
1259        let e = Schema::new(obj([("s", Spec::from(Token::String))]));
1260        assert!(!e.valid(&j(r#"{"s":""}"#)));
1261        assert!(
1262            Schema::new(obj([("s", Spec::from(empty(Token::String)))])).valid(&j(r#"{"s":""}"#))
1263        );
1264    }
1265
1266    #[test]
1267    fn unprepared_object_keys_are_allocated() {
1268        // A node that never went through prepare has no shared keys, so the
1269        // walk allocates them; the path still renders.
1270        let n = crate::normalize::normalize(obj([("a", Token::Number)]));
1271        assert!(n.obj_keys.is_empty());
1272        let mut ctx = crate::Context::new();
1273        let defs = HashMap::new();
1274        let mut w = Walk {
1275            ctx: &mut ctx,
1276            defs: &defs,
1277            is_match: false,
1278            path: Vec::new(),
1279            paths: true,
1280        };
1281        let mut verr = ValidationError::default();
1282        let mut val = j(r#"{"a":"x"}"#);
1283        validate_node(&n, Cur::Mut(&mut val), "", false, &mut w, &mut verr);
1284        // Nothing is consumed either, so the key is also reported unknown.
1285        assert_eq!(
1286            verr.to_string(),
1287            "Validation failed for object \"{a:x}\" because the property \"a\" is not allowed.\n\
1288             Validation failed for property \"a\" with string \"x\" because the string is not of type number."
1289        );
1290    }
1291
1292    #[test]
1293    fn marks() {
1294        assert_eq!(required_mark_for(Kind::Object), MARK_OBJECT_REQUIRED);
1295        assert_eq!(required_mark_for(Kind::Array), MARK_ARRAY_REQUIRED);
1296        assert_eq!(required_mark_for(Kind::String), MARK_SCALAR_REQUIRED);
1297        assert_eq!(type_mark_for(Kind::Object), MARK_OBJECT_TYPE);
1298        assert_eq!(type_mark_for(Kind::Array), MARK_ARRAY_TYPE);
1299        assert_eq!(type_mark_for(Kind::Check), MARK_CHECK_TYPE);
1300        assert_eq!(type_mark_for(Kind::String), MARK_SCALAR_TYPE);
1301    }
1302
1303    #[test]
1304    fn custom_check_errors() {
1305        let s = at_a(before(buildize(Token::Number), fail()));
1306        assert_eq!(
1307            run(&s, r#"{"a":1}"#),
1308            "ERR Validation failed for property \"a\" with number \"1\" because check \"Odd\" failed."
1309        );
1310        assert!(!s.valid(&j(r#"{"a":1}"#)));
1311        assert_eq!(s.error(&j(r#"{"a":1}"#))[0].mark, MARK_CUSTOM_CHECK_ERR);
1312
1313        let s = at_a(before(fault("bad $PATH $VALUE", Token::Number), fail()));
1314        assert_eq!(run(&s, r#"{"a":1}"#), "ERR bad a 1");
1315        assert!(!s.valid(&j(r#"{"a":1}"#)));
1316
1317        let mut quiet = buildize(Token::Number);
1318        quiet.silent = true;
1319        let s = at_a(before(quiet, fail()));
1320        assert_eq!(run(&s, r#"{"a":1}"#), r#"{"a":1}"#);
1321
1322        let text = chk("T", |_, u| {
1323            u.err = Some(UpdateErr::Text("no $PATH=$VALUE".into()));
1324            false
1325        });
1326        let s = at_a(before(buildize(Token::Number), text));
1327        assert_eq!(run(&s, r#"{"a":1}"#), "ERR no a=1");
1328        let e = &s.error(&j(r#"{"a":1}"#))[0];
1329        assert_eq!(
1330            (e.mark, e.why.as_str()),
1331            (MARK_CUSTOM_CHECK_TEXT, WHY_CHECK)
1332        );
1333
1334        let marked = chk("M", |_, u| {
1335            u.err = Some(UpdateErr::Text("m".into()));
1336            u.why = Some("odd".into());
1337            u.mark = 99;
1338            false
1339        });
1340        let e = &at_a(before(buildize(Token::Number), marked)).error(&j(r#"{"a":1}"#))[0];
1341        assert_eq!((e.mark, e.why.as_str(), e.text.as_str()), (99, "odd", "m"));
1342        let marked = chk("M", |_, u| {
1343            u.why = Some("odd".into());
1344            u.mark = 98;
1345            false
1346        });
1347        let e = &at_a(before(buildize(Token::Number), marked)).error(&j(r#"{"a":1}"#))[0];
1348        assert_eq!((e.mark, e.why.as_str()), (98, "odd"));
1349
1350        let field = chk("F", |_, u| {
1351            u.err = Some(UpdateErr::Field(Box::new(FieldError {
1352                text: "f".into(),
1353                ..Default::default()
1354            })));
1355            false
1356        });
1357        let s = at_a(before(buildize(Token::Number), field));
1358        assert_eq!(run(&s, r#"{"a":1}"#), "ERR f");
1359        let e = &s.error(&j(r#"{"a":1}"#))[0];
1360        assert_eq!((e.path.as_str(), e.mark), ("a", MARK_CUSTOM_CHECK_TEXT));
1361
1362        let fields = chk("F", |_, u| {
1363            u.err = Some(UpdateErr::Fields(vec![
1364                FieldError {
1365                    text: "f1".into(),
1366                    ..Default::default()
1367                },
1368                FieldError {
1369                    text: "f2".into(),
1370                    path: "z".into(),
1371                    mark: 7,
1372                    ..Default::default()
1373                },
1374            ]));
1375            false
1376        });
1377        let s = at_a(before(buildize(Token::Number), fields));
1378        assert_eq!(run(&s, r#"{"a":1}"#), "ERR f1\nf2");
1379        let es = s.error(&j(r#"{"a":1}"#));
1380        assert_eq!((es[1].path.as_str(), es[1].mark), ("z", 7));
1381    }
1382
1383    #[test]
1384    fn befores_replace_value_or_node_and_may_stop() {
1385        let dbl = chk("Dbl", |s, u| {
1386            if let Value::Num(x) = s.value {
1387                u.val = Some(Value::Num(x * 2.0));
1388            }
1389            true
1390        });
1391        let s = at_a(before(buildize(Token::Number), dbl));
1392        assert_eq!(run(&s, r#"{"a":2}"#), r#"{"a":4}"#);
1393        assert!(s.valid(&j(r#"{"a":2}"#)));
1394
1395        let to_str = chk("Str", |_, u| {
1396            u.val = Some(Value::Str("x".into()));
1397            true
1398        });
1399        let s = at_a(before(buildize(Token::Number), to_str));
1400        assert_eq!(
1401            run(&s, r#"{"a":2}"#),
1402            "ERR Validation failed for property \"a\" with string \"x\" because the string is not of type number."
1403        );
1404        assert!(!s.valid(&j(r#"{"a":2}"#)));
1405
1406        let swap = chk("Swap", |_, u| {
1407            u.node = Some(Arc::new(buildize(Token::String)));
1408            true
1409        });
1410        let s = at_a(before(buildize(Token::Number), swap));
1411        assert_eq!(run(&s, r#"{"a":"x"}"#), r#"{"a":"x"}"#);
1412
1413        let stop = chk("Stop", |_, u| {
1414            u.done = true;
1415            true
1416        });
1417        let s = at_a(before(buildize(Token::Number), stop));
1418        assert_eq!(run(&s, r#"{"a":"x"}"#), r#"{"a":"x"}"#);
1419
1420        // A failing check against an absent optional value is not an error.
1421        let s = at_a(before(optional(Token::Number), fail()));
1422        assert_eq!(run(&s, "{}"), r#"{"a":0}"#);
1423        // Unless the check insisted.
1424        let insist = chk("I", |_, u| {
1425            u.done = true;
1426            false
1427        });
1428        let s = at_a(before(optional(Token::Number), insist));
1429        assert_eq!(s.error(&j("{}")).len(), 1);
1430        // A required node's absence is the check's to report too, once.
1431        let s = at_a(before(required(Token::Number), fail()));
1432        assert_eq!(s.error(&j("{}")).len(), 1);
1433        // The check sees the state it is given.
1434        let peek = chk("Peek", |s, _| {
1435            s.absent && s.key == "a" && s.path_str() == "a" && !s.is_match && !s.parent_is_array
1436        });
1437        let s = at_a(before(optional(Token::Number), peek));
1438        assert_eq!(run(&s, "{}"), r#"{"a":0}"#);
1439    }
1440
1441    #[test]
1442    fn afters_run_after_structure() {
1443        let s = at_a(after(buildize(Token::Number), fail()));
1444        assert_eq!(s.error(&j(r#"{"a":1}"#))[0].check, "Odd");
1445
1446        let inc = chk("Inc", |s, u| {
1447            if let Value::Num(x) = s.value {
1448                u.val = Some(Value::Num(x + 1.0));
1449            }
1450            true
1451        });
1452        let s = at_a(after(buildize(Token::Number), inc));
1453        assert_eq!(run(&s, r#"{"a":1}"#), r#"{"a":2}"#);
1454        assert!(s.valid(&j(r#"{"a":1}"#)));
1455
1456        let fill = chk("Fill", |_, u| {
1457            u.val = Some(Value::Num(9.0));
1458            true
1459        });
1460        let s = at_a(after(skip(Token::Number), fill));
1461        assert_eq!(run(&s, "{}"), r#"{"a":9}"#);
1462
1463        // An injected default is the value a failing after reports.
1464        let s = at_a(after(optional(Token::Number), fail()));
1465        assert_eq!(
1466            run(&s, "{}"),
1467            "ERR Validation failed for property \"a\" with number \"0\" because check \"Odd\" failed."
1468        );
1469        let s = at_a(after(skip(Token::Number), fail()));
1470        assert_eq!(run(&s, "{}"), "{}");
1471    }
1472
1473    #[test]
1474    fn structural_rules() {
1475        assert_eq!(
1476            run(&Schema::new(one([Spec::from(Token::Number)])), "1"),
1477            "1"
1478        );
1479        assert_eq!(
1480            run(&Schema::new(Spec::from(Node::of(Kind::List))), "1"),
1481            "1"
1482        );
1483
1484        assert_eq!(
1485            run(&at_a(nullable(Token::String)), r#"{"a":null}"#),
1486            r#"{"a":null}"#
1487        );
1488
1489        let s = at_a(never(1));
1490        assert_eq!(s.error(&j(r#"{"a":1}"#))[0].why, WHY_NEVER);
1491        assert_eq!(s.error(&j("{}"))[0].why, WHY_NEVER);
1492        assert!(!s.valid(&j("{}")));
1493        assert_eq!(run(&at_a(never(1).fault("nope")), r#"{"a":1}"#), "ERR nope");
1494
1495        let re = || Regex::new("^a+$").unwrap();
1496        let s = at_a(buildize(re()));
1497        assert_eq!(
1498            run(&s, r#"{"a":"b"}"#),
1499            "ERR Validation failed for property \"a\" with string \"b\" because the string did not match /^a+$/."
1500        );
1501        assert_eq!(
1502            run(&s, r#"{"a":1}"#),
1503            "ERR Validation failed for property \"a\" with number \"1\" because the number is not of type string."
1504        );
1505        // A regexp never reports "required": an absent value is a non-string.
1506        assert_eq!(
1507            run(&s, "{}"),
1508            "ERR Validation failed for property \"a\" with value \"undefined\" because the value is not of type string."
1509        );
1510        assert_eq!(run(&s, r#"{"a":"aa"}"#), r#"{"a":"aa"}"#);
1511        assert_eq!(run(&at_a(optional(re())), "{}"), "{}");
1512        assert_eq!(run(&at_a(fault("F", re())), r#"{"a":1}"#), "ERR F");
1513        assert!(!at_a(fault("F", re())).valid(&j(r#"{"a":1}"#)));
1514        let mut quiet = buildize(re());
1515        quiet.silent = true;
1516        assert_eq!(run(&at_a(quiet), r#"{"a":1}"#), r#"{"a":1}"#);
1517
1518        // An absent container is probed on a shared cursor so that required
1519        // descendants still raise.
1520        let s = at_a(obj([("b", Spec::from(Token::String))]));
1521        assert!(!s.valid(&j("{}")));
1522        assert_eq!(s.error(&j("{}"))[0].path, "a.b");
1523        let s = at_a(arr([Spec::from(Token::String), Spec::from(1)]));
1524        assert!(!s.valid(&j("{}")));
1525        assert_eq!(run(&at_a(buildize(Token::Any)), "{}"), "{}");
1526        // An absent root, likewise.
1527        let s = Schema::new(obj([("b", Spec::from(Token::String))]));
1528        assert!(!s.valid(&Value::Undefined));
1529        assert_eq!(s.error(&Value::Undefined)[0].path, "b");
1530        let s = Schema::new(arr([Spec::from(Token::String), Spec::from(1)]));
1531        assert!(!s.valid(&Value::Undefined));
1532        assert_eq!(s.error(&Value::Undefined)[0].path, "0");
1533        assert!(Schema::new(obj([("b", Spec::from(1))])).valid(&Value::Undefined));
1534    }
1535
1536    #[test]
1537    fn leaf_type_errors() {
1538        let cases: Vec<(Node, &str, &str)> = vec![
1539            (
1540                buildize(Token::Integer),
1541                r#"{"a":1.5}"#,
1542                "number \"1.5\" because the number is not of type integer.",
1543            ),
1544            (
1545                buildize(Token::Boolean),
1546                r#"{"a":1}"#,
1547                "number \"1\" because the number is not of type boolean.",
1548            ),
1549            (
1550                buildize(Spec::Value(Value::Date(0))),
1551                r#"{"a":1}"#,
1552                "number \"1\" because the number is not of type date.",
1553            ),
1554            (
1555                buildize(num_bigint::BigInt::from(1)),
1556                r#"{"a":1}"#,
1557                "number \"1\" because the number is not of type bigint.",
1558            ),
1559            (
1560                buildize(f64::NAN),
1561                r#"{"a":1}"#,
1562                "number \"1\" because the number is not of type nan.",
1563            ),
1564            (
1565                buildize(Spec::Value(Value::Null)),
1566                r#"{"a":1}"#,
1567                "number \"1\" because the number is not of type null.",
1568            ),
1569            (
1570                buildize(Spec::Value(Value::Func(1))),
1571                r#"{"a":1}"#,
1572                "number \"1\" because the number is not of type function.",
1573            ),
1574        ];
1575        for (n, input, tail) in cases {
1576            assert_eq!(
1577                run(&at_a(n), input),
1578                format!("ERR Validation failed for property \"a\" with {}", tail)
1579            );
1580        }
1581        let s = at_a(buildize(Spec::Value(Value::Date(0))));
1582        let dated = Value::Obj([("a".to_string(), Value::Date(5))].into_iter().collect());
1583        assert_eq!(s.validate(dated.clone()).unwrap(), dated);
1584    }
1585
1586    #[test]
1587    fn objects() {
1588        let inner = || obj([("a", Spec::from(1))]);
1589        let s = Schema::new(obj([("o", Spec::from(fault("F $PATH", inner())))]));
1590        assert_eq!(run(&s, r#"{"o":{"z":1}}"#), "ERR F o");
1591        assert!(!s.valid(&j(r#"{"o":{"z":1}}"#)));
1592        let mut quiet = buildize(inner());
1593        quiet.silent = true;
1594        let s = Schema::new(obj([("o", Spec::from(quiet))]));
1595        assert_eq!(run(&s, r#"{"o":{"z":1}}"#), r#"{"o":{"z":1,"a":1}}"#);
1596
1597        let s = Schema::new(obj([("o", inner())]));
1598        assert!(s.valid(&j(r#"{"o":{"a":2}}"#)));
1599        assert!(!s.valid(&j(r#"{"o":{"a":"x"}}"#)));
1600        assert!(s.valid(&j(r#"{"o":{}}"#)));
1601        assert!(!s.valid(&j(r#"{"o":1}"#)));
1602
1603        let s = at_a(ignore(Token::Number));
1604        assert_eq!(run(&s, r#"{"a":"x"}"#), "{}");
1605        assert_eq!(run(&s, r#"{"a":5}"#), r#"{"a":5}"#);
1606        assert_eq!(run(&s, "{}"), "{}");
1607        assert!(s.valid(&j(r#"{"a":"x"}"#)));
1608        assert!(s.valid(&j(r#"{"a":5}"#)));
1609
1610        let s = Schema::new(child(Token::Number, inner()));
1611        assert_eq!(run(&s, r#"{"a":1,"z":2}"#), r#"{"a":1,"z":2}"#);
1612        assert!(!s.valid(&j(r#"{"a":1,"z":"x"}"#)));
1613        let s = Schema::new(child(ignore(Token::Number), inner()));
1614        assert_eq!(run(&s, r#"{"a":1,"z":"x"}"#), r#"{"a":1}"#);
1615        assert!(s.valid(&j(r#"{"a":1,"z":"x"}"#)));
1616    }
1617
1618    #[test]
1619    fn arrays() {
1620        let s = at_a(fault("F", arr([Spec::from(Token::Number)])));
1621        assert_eq!(run(&s, r#"{"a":1}"#), "ERR F");
1622        let tuple = || arr([Spec::from(1), Spec::from(2)]);
1623        assert_eq!(run(&at_a(fault("T", tuple())), r#"{"a":[1,2,3]}"#), "ERR T");
1624        assert!(!at_a(fault("T", tuple())).valid(&j(r#"{"a":[1,2,3]}"#)));
1625        let mut quiet = buildize(tuple());
1626        quiet.silent = true;
1627        assert_eq!(run(&at_a(quiet), r#"{"a":[1,2,3]}"#), r#"{"a":[1,2,3]}"#);
1628
1629        let s = at_a(tuple());
1630        assert!(s.valid(&j(r#"{"a":[1,2]}"#)));
1631        assert!(!s.valid(&j(r#"{"a":["x"]}"#)));
1632        assert!(s.valid(&j(r#"{"a":[]}"#)));
1633        let s = at_a(arr([Spec::from(Token::String), Spec::from(2)]));
1634        assert!(!s.valid(&j(r#"{"a":[]}"#)));
1635
1636        let s = at_a(arr([Spec::from(ignore(Token::Number)), Spec::from(2)]));
1637        assert_eq!(run(&s, r#"{"a":["x",2]}"#), r#"{"a":[null,2]}"#);
1638        assert_eq!(run(&s, r#"{"a":[1]}"#), r#"{"a":[1,2]}"#);
1639        assert!(s.valid(&j(r#"{"a":["x",2]}"#)));
1640
1641        let s = at_a(arr([Spec::from(1), Spec::from(skip(Token::Number))]));
1642        assert_eq!(run(&s, r#"{"a":[]}"#), r#"{"a":[1]}"#);
1643
1644        let s = at_a(arr([Spec::from(Token::Number)]));
1645        assert!(!s.valid(&j(r#"{"a":[1,"x"]}"#)));
1646        assert!(s.valid(&j(r#"{"a":[1,2]}"#)));
1647        let s = at_a(arr([Spec::from(ignore(Token::Number))]));
1648        assert_eq!(run(&s, r#"{"a":[1,"x"]}"#), r#"{"a":[1,null]}"#);
1649        assert!(s.valid(&j(r#"{"a":[1,"x"]}"#)));
1650    }
1651
1652    #[test]
1653    fn root_ignore_drops_a_bad_value() {
1654        let s = Schema::new(ignore(Token::Number));
1655        assert_eq!(run(&s, "\"x\""), "null");
1656        assert_eq!(run(&s, "5"), "5");
1657        assert!(s.valid(&j("\"x\"")));
1658        assert!(s.error(&j("\"x\"")).is_empty());
1659        assert_eq!(s.node().kind, Kind::Number);
1660    }
1661}