miniconf/impls/
key.rs

1use core::fmt::Write;
2
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5
6use serde::{Deserialize, Serialize};
7
8use crate::{DescendError, Internal, IntoKeys, Key, Schema, Seeded, Track, Transcode};
9
10// index
11macro_rules! impl_key_integer {
12    ($($t:ty)+) => {$(
13        impl Key for $t {
14            fn find(&self, internal: &Internal) -> Option<usize> {
15                (*self).try_into().ok().filter(|i| *i < internal.len().get())
16            }
17        }
18    )+};
19}
20impl_key_integer!(usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128);
21
22/// Indices of `usize` to identify a node in a `TreeSchema`
23#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
24pub struct Indices<T: ?Sized> {
25    len: usize,
26    data: T,
27}
28
29impl<T> Indices<T> {
30    /// Create a new `Indices`
31    pub fn new(data: T, len: usize) -> Self {
32        Self { len, data }
33    }
34
35    /// The length of the indices keys
36    pub fn len(&self) -> usize {
37        self.len
38    }
39
40    /// See [`Self::len()`]
41    pub fn is_empty(&self) -> bool {
42        self.len == 0
43    }
44
45    /// Split indices into data and length
46    pub fn into_inner(self) -> (T, usize) {
47        (self.data, self.len)
48    }
49}
50
51impl<T> From<T> for Indices<T> {
52    fn from(value: T) -> Self {
53        Self {
54            len: 0,
55            data: value,
56        }
57    }
58}
59
60impl<U, T: AsRef<[U]> + ?Sized> AsRef<[U]> for Indices<T> {
61    fn as_ref(&self) -> &[U] {
62        &self.data.as_ref()[..self.len]
63    }
64}
65
66impl<'a, U, T: ?Sized> IntoIterator for &'a Indices<T>
67where
68    &'a T: IntoIterator<Item = U>,
69{
70    type Item = U;
71
72    type IntoIter = core::iter::Take<<&'a T as IntoIterator>::IntoIter>;
73
74    fn into_iter(self) -> Self::IntoIter {
75        (&self.data).into_iter().take(self.len)
76    }
77}
78
79impl<T: AsMut<[usize]> + ?Sized> Transcode for Indices<T> {
80    type Error = <[usize] as Transcode>::Error;
81
82    fn transcode(
83        &mut self,
84        schema: &Schema,
85        keys: impl IntoKeys,
86    ) -> Result<(), DescendError<Self::Error>> {
87        let mut slic = Track::new(self.data.as_mut());
88        let ret = slic.transcode(schema, keys);
89        self.len = slic.depth();
90        ret
91    }
92}
93
94impl<T: Default> Seeded for Indices<T> {
95    type Seed = ();
96    const DEFAULT_SEED: Self::Seed = ();
97
98    fn from_seed(_: &Self::Seed) -> Self {
99        Self::from(T::default())
100    }
101}
102
103macro_rules! impl_transcode_slice {
104    ($($t:ty)+) => {$(
105        impl Transcode for [$t] {
106            type Error = ();
107
108            fn transcode(&mut self, schema: &Schema, keys: impl IntoKeys) -> Result<(), DescendError<Self::Error>> {
109                let mut it = self.iter_mut();
110                schema.descend(keys.into_keys(), |_meta, idx_schema| {
111                    if let Some((index, internal)) = idx_schema {
112                        debug_assert!(internal.len().get() <= <$t>::MAX as _);
113                        let i = index.try_into().or(Err(()))?;
114                        let idx = it.next().ok_or(())?;
115                        *idx = i;
116                    }
117                    Ok(())
118                })
119            }
120        }
121    )+};
122}
123impl_transcode_slice!(usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128);
124
125#[cfg(feature = "alloc")]
126impl<T> Transcode for Vec<T>
127where
128    usize: TryInto<T>,
129{
130    type Error = <usize as TryInto<T>>::Error;
131
132    fn transcode(
133        &mut self,
134        schema: &Schema,
135        keys: impl IntoKeys,
136    ) -> Result<(), DescendError<Self::Error>> {
137        schema.descend(keys.into_keys(), |_meta, idx_schema| {
138            if let Some((index, _schema)) = idx_schema {
139                self.push(index.try_into()?);
140            }
141            Ok(())
142        })
143    }
144}
145
146#[cfg(feature = "alloc")]
147impl<T> Seeded for Vec<T> {
148    type Seed = ();
149    const DEFAULT_SEED: Self::Seed = ();
150
151    fn from_seed(_: &Self::Seed) -> Self {
152        Self::default()
153    }
154}
155
156#[cfg(feature = "heapless")]
157impl<T, const N: usize> Transcode for heapless::Vec<T, N>
158where
159    usize: TryInto<T>,
160{
161    type Error = ();
162
163    fn transcode(
164        &mut self,
165        schema: &Schema,
166        keys: impl IntoKeys,
167    ) -> Result<(), DescendError<Self::Error>> {
168        schema.descend(keys.into_keys(), |_meta, idx_schema| {
169            if let Some((index, _schema)) = idx_schema {
170                let i = index.try_into().or(Err(()))?;
171                self.push(i).or(Err(()))?;
172            }
173            Ok(())
174        })
175    }
176}
177
178#[cfg(feature = "heapless")]
179impl<T, const N: usize> Seeded for heapless::Vec<T, N> {
180    type Seed = ();
181    const DEFAULT_SEED: Self::Seed = ();
182
183    fn from_seed(_: &Self::Seed) -> Self {
184        Self::default()
185    }
186}
187
188#[cfg(feature = "heapless-09")]
189impl<T, const N: usize> Transcode for heapless_09::Vec<T, N>
190where
191    usize: TryInto<T>,
192{
193    type Error = ();
194
195    fn transcode(
196        &mut self,
197        schema: &Schema,
198        keys: impl IntoKeys,
199    ) -> Result<(), DescendError<Self::Error>> {
200        schema.descend(keys.into_keys(), |_meta, idx_schema| {
201            if let Some((index, _schema)) = idx_schema {
202                let i = index.try_into().or(Err(()))?;
203                self.push(i).or(Err(()))?;
204            }
205            Ok(())
206        })
207    }
208}
209
210#[cfg(feature = "heapless-09")]
211impl<T, const N: usize> Seeded for heapless_09::Vec<T, N> {
212    type Seed = ();
213    const DEFAULT_SEED: Self::Seed = ();
214
215    fn from_seed(_: &Self::Seed) -> Self {
216        Self::default()
217    }
218}
219
220////////////////////////////////////////////////////////////////////
221
222// name
223impl Key for str {
224    fn find(&self, internal: &Internal) -> Option<usize> {
225        internal.get_index(self)
226    }
227}
228
229/// Path with named keys separated by a separator char
230///
231/// The path will either be empty or start with the separator.
232///
233/// * `path: T`: A `Write` to write the separators and node names into during `Transcode`.
234///   See also [Schema::transcode()] and `Shape.max_length` for upper bounds
235///   on path length. Can also be a `AsRef<str>` to implement `IntoKeys` (see [`crate::KeysIter`]).
236/// * `separator`: The path hierarchy separator to be inserted before each name,
237///   e.g. `'/'`.
238#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
239pub struct Path<T> {
240    /// The underlying path buffer or string.
241    pub path: T,
242    /// The path hierarchy separator.
243    pub separator: char,
244}
245
246impl<T> Default for Path<T>
247where
248    T: Default,
249{
250    fn default() -> Self {
251        Self {
252            path: T::default(),
253            separator: '/',
254        }
255    }
256}
257
258impl<T> Path<T> {
259    /// Create a new `Path`.
260    pub const fn new(path: T, separator: char) -> Self {
261        Self { path, separator }
262    }
263
264    /// The path hierarchy separator
265    pub const fn separator(&self) -> char {
266        self.separator
267    }
268
269    /// Extract just the path
270    pub fn into_inner(self) -> T {
271        self.path
272    }
273}
274
275impl<T: AsRef<str>> AsRef<str> for Path<T> {
276    fn as_ref(&self) -> &str {
277        self.path.as_ref()
278    }
279}
280
281impl<T: core::fmt::Display> core::fmt::Display for Path<T> {
282    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
283        self.path.fmt(f)
284    }
285}
286
287impl<T: Default> Seeded for Path<T> {
288    type Seed = char;
289    const DEFAULT_SEED: Self::Seed = '/';
290
291    fn from_seed(seed: &Self::Seed) -> Self {
292        Self::new(T::default(), *seed)
293    }
294}
295
296/// Const-specialized path with named keys separated by a const separator.
297#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
298#[repr(transparent)]
299#[serde(transparent)]
300pub struct ConstPath<T, const S: char>(pub T);
301
302impl<T, const S: char> ConstPath<T, S> {
303    /// The path hierarchy separator.
304    pub const fn separator(&self) -> char {
305        S
306    }
307
308    /// Extract just the path.
309    pub fn into_inner(self) -> T {
310        self.0
311    }
312}
313
314impl<T: AsRef<str>, const S: char> AsRef<str> for ConstPath<T, S> {
315    fn as_ref(&self) -> &str {
316        self.0.as_ref()
317    }
318}
319
320impl<T: core::fmt::Display, const S: char> core::fmt::Display for ConstPath<T, S> {
321    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
322        self.0.fmt(f)
323    }
324}
325
326impl<T: Default, const S: char> Seeded for ConstPath<T, S> {
327    type Seed = ();
328    const DEFAULT_SEED: Self::Seed = ();
329
330    fn from_seed(_: &Self::Seed) -> Self {
331        Self::default()
332    }
333}
334
335fn split_path(path: &str, separator: char) -> (&str, Option<&str>) {
336    let pos = path
337        .chars()
338        .map_while(|c| (c != separator).then_some(c.len_utf8()))
339        .sum();
340    let (left, right) = path.split_at(pos);
341    (left, right.get(separator.len_utf8()..))
342}
343
344/// String split/skip wrapper, smaller/simpler than `.split(separator).skip(1)`
345#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
346pub struct PathIter<'a> {
347    path: Option<&'a str>,
348    separator: char,
349}
350
351impl<'a> PathIter<'a> {
352    /// Create a new `PathIter`.
353    pub fn new(path: Option<&'a str>, separator: char) -> Self {
354        Self { path, separator }
355    }
356
357    /// Create a new `PathIter` starting at the root.
358    ///
359    /// This calls `next()` once to pop everything up to and including the first separator.
360    pub fn root(path: &'a str, separator: char) -> Self {
361        let mut s = Self::new(Some(path), separator);
362        // Skip the first part to disambiguate between
363        // the one-Key Keys `[""]` and the zero-Key Keys `[]`.
364        // This is relevant in the case of e.g. `Option` and newtypes.
365        // See the corresponding unittests (`just_option`).
366        // It implies that Paths start with the separator
367        // or are empty. Everything before the first separator is ignored.
368        // This also means that paths can always be concatenated without having to
369        // worry about adding/trimming leading or trailing separators.
370        s.next();
371        s
372    }
373}
374
375impl<'a> Iterator for PathIter<'a> {
376    type Item = &'a str;
377
378    fn next(&mut self) -> Option<Self::Item> {
379        let (left, right) = split_path(self.path?, self.separator);
380        self.path = right;
381        Some(left)
382    }
383}
384
385impl core::iter::FusedIterator for PathIter<'_> {}
386
387/// Const-specialized string split/skip wrapper.
388#[repr(transparent)]
389#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
390#[serde(transparent)]
391pub struct ConstPathIter<'a, const S: char>(Option<&'a str>);
392
393impl<'a, const S: char> ConstPathIter<'a, S> {
394    /// Create a new const-specialized `PathIter`.
395    pub fn new(path: Option<&'a str>) -> Self {
396        Self(path)
397    }
398
399    /// Create a new const-specialized `PathIter` starting at the root.
400    pub fn root(path: &'a str) -> Self {
401        let mut s = Self::new(Some(path));
402        s.next();
403        s
404    }
405}
406
407impl<'a, const S: char> Iterator for ConstPathIter<'a, S> {
408    type Item = &'a str;
409
410    fn next(&mut self) -> Option<Self::Item> {
411        let s = self.0?;
412        if S.is_ascii() {
413            if let Some(i) = s.as_bytes().iter().position(|b| *b == S as u8) {
414                self.0 = s.get(i + 1..);
415                s.get(..i)
416            } else {
417                self.0 = None;
418                Some(s)
419            }
420        } else {
421            let (left, right) = split_path(s, S);
422            self.0 = right;
423            Some(left)
424        }
425    }
426}
427
428impl<const S: char> core::iter::FusedIterator for ConstPathIter<'_, S> {}
429
430impl<'a, T: AsRef<str> + ?Sized> IntoKeys for Path<&'a T> {
431    type IntoKeys = <PathIter<'a> as IntoKeys>::IntoKeys;
432
433    fn into_keys(self) -> Self::IntoKeys {
434        PathIter::root(self.path.as_ref(), self.separator).into_keys()
435    }
436}
437
438impl<'a, T: AsRef<str>> IntoKeys for &'a Path<T> {
439    type IntoKeys = <Path<&'a str> as IntoKeys>::IntoKeys;
440
441    fn into_keys(self) -> Self::IntoKeys {
442        PathIter::root(self.path.as_ref(), self.separator).into_keys()
443    }
444}
445
446impl<'a, T: AsRef<str> + ?Sized, const S: char> IntoKeys for ConstPath<&'a T, S> {
447    type IntoKeys = <ConstPathIter<'a, S> as IntoKeys>::IntoKeys;
448
449    fn into_keys(self) -> Self::IntoKeys {
450        ConstPathIter::root(self.0.as_ref()).into_keys()
451    }
452}
453
454impl<'a, T: AsRef<str>, const S: char> IntoKeys for &'a ConstPath<T, S> {
455    type IntoKeys = <ConstPath<&'a str, S> as IntoKeys>::IntoKeys;
456
457    fn into_keys(self) -> Self::IntoKeys {
458        ConstPathIter::root(self.0.as_ref()).into_keys()
459    }
460}
461
462impl<T: Write> Transcode for Path<T> {
463    type Error = core::fmt::Error;
464
465    fn transcode(
466        &mut self,
467        schema: &Schema,
468        keys: impl IntoKeys,
469    ) -> Result<(), DescendError<Self::Error>> {
470        schema.descend(keys.into_keys(), |_meta, idx_schema| {
471            if let Some((index, internal)) = idx_schema {
472                self.path.write_char(self.separator)?;
473                let mut buf = itoa::Buffer::new();
474                let name = internal
475                    .get_name(index)
476                    .unwrap_or_else(|| buf.format(index));
477                debug_assert!(!name.contains(self.separator));
478                self.path.write_str(name)
479            } else {
480                Ok(())
481            }
482        })
483    }
484}
485
486impl<T: Write, const S: char> Transcode for ConstPath<T, S> {
487    type Error = core::fmt::Error;
488
489    fn transcode(
490        &mut self,
491        schema: &Schema,
492        keys: impl IntoKeys,
493    ) -> Result<(), DescendError<Self::Error>> {
494        schema.descend(keys.into_keys(), |_meta, idx_schema| {
495            if let Some((index, internal)) = idx_schema {
496                self.0.write_char(S)?;
497                let mut buf = itoa::Buffer::new();
498                let name = internal
499                    .get_name(index)
500                    .unwrap_or_else(|| buf.format(index));
501                debug_assert!(!name.contains(S));
502                self.0.write_str(name)
503            } else {
504                Ok(())
505            }
506        })
507    }
508}
509
510#[cfg(test)]
511mod test {
512    use super::*;
513
514    #[test]
515    fn strsplit() {
516        use heapless_09::Vec;
517        for p in ["/d/1", "/a/bccc//d/e/", "", "/", "a/b", "a"] {
518            let a: Vec<_, 10> = PathIter::root(p, '/').collect();
519            let b: Vec<_, 10> = p.split('/').skip(1).collect();
520            assert_eq!(a, b);
521        }
522    }
523
524    #[test]
525    fn ascii_strsplit() {
526        use heapless_09::Vec;
527        for p in ["/d/1", "/a/bccc//d/e/", "", "/", "a/b", "a"] {
528            let a: Vec<_, 10> = ConstPathIter::<'/'>::root(p).collect();
529            let b: Vec<_, 10> = p.split('/').skip(1).collect();
530            assert_eq!(a, b);
531        }
532    }
533}