ec4rs/
properties.rs

1mod iter;
2
3pub use iter::*;
4
5use crate::rawvalue::RawValue;
6use crate::{PropertyKey, PropertyValue};
7
8/// Map of property names to property values.
9///
10/// It features O(log n) lookup and preserves insertion order,
11/// as well as convenience methods for type-safe access and parsing of values.
12///
13/// This structure is case-sensitive.
14/// It's the caller's responsibility to ensure all keys and values are lowercased.
15#[derive(Clone, Default)]
16pub struct Properties {
17    // Don't use Cow<'static, str> here because it's actually less-optimal
18    // for the vastly more-common case of reading parsed properties.
19    // It's a micro-optimization anyway.
20    /// Key-value pairs, ordered from oldest to newest.
21    pairs: Vec<(String, RawValue)>,
22    /// Indices of `pairs`, ordered matching the key of the pair each index refers to.
23    /// This part is what allows logarithmic lookups.
24    idxes: Vec<usize>,
25    // Unfortunately, we hand out `&mut RawValue`s all over the place,
26    // so "no empty RawValues in Properties" cannot be made an invariant
27    // without breaking API changes.
28}
29
30// TODO: Deletion.
31
32impl Properties {
33    /// Constructs a new empty [`Properties`].
34    pub const fn new() -> Properties {
35        Properties {
36            pairs: Vec::new(),
37            idxes: Vec::new(),
38        }
39    }
40
41    /// Returns the number of key-value pairs, including those with empty values.
42    pub fn len(&self) -> usize {
43        self.pairs.len()
44    }
45
46    /// Returns `true` if `self` contains no key-value pairs.
47    pub fn is_empty(&self) -> bool {
48        self.pairs.is_empty()
49    }
50
51    /// Returns either the index of the pair with the desired key in `pairs`,
52    /// or the index to insert a new index into `index`.
53    fn find_idx(&self, key: &str) -> Result<usize, usize> {
54        self.idxes
55            .as_slice()
56            .binary_search_by_key(&key, |ki| self.pairs[*ki].0.as_str())
57            .map(|idx| self.idxes[idx])
58    }
59
60    /// Returns the unparsed "raw" value for the specified key.
61    ///
62    /// Does not test for the "unset" value. Use [`RawValue::filter_unset`].
63    pub fn get_raw_for_key(&self, key: impl AsRef<str>) -> &RawValue {
64        self.find_idx(key.as_ref())
65            .ok()
66            .map_or(&crate::rawvalue::UNSET, |idx| &self.pairs[idx].1)
67    }
68
69    /// Returns the unparsed "raw" value for the specified property.
70    ///
71    /// Does not test for the "unset" value. Use [`RawValue::filter_unset`].
72    pub fn get_raw<T: PropertyKey>(&self) -> &RawValue {
73        self.get_raw_for_key(T::key())
74    }
75
76    /// Returns the parsed value for the specified property.
77    ///
78    /// Does not test for the "unset" value if parsing fails. Use [`RawValue::filter_unset`].
79    pub fn get<T: PropertyKey + PropertyValue>(&self) -> Result<T, &RawValue> {
80        let retval = self.get_raw::<T>();
81        retval.parse::<T>().or(Err(retval))
82    }
83
84    /// Returns an iterator over the key-value pairs.
85    ///
86    /// If the `allow-empty-values` feature is NOT used,
87    /// key-value pairs where the value is empty will be skipped.
88    /// Otherwise, they will be returned as normal.
89    ///
90    /// Pairs are returned from oldest to newest.
91    pub fn iter(&self) -> Iter<'_> {
92        Iter(self.pairs.iter())
93    }
94
95    /// Returns an iterator over the key-value pairs that allows mutation of the values.
96    ///
97    /// If the `allow-empty-values` feature is NOT used,
98    /// key-value pairs where the value is empty will be skipped.
99    /// Otherwise, they will be returned as normal.
100    ///
101    /// Pairs are returned from oldest to newest.
102    pub fn iter_mut(&mut self) -> IterMut<'_> {
103        IterMut(self.pairs.iter_mut())
104    }
105
106    fn get_at_mut(&mut self, idx: usize) -> &mut RawValue {
107        &mut self.pairs.get_mut(idx).unwrap().1
108    }
109
110    fn insert_at(&mut self, idx: usize, key: String, val: RawValue) {
111        self.idxes.insert(idx, self.pairs.len());
112        self.pairs.push((key, val));
113    }
114
115    /// Sets the value for a specified key.
116    pub fn insert_raw_for_key(&mut self, key: impl AsRef<str>, val: impl Into<RawValue>) {
117        let key_str = key.as_ref();
118        match self.find_idx(key_str) {
119            Ok(idx) => {
120                *self.get_at_mut(idx) = val.into();
121            }
122            Err(idx) => {
123                self.insert_at(idx, key_str.to_owned(), val.into());
124            }
125        }
126    }
127
128    /// Sets the value for a specified property's key.
129    pub fn insert_raw<K: PropertyKey, V: Into<RawValue>>(&mut self, val: V) {
130        self.insert_raw_for_key(K::key(), val)
131    }
132
133    /// Inserts a specified property into the map.
134    pub fn insert<T: PropertyKey + Into<RawValue>>(&mut self, prop: T) {
135        self.insert_raw_for_key(T::key(), prop.into())
136    }
137
138    /// Attempts to add a new key-value pair to the map.
139    ///
140    /// If the key was already associated with a value,
141    /// returns a mutable reference to the old value and does not update the map.
142    pub fn try_insert_raw_for_key(
143        &mut self,
144        key: impl AsRef<str>,
145        value: impl Into<RawValue>,
146    ) -> Result<(), &mut RawValue> {
147        let key_str = key.as_ref();
148        #[allow(clippy::unit_arg)]
149        match self.find_idx(key_str) {
150            Ok(idx) => {
151                let valref = self.get_at_mut(idx);
152                if valref.is_unset() {
153                    *valref = value.into();
154                    Ok(())
155                } else {
156                    Err(valref)
157                }
158            }
159            Err(idx) => Ok(self.insert_at(idx, key_str.to_owned(), value.into())),
160        }
161    }
162
163    /// Attempts to add a new property to the map with a specified value.
164    ///
165    /// If the key was already associated with a value,
166    /// returns a mutable reference to the old value and does not update the map.
167    pub fn try_insert_raw<K: PropertyKey, V: Into<RawValue>>(
168        &mut self,
169        val: V,
170    ) -> Result<(), &mut RawValue> {
171        self.try_insert_raw_for_key(K::key(), val)
172    }
173
174    /// Attempts to add a new property to the map.
175    ///
176    /// If the key was already associated with a value,
177    /// returns a mutable reference to the old value and does not update the map.
178    pub fn try_insert<T: PropertyKey + Into<RawValue>>(
179        &mut self,
180        prop: T,
181    ) -> Result<(), &mut RawValue> {
182        self.try_insert_raw_for_key(T::key(), prop.into())
183    }
184
185    /// Adds fallback values for certain common key-value pairs.
186    ///
187    /// Used to obtain spec-compliant values for [`crate::property::IndentSize`]
188    /// and [`crate::property::TabWidth`].
189    pub fn use_fallbacks(&mut self) {
190        crate::fallback::add_fallbacks(self, false)
191    }
192
193    /// Adds pre-0.9.0 fallback values for certain common key-value pairs.
194    ///
195    /// This shouldn't be used outside of narrow cases where
196    /// compatibility with those older standards is required.
197    /// Prefer [`Properties::use_fallbacks`] instead.
198    pub fn use_fallbacks_legacy(&mut self) {
199        crate::fallback::add_fallbacks(self, true)
200    }
201}
202
203impl PartialEq for Properties {
204    fn eq(&self, other: &Self) -> bool {
205        if self.len() != other.len() {
206            return false;
207        }
208        self.idxes
209            .iter()
210            .zip(other.idxes.iter())
211            .all(|(idx_s, idx_o)| self.pairs[*idx_s] == other.pairs[*idx_o])
212    }
213}
214
215impl Eq for Properties {}
216
217impl std::fmt::Debug for Properties {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        f.debug_tuple("Properties")
220            .field(&self.pairs.as_slice())
221            .finish()
222    }
223}
224
225impl<'a> IntoIterator for &'a Properties {
226    type Item = <Iter<'a> as Iterator>::Item;
227
228    type IntoIter = Iter<'a>;
229
230    fn into_iter(self) -> Self::IntoIter {
231        self.iter()
232    }
233}
234
235impl<'a> IntoIterator for &'a mut Properties {
236    type Item = <IterMut<'a> as Iterator>::Item;
237
238    type IntoIter = IterMut<'a>;
239
240    fn into_iter(self) -> Self::IntoIter {
241        self.iter_mut()
242    }
243}
244
245impl<K: AsRef<str>, V: Into<RawValue>> FromIterator<(K, V)> for Properties {
246    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
247        let mut result = Properties::new();
248        result.extend(iter);
249        result
250    }
251}
252
253impl<K: AsRef<str>, V: Into<RawValue>> Extend<(K, V)> for Properties {
254    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
255        let iter = iter.into_iter();
256        let min_len = iter.size_hint().0;
257        self.pairs.reserve(min_len);
258        self.idxes.reserve(min_len);
259        for (k, v) in iter {
260            let k = k.as_ref();
261            let v = v.into();
262            self.insert_raw_for_key(k, v);
263        }
264    }
265}
266
267/// Trait for types that can add properties to a [`Properties`] map.
268pub trait PropertiesSource {
269    /// Adds properties that apply to a file at the specified path
270    /// to the provided [`Properties`].
271    fn apply_to(
272        self,
273        props: &mut Properties,
274        path: impl AsRef<std::path::Path>,
275    ) -> Result<(), crate::Error>;
276}
277
278impl<'a> PropertiesSource for &'a Properties {
279    fn apply_to(
280        self,
281        props: &mut Properties,
282        _: impl AsRef<std::path::Path>,
283    ) -> Result<(), crate::Error> {
284        props.extend(self.pairs.iter().cloned());
285        Ok(())
286    }
287}