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