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, Track, Transcode};
9
10// index
11macro_rules! impl_key_integer {
12    ($($t:ty)+) => {$(
13        impl Key for $t {
14            #[inline]
15            fn find(&self, internal: &Internal) -> Option<usize> {
16                (*self).try_into().ok().filter(|i| *i < internal.len().get())
17            }
18        }
19    )+};
20}
21impl_key_integer!(usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128);
22
23/// Indices of `usize` to identify a node in a `TreeSchema`
24#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
25pub struct Indices<T: ?Sized> {
26    len: usize,
27    data: T,
28}
29
30impl<T> Indices<T> {
31    /// Create a new `Indices`
32    pub fn new(data: T, len: usize) -> Self {
33        Self { len, data }
34    }
35
36    /// The length of the indices keys
37    #[inline]
38    pub fn len(&self) -> usize {
39        self.len
40    }
41
42    /// See [`Self::len()`]
43    #[inline]
44    pub fn is_empty(&self) -> bool {
45        self.len == 0
46    }
47
48    /// Split indices into data and length
49    pub fn into_inner(self) -> (T, usize) {
50        (self.data, self.len)
51    }
52}
53
54impl<T> From<T> for Indices<T> {
55    #[inline]
56    fn from(value: T) -> Self {
57        Self {
58            len: 0,
59            data: value,
60        }
61    }
62}
63
64impl<U, T: AsRef<[U]> + ?Sized> AsRef<[U]> for Indices<T> {
65    #[inline]
66    fn as_ref(&self) -> &[U] {
67        &self.data.as_ref()[..self.len]
68    }
69}
70
71impl<'a, U, T: ?Sized> IntoIterator for &'a Indices<T>
72where
73    &'a T: IntoIterator<Item = U>,
74{
75    type Item = U;
76
77    type IntoIter = core::iter::Take<<&'a T as IntoIterator>::IntoIter>;
78
79    #[inline]
80    fn into_iter(self) -> Self::IntoIter {
81        (&self.data).into_iter().take(self.len)
82    }
83}
84
85impl<T: AsMut<[usize]> + ?Sized> Transcode for Indices<T> {
86    type Error = <[usize] as Transcode>::Error;
87
88    fn transcode(
89        &mut self,
90        schema: &Schema,
91        keys: impl IntoKeys,
92    ) -> Result<(), DescendError<Self::Error>> {
93        let mut slic = Track::new(self.data.as_mut());
94        let ret = slic.transcode(schema, keys);
95        self.len = slic.depth();
96        ret
97    }
98}
99
100macro_rules! impl_transcode_slice {
101    ($($t:ty)+) => {$(
102        impl Transcode for [$t] {
103            type Error = ();
104
105            fn transcode(&mut self, schema: &Schema, keys: impl IntoKeys) -> Result<(), DescendError<Self::Error>> {
106                let mut it = self.iter_mut();
107                schema.descend(keys.into_keys(), |_meta, idx_schema| {
108                    if let Some((index, internal)) = idx_schema {
109                        debug_assert!(internal.len().get() <= <$t>::MAX as _);
110                        let i = index.try_into().or(Err(()))?;
111                        let idx = it.next().ok_or(())?;
112                        *idx = i;
113                    }
114                    Ok(())
115                })
116            }
117        }
118    )+};
119}
120impl_transcode_slice!(usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128);
121
122#[cfg(feature = "alloc")]
123impl<T> Transcode for Vec<T>
124where
125    usize: TryInto<T>,
126{
127    type Error = <usize as TryInto<T>>::Error;
128
129    fn transcode(
130        &mut self,
131        schema: &Schema,
132        keys: impl IntoKeys,
133    ) -> Result<(), DescendError<Self::Error>> {
134        schema.descend(keys.into_keys(), |_meta, idx_schema| {
135            if let Some((index, _schema)) = idx_schema {
136                self.push(index.try_into()?);
137            }
138            Ok(())
139        })
140    }
141}
142
143#[cfg(feature = "heapless")]
144impl<T, const N: usize> Transcode for heapless::Vec<T, N>
145where
146    usize: TryInto<T>,
147{
148    type Error = ();
149
150    fn transcode(
151        &mut self,
152        schema: &Schema,
153        keys: impl IntoKeys,
154    ) -> Result<(), DescendError<Self::Error>> {
155        schema.descend(keys.into_keys(), |_meta, idx_schema| {
156            if let Some((index, _schema)) = idx_schema {
157                let i = index.try_into().or(Err(()))?;
158                self.push(i).or(Err(()))?;
159            }
160            Ok(())
161        })
162    }
163}
164
165////////////////////////////////////////////////////////////////////
166
167// name
168impl Key for str {
169    #[inline]
170    fn find(&self, internal: &Internal) -> Option<usize> {
171        internal.get_index(self)
172    }
173}
174
175/// Path with named keys separated by a separator char
176///
177/// The path will either be empty or start with the separator.
178///
179/// * `path: T`: A `Write` to write the separators and node names into during `Transcode`.
180///   See also [Schema::transcode()] and `Shape.max_length` for upper bounds
181///   on path length. Can also be a `AsRef<str>` to implement `IntoKeys` (see [`crate::KeysIter`]).
182/// * `const S: char`: The path hierarchy separator to be inserted before each name,
183///   e.g. `'/'`.
184#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
185#[repr(transparent)]
186#[serde(transparent)]
187pub struct Path<T: ?Sized, const S: char>(pub T);
188
189impl<T: ?Sized, const S: char> Path<T, S> {
190    /// The path hierarchy separator
191    #[inline]
192    pub const fn separator(&self) -> char {
193        S
194    }
195}
196
197impl<T, const S: char> Path<T, S> {
198    /// Extract just the path
199    #[inline]
200    pub fn into_inner(self) -> T {
201        self.0
202    }
203}
204
205impl<T: AsRef<str> + ?Sized, const S: char> AsRef<str> for Path<T, S> {
206    fn as_ref(&self) -> &str {
207        self.0.as_ref()
208    }
209}
210
211impl<T: core::fmt::Display, const S: char> core::fmt::Display for Path<T, S> {
212    #[inline]
213    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214        self.0.fmt(f)
215    }
216}
217
218/// String split/skip wrapper, smaller/simpler than `.split(S).skip(1)`
219#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
220pub struct PathIter<'a> {
221    data: Option<&'a str>,
222    sep: char,
223}
224
225impl<'a> PathIter<'a> {
226    /// Create a new `PathIter`
227    #[inline]
228    pub fn new(data: Option<&'a str>, sep: char) -> Self {
229        Self { data, sep }
230    }
231
232    /// Create a new `PathIter` starting at the root.
233    ///
234    /// This calls `next()` once to pop everything up to and including the first separator.
235    #[inline]
236    pub fn root(data: &'a str, sep: char) -> Self {
237        let mut s = Self::new(Some(data), sep);
238        // Skip the first part to disambiguate between
239        // the one-Key Keys `[""]` and the zero-Key Keys `[]`.
240        // This is relevant in the case of e.g. `Option` and newtypes.
241        // See the corresponding unittests (`just_option`).
242        // It implies that Paths start with the separator
243        // or are empty. Everything before the first separator is ignored.
244        // This also means that paths can always be concatenated without having to
245        // worry about adding/trimming leading or trailing separators.
246        s.next();
247        s
248    }
249}
250
251impl<'a> Iterator for PathIter<'a> {
252    type Item = &'a str;
253
254    fn next(&mut self) -> Option<Self::Item> {
255        self.data.map(|s| {
256            let pos = s
257                .chars()
258                .map_while(|c| (c != self.sep).then_some(c.len_utf8()))
259                .sum();
260            let (left, right) = s.split_at(pos);
261            self.data = right.get(self.sep.len_utf8()..);
262            left
263        })
264    }
265}
266
267impl core::iter::FusedIterator for PathIter<'_> {}
268
269impl<'a, T: AsRef<str> + ?Sized, const S: char> IntoKeys for Path<&'a T, S> {
270    type IntoKeys = <PathIter<'a> as IntoKeys>::IntoKeys;
271
272    #[inline]
273    fn into_keys(self) -> Self::IntoKeys {
274        PathIter::root(self.0.as_ref(), S).into_keys()
275    }
276}
277
278impl<'a, T: AsRef<str> + ?Sized, const S: char> IntoKeys for &'a Path<T, S> {
279    type IntoKeys = <Path<&'a str, S> as IntoKeys>::IntoKeys;
280
281    #[inline]
282    fn into_keys(self) -> Self::IntoKeys {
283        PathIter::root(self.0.as_ref(), S).into_keys()
284    }
285}
286
287impl<T: Write + ?Sized, const S: char> Transcode for Path<T, S> {
288    type Error = core::fmt::Error;
289
290    fn transcode(
291        &mut self,
292        schema: &Schema,
293        keys: impl IntoKeys,
294    ) -> Result<(), DescendError<Self::Error>> {
295        schema.descend(keys.into_keys(), |_meta, idx_schema| {
296            if let Some((index, internal)) = idx_schema {
297                self.0.write_char(S)?;
298                let mut buf = itoa::Buffer::new();
299                let name = internal
300                    .get_name(index)
301                    .unwrap_or_else(|| buf.format(index));
302                debug_assert!(!name.contains(S));
303                self.0.write_str(name)
304            } else {
305                Ok(())
306            }
307        })
308    }
309}
310
311#[cfg(test)]
312mod test {
313    use super::*;
314
315    #[test]
316    fn strsplit() {
317        use heapless::Vec;
318        for p in ["/d/1", "/a/bccc//d/e/", "", "/", "a/b", "a"] {
319            let a: Vec<_, 10> = PathIter::root(p, '/').collect();
320            let b: Vec<_, 10> = p.split('/').skip(1).collect();
321            assert_eq!(a, b);
322        }
323    }
324}