Skip to main content

shape/
isolate.rs

1//! Isolated validation: Catch and Transform (Ignore probes the same way from
2//! its call sites). These builders take the checks a node carries, its
3//! befores and its afters, inside, and validate the node as a whole (those
4//! checks, the structural check, every descendant) in a sub-run before the
5//! node itself proceeds. Only then is the outcome of the entire subtree
6//! known at once; the canonical implementation does the same, since it runs
7//! a node's afters before visiting its children.
8
9use crate::context::{State, Update, UpdateErr};
10use crate::error::ValidationError;
11use crate::node::{Node, Validator};
12use crate::validate::{validate_node_with, Cur, Walk};
13use crate::value::Value;
14use std::sync::Arc;
15
16/// The checks a Catch or Transform took inside.
17#[derive(Clone, Debug, Default)]
18pub struct Inner {
19    pub befores: Vec<Validator>,
20    pub afters: Vec<Validator>,
21}
22
23impl Inner {
24    /// The taken checks rendered ahead of the taking builder, so that the
25    /// shape still reads `Number.Min(2).Catch(0)`.
26    fn desc(&self) -> String {
27        let mut out = String::new();
28        for v in self.befores.iter().chain(self.afters.iter()) {
29            if let Some(s) = &v.suffix {
30                out.push_str(s);
31                out.push('.');
32            }
33        }
34        out
35    }
36
37    /// Validate the node as it stands, with the taken checks, in isolation,
38    /// reporting the produced value and whatever failed.
39    fn probe(&self, state: &mut State<'_>) -> (Value, ValidationError) {
40        let mut val = state.value.clone();
41        let mut sub = ValidationError::default();
42        let mut w = Walk {
43            ctx: state.ctx,
44            defs: state.defs,
45            is_match: state.is_match,
46            path: state.path_arr.to_vec(),
47            paths: true,
48        };
49        let kept = validate_node_with(
50            state.node,
51            &self.befores,
52            &self.afters,
53            state.node.silent,
54            Cur::Mut(&mut val),
55            state.key,
56            state.parent_is_array,
57            &mut w,
58            &mut sub,
59        );
60        if !kept {
61            val = Value::Undefined;
62        }
63        (val, sub)
64    }
65}
66
67fn take_inner(n: &mut Node) -> Arc<Inner> {
68    Arc::new(Inner {
69        befores: std::mem::take(&mut n.befores),
70        afters: std::mem::take(&mut n.afters),
71    })
72}
73
74/// Catch: whatever fails inside is replaced with the fallback, raising
75/// nothing.
76pub(crate) fn catch_node(mut n: Node, fallback: Value) -> Node {
77    let inner = take_inner(&mut n);
78    let suffix = format!(
79        "{}Catch({})",
80        inner.desc(),
81        crate::stringify::inline_value(&fallback)
82    );
83    let run = Arc::clone(&inner);
84    let fb = fallback.clone();
85    n.befores = vec![Validator {
86        name: "Catch".to_string(),
87        func: Arc::new(move |state: &mut State<'_>, update: &mut Update| {
88            let (mut out, sub) = run.probe(state);
89            if sub.has_any() {
90                out = fb.clone();
91            }
92            update.val = Some(out);
93            update.done = true;
94            true
95        }),
96        args: vec![fallback],
97        suffix: Some(suffix),
98        inner: Some(inner),
99    }];
100    n
101}
102
103/// A Transform function: the produced value, and the state it was produced
104/// in, to a new value.
105pub type TransformFn = dyn Fn(Value, &mut State<'_>) -> Value + Send + Sync;
106
107/// Transform: a valid value is replaced with a function of it. An invalid
108/// one fails as it would have, with the same errors.
109pub(crate) fn transform_node(mut n: Node, f: Arc<TransformFn>) -> Node {
110    let inner = take_inner(&mut n);
111    let suffix = format!("{}Transform", inner.desc());
112    let run = Arc::clone(&inner);
113    n.befores = vec![Validator {
114        name: "Transform".to_string(),
115        func: Arc::new(move |state: &mut State<'_>, update: &mut Update| {
116            let (out, sub) = run.probe(state);
117            if sub.has_any() {
118                update.err = Some(UpdateErr::Fields(sub.issues));
119                return false;
120            }
121            update.val = Some(f(out, state));
122            update.done = true;
123            true
124        }),
125        args: Vec::new(),
126        suffix: Some(suffix),
127        inner: Some(inner),
128    }];
129    n
130}