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