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}