miniconf/
key.rs

1use core::{iter::Fuse, num::NonZero};
2
3use serde::Serialize;
4
5use crate::Traversal;
6
7/// Data to look up field names and convert to indices
8///
9/// This struct used together with [`crate::TreeKey`].
10#[derive(Clone, Debug, PartialEq, PartialOrd, Hash, Serialize)]
11pub enum KeyLookup {
12    /// Named children
13    Named(&'static [&'static str]),
14    /// Numbered heterogeneous children
15    Numbered(NonZero<usize>),
16    /// Homogeneous numbered children
17    Homogeneous(NonZero<usize>),
18}
19
20impl KeyLookup {
21    /// Return a named KeyLookup
22    #[inline]
23    pub const fn named(names: &'static [&'static str]) -> Self {
24        if names.is_empty() {
25            panic!("Must have at least one child");
26        }
27        Self::Named(names)
28    }
29
30    /// Return a homogenenous, unnamed KeyLookup
31    #[inline]
32    pub const fn homogeneous(len: usize) -> Self {
33        match NonZero::new(len) {
34            Some(len) => Self::Homogeneous(len),
35            None => panic!("Must have at least one child"),
36        }
37    }
38
39    /// Return a heterogeneous numbered KeyLookup
40    #[inline]
41    pub const fn numbered(len: usize) -> Self {
42        match NonZero::new(len) {
43            Some(len) => Self::Numbered(len),
44            None => panic!("Must have at least one child"),
45        }
46    }
47
48    /// Return the number of elements in the lookup
49    #[inline]
50    pub const fn len(&self) -> NonZero<usize> {
51        match self {
52            Self::Named(names) => match NonZero::new(names.len()) {
53                Some(len) => len,
54                None => panic!("Must have at least one child"),
55            },
56            Self::Numbered(len) | Self::Homogeneous(len) => *len,
57        }
58    }
59
60    /// Perform a index-to-name lookup
61    #[inline]
62    pub fn lookup(&self, index: usize) -> Result<Option<&'static str>, Traversal> {
63        match self {
64            Self::Named(names) => match names.get(index) {
65                Some(name) => Ok(Some(name)),
66                None => Err(Traversal::NotFound(1)),
67            },
68            Self::Numbered(len) | Self::Homogeneous(len) => {
69                if index >= len.get() {
70                    Err(Traversal::NotFound(1))
71                } else {
72                    Ok(None)
73                }
74            }
75        }
76    }
77}
78
79/// Convert a `&str` key into a node index on a `KeyLookup`
80pub trait Key {
81    /// Convert the key `self` to a `usize` index
82    fn find(&self, lookup: &KeyLookup) -> Result<usize, Traversal>;
83}
84
85impl<T: Key> Key for &T
86where
87    T: Key + ?Sized,
88{
89    #[inline]
90    fn find(&self, lookup: &KeyLookup) -> Result<usize, Traversal> {
91        (**self).find(lookup)
92    }
93}
94
95impl<T: Key> Key for &mut T
96where
97    T: Key + ?Sized,
98{
99    #[inline]
100    fn find(&self, lookup: &KeyLookup) -> Result<usize, Traversal> {
101        (**self).find(lookup)
102    }
103}
104
105// index
106macro_rules! impl_key_integer {
107    ($($t:ty)+) => {$(
108        impl Key for $t {
109            #[inline]
110            fn find(&self, lookup: &KeyLookup) -> Result<usize, Traversal> {
111                (*self)
112                    .try_into()
113                    .ok()
114                    .filter(|i| *i < lookup.len().get())
115                    .ok_or(Traversal::NotFound(1))
116            }
117        }
118    )+};
119}
120impl_key_integer!(usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128);
121
122// name
123impl Key for str {
124    #[inline]
125    fn find(&self, lookup: &KeyLookup) -> Result<usize, Traversal> {
126        match lookup {
127            KeyLookup::Named(names) => names.iter().position(|n| *n == self),
128            KeyLookup::Homogeneous(len) | KeyLookup::Numbered(len) => {
129                self.parse().ok().filter(|i| *i < len.get())
130            }
131        }
132        .ok_or(Traversal::NotFound(1))
133    }
134}
135
136/// Capability to yield and look up [`Key`]s
137pub trait Keys {
138    /// Look up the next key in a [`KeyLookup`] and convert to `usize` index.
139    ///
140    /// This must be fused (like [`core::iter::FusedIterator`]).
141    fn next(&mut self, lookup: &KeyLookup) -> Result<usize, Traversal>;
142
143    /// Finalize the keys, ensure there are no more.
144    ///
145    /// This must be fused.
146    fn finalize(&mut self) -> Result<(), Traversal>;
147
148    /// Chain another `Keys` to this one.
149    #[inline]
150    fn chain<U: IntoKeys>(self, other: U) -> Chain<Self, U::IntoKeys>
151    where
152        Self: Sized,
153    {
154        Chain(self, other.into_keys())
155    }
156}
157
158impl<T> Keys for &mut T
159where
160    T: Keys + ?Sized,
161{
162    #[inline]
163    fn next(&mut self, lookup: &KeyLookup) -> Result<usize, Traversal> {
164        (**self).next(lookup)
165    }
166
167    #[inline]
168    fn finalize(&mut self) -> Result<(), Traversal> {
169        (**self).finalize()
170    }
171}
172
173/// [`Keys`]/[`IntoKeys`] for Iterators of [`Key`]
174#[derive(Debug, Clone)]
175#[repr(transparent)]
176pub struct KeysIter<T>(Fuse<T>);
177
178impl<T: Iterator> KeysIter<T> {
179    #[inline]
180    fn new(inner: T) -> Self {
181        Self(inner.fuse())
182    }
183}
184
185impl<T> Keys for KeysIter<T>
186where
187    T: Iterator,
188    T::Item: Key,
189{
190    #[inline]
191    fn next(&mut self, lookup: &KeyLookup) -> Result<usize, Traversal> {
192        self.0.next().ok_or(Traversal::TooShort(0))?.find(lookup)
193    }
194
195    #[inline]
196    fn finalize(&mut self) -> Result<(), Traversal> {
197        self.0
198            .next()
199            .is_none()
200            .then_some(())
201            .ok_or(Traversal::TooLong(0))
202    }
203}
204
205/// Be converted into a `Keys`
206pub trait IntoKeys {
207    /// The specific `Keys` implementor.
208    type IntoKeys: Keys;
209
210    /// Convert `self` into a `Keys` implementor.
211    fn into_keys(self) -> Self::IntoKeys;
212}
213
214impl<T> IntoKeys for T
215where
216    T: IntoIterator,
217    <T::IntoIter as Iterator>::Item: Key,
218{
219    type IntoKeys = KeysIter<T::IntoIter>;
220
221    #[inline]
222    fn into_keys(self) -> Self::IntoKeys {
223        KeysIter::new(self.into_iter())
224    }
225}
226
227impl<T> IntoKeys for KeysIter<T>
228where
229    T: Iterator,
230    T::Item: Key,
231{
232    type IntoKeys = KeysIter<T>;
233
234    #[inline]
235    fn into_keys(self) -> Self::IntoKeys {
236        self
237    }
238}
239
240/// Concatenate two `Keys` of different types
241pub struct Chain<T, U>(T, U);
242
243impl<T, U> Chain<T, U> {
244    /// Return a new concatenated `Keys`
245    #[inline]
246    pub fn new(t: T, u: U) -> Self {
247        Self(t, u)
248    }
249}
250
251impl<T: Keys, U: Keys> Keys for Chain<T, U> {
252    #[inline]
253    fn next(&mut self, lookup: &KeyLookup) -> Result<usize, Traversal> {
254        match self.0.next(lookup) {
255            Err(Traversal::TooShort(_)) => self.1.next(lookup),
256            ret => ret,
257        }
258    }
259
260    #[inline]
261    fn finalize(&mut self) -> Result<(), Traversal> {
262        self.0.finalize().and_then(|()| self.1.finalize())
263    }
264}
265
266impl<T: Keys, U: Keys> IntoKeys for Chain<T, U> {
267    type IntoKeys = Self;
268
269    #[inline]
270    fn into_keys(self) -> Self::IntoKeys {
271        self
272    }
273}