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            is_match: state.is_match,
45            path: state.path_arr.to_vec(),
46            paths: true,
47        };
48        let kept = validate_node_with(
49            state.node,
50            &self.befores,
51            &self.afters,
52            state.node.silent,
53            Cur::Mut(&mut val),
54            state.key,
55            state.parent_is_array,
56            &mut w,
57            &mut sub,
58        );
59        if !kept {
60            val = Value::Undefined;
61        }
62        (val, sub)
63    }
64}
65
66fn take_inner(n: &mut Node) -> Arc<Inner> {
67    Arc::new(Inner {
68        befores: std::mem::take(&mut n.befores),
69        afters: std::mem::take(&mut n.afters),
70    })
71}
72
73/// Catch: whatever fails inside is replaced with the fallback, raising
74/// nothing.
75pub(crate) fn catch_node(mut n: Node, fallback: Value) -> Node {
76    let inner = take_inner(&mut n);
77    let suffix = format!(
78        "{}Catch({})",
79        inner.desc(),
80        crate::stringify::inline_value(&fallback)
81    );
82    let run = Arc::clone(&inner);
83    let fb = fallback.clone();
84    n.befores = vec![Validator {
85        name: "Catch".to_string(),
86        func: Arc::new(move |state: &mut State<'_>, update: &mut Update| {
87            let (mut out, sub) = run.probe(state);
88            if sub.has_any() {
89                out = fb.clone();
90            }
91            update.val = Some(out);
92            update.done = true;
93            true
94        }),
95        args: vec![fallback],
96        suffix: Some(suffix),
97        inner: Some(inner),
98    }];
99    n
100}
101
102/// A Transform function: the produced value, and the state it was produced
103/// in, to a new value.
104pub type TransformFn = dyn Fn(Value, &mut State<'_>) -> Value + Send + Sync;
105
106/// Transform: a valid value is replaced with a function of it. An invalid
107/// one fails as it would have, with the same errors.
108pub(crate) fn transform_node(mut n: Node, f: Arc<TransformFn>) -> Node {
109    let inner = take_inner(&mut n);
110    let suffix = format!("{}Transform", inner.desc());
111    let run = Arc::clone(&inner);
112    n.befores = vec![Validator {
113        name: "Transform".to_string(),
114        func: Arc::new(move |state: &mut State<'_>, update: &mut Update| {
115            let (out, sub) = run.probe(state);
116            if sub.has_any() {
117                update.err = Some(UpdateErr::Fields(sub.issues));
118                return false;
119            }
120            update.val = Some(f(out, state));
121            update.done = true;
122            true
123        }),
124        args: Vec::new(),
125        suffix: Some(suffix),
126        inner: Some(inner),
127    }];
128    n
129}