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