Skip to main content

config/
settings.rs

1use crate::{context, path, Merge};
2use indexmap::{
3    map::{self, Entry::*},
4    IndexMap,
5};
6use std::{
7    borrow::{
8        Borrow,
9        Cow::{self, *},
10    },
11    cmp::Ordering,
12    fmt::{Debug, Display, Formatter, Result},
13    hash::{Hash, Hasher},
14    mem::replace,
15};
16
17#[derive(Clone, Eq)]
18struct Key<'a>(Cow<'a, str>);
19
20impl PartialEq for Key<'_> {
21    #[inline]
22    fn eq(&self, other: &Self) -> bool {
23        self.0.eq_ignore_ascii_case(&other.0)
24    }
25}
26
27impl Ord for Key<'_> {
28    fn cmp(&self, other: &Self) -> Ordering {
29        let mut order = self.0.len().cmp(&other.0.len());
30
31        if order != Ordering::Equal {
32            return order;
33        }
34
35        for (a, b) in self.0.chars().zip(other.0.chars()) {
36            order = a.to_ascii_uppercase().cmp(&b.to_ascii_uppercase());
37
38            if order != Ordering::Equal {
39                break;
40            }
41        }
42
43        order
44    }
45}
46
47impl PartialOrd for Key<'_> {
48    #[inline]
49    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
50        Some(self.cmp(other))
51    }
52}
53
54impl Hash for Key<'_> {
55    fn hash<H: Hasher>(&self, state: &mut H) {
56        self.0
57            .as_ref()
58            .chars()
59            .map(|c| c.to_ascii_uppercase())
60            .for_each(|c| c.hash(state))
61    }
62}
63
64impl AsRef<str> for Key<'_> {
65    #[inline]
66    fn as_ref(&self) -> &str {
67        self.0.as_ref()
68    }
69}
70
71impl From<String> for Key<'static> {
72    #[inline]
73    fn from(value: String) -> Self {
74        Key(Owned(value))
75    }
76}
77
78impl<'a> From<&'a str> for Key<'a> {
79    #[inline]
80    fn from(value: &'a str) -> Self {
81        Key(Borrowed(value))
82    }
83}
84
85/// An unsized wrapper for case-insensitive key lookups in the settings IndexMap.
86/// This type has the same Hash/Eq semantics as Key, allowing it to be
87/// used as a query type via the Borrow trait.
88#[derive(Eq)]
89#[repr(transparent)]
90struct KeyRef(str);
91
92impl KeyRef {
93    #[inline]
94    fn new(s: &str) -> &Self {
95        // SAFETY: KeyRef is #[repr(transparent)] over str,
96        // so &str and &KeyRef have the same layout.
97        unsafe { &*(s as *const str as *const KeyRef) }
98    }
99}
100
101impl PartialEq for KeyRef {
102    #[inline]
103    fn eq(&self, other: &Self) -> bool {
104        self.0.eq_ignore_ascii_case(&other.0)
105    }
106}
107
108impl Hash for KeyRef {
109    fn hash<H: Hasher>(&self, state: &mut H) {
110        self.0
111            .chars()
112            .map(|c| c.to_ascii_uppercase())
113            .for_each(|c| c.hash(state))
114    }
115}
116
117impl Borrow<KeyRef> for Key<'_> {
118    #[inline]
119    fn borrow(&self) -> &KeyRef {
120        KeyRef::new(self.0.as_ref())
121    }
122}
123
124impl Debug for Key<'_> {
125    #[inline]
126    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
127        Debug::fmt(&self.0, f)
128    }
129}
130
131impl Display for Key<'_> {
132    #[inline]
133    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
134        Display::fmt(&self.0, f)
135    }
136}
137
138/// Represents a case-insensitive collection of configuration settings.
139#[derive(Clone, Default)]
140pub struct Settings(IndexMap<Key<'static>, (String, u8)>);
141
142impl Settings {
143    /// Initializes new [Settings].
144    #[inline]
145    pub fn new() -> Self {
146        Self::default()
147    }
148
149    /// Gets the number of settings.
150    #[inline]
151    pub fn len(&self) -> usize {
152        self.0.len()
153    }
154
155    /// Gets a value indicating whether the collection is empty.
156    #[inline]
157    pub fn is_empty(&self) -> bool {
158        self.0.is_empty()
159    }
160
161    /// Gets an iterator over the keys in the collection in an arbitrary order.
162    #[inline]
163    pub fn keys(&self) -> Keys<'_> {
164        Keys(self.0.keys())
165    }
166
167    pub(crate) fn get_subkey(&self, path: &str, key: &str) -> Option<&str> {
168        const CH_256: usize = 256;
169        let delimiter = path::delimiter();
170        let len = path.len() + 1 + key.len();
171
172        if len <= CH_256 {
173            let mut buf = [0u8; CH_256];
174
175            buf[..path.len()].copy_from_slice(path.as_bytes());
176            buf[path.len()] = delimiter as u8;
177            buf[path.len() + 1..len].copy_from_slice(key.as_bytes());
178
179            // SAFETY: path and key are valid UTF-8 str slices, and delimiter
180            // is an ASCII character, so the concatenation is valid UTF-8.
181
182            let combined = unsafe { std::str::from_utf8_unchecked(&buf[..len]) };
183            self.0.get(KeyRef::new(combined)).map(|(s, _)| s.as_str())
184        } else {
185            let combined = format!("{path}{delimiter}{key}");
186            self.0.get(KeyRef::new(combined.as_str())).map(|(s, _)| s.as_str())
187        }
188    }
189
190    pub(crate) fn get_with_id(&self, key: &str) -> Option<(&str, u8)> {
191        self.0.get(KeyRef::new(key)).map(|(s, i)| (s.as_str(), *i))
192    }
193
194    /// Gets the setting with the specified key.
195    ///
196    /// # Arguments
197    ///
198    /// * `key` - The case-insensitive key of the setting to retrieve
199    pub fn get(&self, key: &str) -> Option<&str> {
200        self.0.get(KeyRef::new(key)).map(|(s, _)| s.as_str())
201    }
202
203    /// Adds or updates a setting with the specified key and value.
204    ///
205    /// # Arguments
206    ///
207    /// * `key` - The key of the setting to add or update
208    /// * `value` - The value of the setting to add or update
209    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> Option<String> {
210        let id = context::id();
211        let value = value.into();
212
213        match self.0.entry(Key::from(key.into())) {
214            Occupied(mut e) => {
215                let entry = e.get_mut();
216                let old = replace(&mut entry.0, value);
217
218                entry.1 |= id;
219
220                let entry = e.get();
221
222                context::overridden(entry.1, e.key().as_ref(), &old, &entry.0);
223                Some(old)
224            }
225            Vacant(e) => {
226                e.insert((value, id));
227                None
228            }
229        }
230    }
231
232    /// Shrinks the capacity of the settings as much as possible. It will drop down as much as possible while
233    /// maintaining the internal rules and possibly leaving some space in accordance with the resize policy.
234    #[inline]
235    pub fn shrink_to_fit(&mut self) {
236        self.0.shrink_to_fit();
237    }
238}
239
240/// Represents a iterator over the key/value pairs in [Settings].
241pub struct Iter<'a>(map::Iter<'a, Key<'static>, (String, u8)>);
242
243/// Represents a consuming iterator over the key/value pairs in [Settings].
244pub struct IntoIter(map::IntoIter<Key<'static>, (String, u8)>);
245
246/// Represents a iterator over the keys in [Settings].
247pub struct Keys<'a>(map::Keys<'a, Key<'static>, (String, u8)>);
248
249impl<'a> Iterator for Iter<'a> {
250    type Item = (&'a str, &'a str);
251
252    fn next(&mut self) -> Option<Self::Item> {
253        self.0.next().map(|(k, (v, _))| (k.as_ref(), v.as_str()))
254    }
255}
256
257impl Iterator for IntoIter {
258    type Item = (String, String);
259
260    fn next(&mut self) -> Option<Self::Item> {
261        self.0.next().map(|(k, (v, _))| (k.0.into_owned(), v))
262    }
263}
264
265impl<'a> IntoIterator for &'a Settings {
266    type Item = (&'a str, &'a str);
267    type IntoIter = Iter<'a>;
268
269    #[inline]
270    fn into_iter(self) -> Self::IntoIter {
271        Iter(self.0.iter())
272    }
273}
274
275impl IntoIterator for Settings {
276    type Item = (String, String);
277    type IntoIter = IntoIter;
278
279    #[inline]
280    fn into_iter(self) -> Self::IntoIter {
281        IntoIter(self.0.into_iter())
282    }
283}
284
285impl<'a> Iterator for Keys<'a> {
286    type Item = &'a str;
287
288    #[inline]
289    fn next(&mut self) -> Option<Self::Item> {
290        self.0.next().map(AsRef::as_ref)
291    }
292}
293
294impl Debug for Settings {
295    #[inline]
296    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
297        Debug::fmt(&self.0, f)
298    }
299}
300
301impl Display for Settings {
302    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
303        let mut pairs = self.0.iter();
304
305        if let Some((key, (value, _))) = pairs.next() {
306            write!(f, "{key}: {value}")?;
307
308            for (key, (value, _)) in pairs {
309                f.write_str(", ")?;
310                write!(f, "{key}: {value}")?;
311            }
312        }
313
314        Ok(())
315    }
316}
317
318impl Merge for Settings {
319    fn merge(&mut self, other: &Self) {
320        // merging from another collection cannot ensure any existing identifier comes from one of the current providers
321        // that can be traced so always replace the current identifier
322        let id = context::id();
323
324        for (key, (value, _)) in &other.0 {
325            self.0.insert(key.clone(), (value.clone(), id));
326        }
327    }
328}