vld/combinators/lazy_schema.rs
1use serde_json::Value;
2use std::sync::OnceLock;
3
4use crate::error::VldError;
5use crate::schema::VldSchema;
6
7/// Lazy schema for recursive/self-referencing data structures.
8///
9/// The schema factory is called on the first `parse_value` of this node and the
10/// result is cached, which allows defining schemas that reference themselves
11/// without rebuilding them on every parse.
12///
13/// Created via [`vld::lazy()`](crate::lazy).
14///
15/// Every `vld::lazy(...)` node calls its factory once; nested lazy nodes
16/// built by the factory are themselves created on first use, so a recursive
17/// schema unfolds only as deep as the inputs go.
18///
19/// # Example
20/// ```ignore
21/// // Recursive tree: { value: i64, children: Tree[] }
22/// fn tree_schema() -> impl VldSchema<Output = serde_json::Map<String, serde_json::Value>> {
23/// vld::object()
24/// .field("value", vld::number().int())
25/// .field("children", vld::array(vld::lazy(tree_schema)))
26/// }
27/// ```
28pub struct ZLazy<T, F>
29where
30 F: Fn() -> T,
31 T: VldSchema,
32{
33 factory: F,
34 schema: OnceLock<T>,
35}
36
37impl<T, F> ZLazy<T, F>
38where
39 F: Fn() -> T,
40 T: VldSchema,
41{
42 pub fn new(factory: F) -> Self {
43 Self {
44 factory,
45 schema: OnceLock::new(),
46 }
47 }
48
49 /// The (cached) schema produced by the factory.
50 pub fn schema(&self) -> &T {
51 self.schema.get_or_init(|| (self.factory)())
52 }
53}
54
55impl<T, F> VldSchema for ZLazy<T, F>
56where
57 F: Fn() -> T,
58 T: VldSchema,
59{
60 type Output = T::Output;
61
62 fn parse_value(&self, value: &Value) -> Result<T::Output, VldError> {
63 self.schema().parse_value(value)
64 }
65}