miniconf/
schema.rs

1use core::{convert::Infallible, num::NonZero};
2use serde::Serialize;
3
4use crate::{DescendError, ExactSize, IntoKeys, KeyError, Keys, NodeIter, Shape, Transcode};
5
6/// A numbered schema item
7#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize)]
8pub struct Numbered {
9    /// The child schema
10    pub schema: &'static Schema,
11    /// The outer metadata
12    pub meta: Option<Meta>,
13}
14
15impl Numbered {
16    /// Create a new Numbered schema item with no outer metadata.
17    pub const fn new(schema: &'static Schema) -> Self {
18        Self { meta: None, schema }
19    }
20}
21
22/// A named schema item
23#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize)]
24pub struct Named {
25    /// The name of the item
26    pub name: &'static str,
27    /// The child schema
28    pub schema: &'static Schema,
29    /// The outer metadata
30    pub meta: Option<Meta>,
31}
32
33impl Named {
34    /// Create a new Named schema item with no outer metadata.
35    pub const fn new(name: &'static str, schema: &'static Schema) -> Self {
36        Self {
37            meta: None,
38            name,
39            schema,
40        }
41    }
42}
43
44/// A representative schema item for a homogeneous array
45#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize)]
46pub struct Homogeneous {
47    /// The number of items
48    pub len: NonZero<usize>,
49    /// The schema of the child nodes
50    pub schema: &'static Schema,
51    /// The outer metadata
52    pub meta: Option<Meta>,
53}
54
55impl Homogeneous {
56    /// Create a new Homogeneous schema item with no outer metadata.
57    pub const fn new(len: usize, schema: &'static Schema) -> Self {
58        Self {
59            meta: None,
60            len: NonZero::new(len).expect("Must have at least one child"),
61            schema,
62        }
63    }
64}
65
66/// An internal node with children
67///
68/// Always non-empty
69#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize)]
70pub enum Internal {
71    /// Named children
72    Named(&'static [Named]),
73    /// Numbered heterogeneous children
74    Numbered(&'static [Numbered]),
75    /// Homogeneous numbered children
76    Homogeneous(Homogeneous),
77}
78
79impl Internal {
80    /// Return the number of direct child nodes
81    pub const fn len(&self) -> NonZero<usize> {
82        match self {
83            Self::Named(n) => NonZero::new(n.len()).expect("Must have at least one child"),
84            Self::Numbered(n) => NonZero::new(n.len()).expect("Must have at least one child"),
85            Self::Homogeneous(h) => h.len,
86        }
87    }
88
89    /// Return the child schema at the given index
90    ///
91    /// # Panics
92    /// If the index is out of bounds
93    pub const fn get_schema(&self, idx: usize) -> &Schema {
94        match self {
95            Self::Named(nameds) => nameds[idx].schema,
96            Self::Numbered(numbereds) => numbereds[idx].schema,
97            Self::Homogeneous(homogeneous) => homogeneous.schema,
98        }
99    }
100
101    /// Return the outer metadata for the given child
102    ///
103    /// # Panics
104    /// If the index is out of bounds
105    pub const fn get_meta(&self, idx: usize) -> &Option<Meta> {
106        match self {
107            Internal::Named(nameds) => &nameds[idx].meta,
108            Internal::Numbered(numbereds) => &numbereds[idx].meta,
109            Internal::Homogeneous(homogeneous) => &homogeneous.meta,
110        }
111    }
112
113    /// Perform a index-to-name lookup
114    ///
115    /// If this succeeds with None, it's a numbered or homogeneous internal node and the
116    /// name is the formatted index.
117    ///
118    /// # Panics
119    /// If the index is out of bounds
120    pub const fn get_name(&self, idx: usize) -> Option<&str> {
121        if let Self::Named(n) = self {
122            Some(n[idx].name)
123        } else {
124            None
125        }
126    }
127
128    /// Perform a name-to-index lookup
129    pub fn get_index(&self, name: &str) -> Option<usize> {
130        match self {
131            Internal::Named(n) => n.iter().position(|n| n.name == name),
132            Internal::Numbered(n) => name.parse().ok().filter(|i| *i < n.len()),
133            Internal::Homogeneous(h, ..) => name.parse().ok().filter(|i| *i < h.len.get()),
134        }
135    }
136}
137
138/// The metadata type
139///
140/// A slice of key-value pairs
141#[cfg(feature = "meta-str")]
142pub type Meta = &'static [(&'static str, &'static str)];
143#[cfg(not(any(feature = "meta-str")))]
144#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize)]
145/// The metadata type
146///
147/// Uninhabited
148pub enum Meta {}
149
150/// Type of a node: leaf or internal
151#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Default)]
152pub struct Schema {
153    /// Inner metadata
154    pub meta: Option<Meta>,
155
156    /// Internal schemata
157    pub internal: Option<Internal>,
158}
159
160impl Schema {
161    /// A leaf without metadata
162    pub const LEAF: Self = Self {
163        meta: None,
164        internal: None,
165    };
166
167    /// Create a new internal node schema with named children and without innner metadata
168    pub const fn numbered(numbered: &'static [Numbered]) -> Self {
169        Self {
170            meta: None,
171            internal: Some(Internal::Numbered(numbered)),
172        }
173    }
174
175    /// Create a new internal node schema with numbered children and without innner metadata
176    pub const fn named(named: &'static [Named]) -> Self {
177        Self {
178            meta: None,
179            internal: Some(Internal::Named(named)),
180        }
181    }
182
183    /// Create a new internal node schema with homogenous children and without innner metadata
184    pub const fn homogeneous(homogeneous: Homogeneous) -> Self {
185        Self {
186            meta: None,
187            internal: Some(Internal::Homogeneous(homogeneous)),
188        }
189    }
190
191    /// Whether this node is a leaf
192    pub const fn is_leaf(&self) -> bool {
193        self.internal.is_none()
194    }
195
196    /// Number of child nodes
197    pub const fn len(&self) -> usize {
198        match &self.internal {
199            None => 0,
200            Some(i) => i.len().get(),
201        }
202    }
203
204    /// See [`Self::is_leaf()`]
205    pub const fn is_empty(&self) -> bool {
206        self.is_leaf()
207    }
208
209    /// Look up the next item from keys and return a child index
210    ///
211    /// # Panics
212    /// On a leaf Schema.
213    pub fn next(&self, mut keys: impl Keys) -> Result<usize, KeyError> {
214        keys.next(self.internal.as_ref().unwrap())
215    }
216
217    /// Traverse from the root to a leaf and call a function for each node.
218    ///
219    /// If a leaf is found early (`keys` being longer than required)
220    /// `Err(KeyError::TooLong)` is returned.
221    /// If `keys` is exhausted before reaching a leaf node,
222    /// `Err(KeyError::TooShort)` is returned.
223    ///
224    /// ```
225    /// # use core::convert::Infallible;
226    /// use miniconf::{IntoKeys, TreeSchema};
227    /// #[derive(TreeSchema)]
228    /// struct S {
229    ///     foo: u32,
230    ///     bar: [u16; 2],
231    /// };
232    /// let mut ret = [
233    ///     (S::SCHEMA, Some(1usize)),
234    ///     (<[u16; 2]>::SCHEMA, Some(0)),
235    ///     (u16::SCHEMA, None),
236    /// ].into_iter();
237    /// let func = |schema, idx_internal: Option<_>| {
238    ///     assert_eq!(ret.next().unwrap(), (schema, idx_internal.map(|(idx, _)| idx)));
239    ///     Ok::<_, Infallible>(())
240    /// };
241    /// assert_eq!(S::SCHEMA.descend(["bar", "0"].into_keys(), func), Ok(()));
242    /// ```
243    ///
244    /// # Args
245    /// * `keys`: A `Key`s identifying the node.
246    /// * `func`: A `FnMut` to be called for each (internal and leaf) node on the path.
247    ///   Its arguments are outer schema and optionally the inner index and internal schema.
248    ///   Returning `Err(E)` aborts the traversal.
249    ///   Returning `Ok(T)` continues the downward traversal.
250    ///
251    /// # Returns
252    /// The leaf `func` call return value.
253    pub fn descend<'a, T, E>(
254        &'a self,
255        mut keys: impl Keys,
256        mut func: impl FnMut(&'a Self, Option<(usize, &'a Internal)>) -> Result<T, E>,
257    ) -> Result<T, DescendError<E>> {
258        let mut schema = self;
259        while let Some(internal) = schema.internal.as_ref() {
260            let idx = keys.next(internal)?;
261            func(schema, Some((idx, internal))).map_err(DescendError::Inner)?;
262            schema = internal.get_schema(idx);
263        }
264        keys.finalize()?;
265        func(schema, None).map_err(DescendError::Inner)
266    }
267
268    /// Look up outer and inner metadata given keys.
269    pub fn get_meta(
270        &self,
271        keys: impl IntoKeys,
272    ) -> Result<(Option<&Option<Meta>>, &Option<Meta>), KeyError> {
273        let mut outer = None;
274        let mut inner = &self.meta;
275        self.descend(keys.into_keys(), |schema, idx_internal| {
276            if let Some((idx, internal)) = idx_internal {
277                outer = Some(internal.get_meta(idx));
278            }
279            inner = &schema.meta;
280            Ok::<_, Infallible>(())
281        })
282        .map_err(|e| e.try_into().unwrap())?;
283        Ok((outer, inner))
284    }
285
286    /// Get the schema of the node identified by keys.
287    pub fn get(&self, keys: impl IntoKeys) -> Result<&Self, KeyError> {
288        let mut schema = self;
289        self.descend(keys.into_keys(), |s, _idx_internal| {
290            schema = s;
291            Ok::<_, Infallible>(())
292        })
293        .map_err(|e| e.try_into().unwrap())?;
294        Ok(schema)
295    }
296
297    /// Transcode keys to a new keys type representation
298    ///
299    /// In order to not require `N: Default`, use [`Transcode::transcode`] on
300    /// an existing `&mut N`.
301    ///
302    /// ```
303    /// use miniconf::{Indices, JsonPath, Packed, Track, Short, Path, TreeSchema};
304    /// #[derive(TreeSchema)]
305    /// struct S {
306    ///     foo: u32,
307    ///     bar: [u16; 5],
308    /// };
309    ///
310    /// let idx = [1, 1];
311    /// let sch = S::SCHEMA;
312    ///
313    /// let path = sch.transcode::<Path<String, '/'>>(idx).unwrap();
314    /// assert_eq!(path.0.as_str(), "/bar/1");
315    /// let path = sch.transcode::<JsonPath<String>>(idx).unwrap();
316    /// assert_eq!(path.0.as_str(), ".bar[1]");
317    /// let indices = sch.transcode::<Indices<[usize; 2]>>(&path).unwrap();
318    /// assert_eq!(indices.as_ref(), idx);
319    /// let indices = sch.transcode::<Indices<[usize; 2]>>(["bar", "1"]).unwrap();
320    /// assert_eq!(indices.as_ref(), [1, 1]);
321    /// let packed = sch.transcode::<Packed>(["bar", "4"]).unwrap();
322    /// assert_eq!(packed.into_lsb().get(), 0b1_1_100);
323    /// let path = sch.transcode::<Path<String, '/'>>(packed).unwrap();
324    /// assert_eq!(path.0.as_str(), "/bar/4");
325    /// let node = sch.transcode::<Short<Track<()>>>(&path).unwrap();
326    /// assert_eq!((node.leaf(), node.inner().depth()), (true, 2));
327    /// ```
328    ///
329    /// # Args
330    /// * `keys`: `IntoKeys` to identify the node.
331    ///
332    /// # Returns
333    /// Transcoded target and node information on success
334    pub fn transcode<N: Transcode + Default>(
335        &self,
336        keys: impl IntoKeys,
337    ) -> Result<N, DescendError<N::Error>> {
338        let mut target = N::default();
339        target.transcode(self, keys)?;
340        Ok(target)
341    }
342
343    /// The Shape of the schema
344    pub const fn shape(&self) -> Shape {
345        Shape::new(self)
346    }
347
348    /// Return an iterator over nodes of a given type
349    ///
350    /// This is a walk of all leaf nodes.
351    /// The iterator will walk all paths, including those that may be absent at
352    /// runtime (see [`crate::TreeSchema#option`]).
353    /// The iterator has an exact and trusted `size_hint()`.
354    /// The `D` const generic of [`NodeIter`] is the maximum key depth.
355    ///
356    /// ```
357    /// use miniconf::{Indices, JsonPath, Short, Track, Packed, Path, TreeSchema};
358    /// #[derive(TreeSchema)]
359    /// struct S {
360    ///     foo: u32,
361    ///     bar: [u16; 2],
362    /// };
363    /// const MAX_DEPTH: usize = S::SCHEMA.shape().max_depth;
364    /// assert_eq!(MAX_DEPTH, 2);
365    ///
366    /// let paths: Vec<_> = S::SCHEMA.nodes::<Path<String, '/'>, MAX_DEPTH>()
367    ///     .map(|p| p.unwrap().into_inner())
368    ///     .collect();
369    /// assert_eq!(paths, ["/foo", "/bar/0", "/bar/1"]);
370    ///
371    /// let paths: Vec<_> = S::SCHEMA.nodes::<JsonPath<String>, MAX_DEPTH>()
372    ///     .map(|p| p.unwrap().into_inner())
373    ///     .collect();
374    /// assert_eq!(paths, [".foo", ".bar[0]", ".bar[1]"]);
375    ///
376    /// let indices: Vec<_> = S::SCHEMA.nodes::<Indices<[_; 2]>, MAX_DEPTH>()
377    ///     .map(|p| p.unwrap().into_inner())
378    ///     .collect();
379    /// assert_eq!(indices, [([0, 0], 1), ([1, 0], 2), ([1, 1], 2)]);
380    ///
381    /// let packed: Vec<_> = S::SCHEMA.nodes::<Packed, MAX_DEPTH>()
382    ///     .map(|p| p.unwrap().into_lsb().get())
383    ///     .collect();
384    /// assert_eq!(packed, [0b1_0, 0b1_1_0, 0b1_1_1]);
385    ///
386    /// let nodes: Vec<_> = S::SCHEMA.nodes::<Short<Track<()>>, MAX_DEPTH>()
387    ///     .map(|p| {
388    ///         let p = p.unwrap();
389    ///         (p.leaf(), p.inner().depth())
390    ///     })
391    ///     .collect();
392    /// assert_eq!(nodes, [(true, 1), (true, 2), (true, 2)]);
393    /// ```
394    pub const fn nodes<N, const D: usize>(&'static self) -> ExactSize<NodeIter<N, D>> {
395        NodeIter::exact_size(self)
396    }
397}