vimwiki_core/lang/elements/utils/
mod.rs

1use crate::StrictEq;
2use derive_more::{Constructor, Deref, DerefMut, Display};
3use serde::{Deserialize, Serialize};
4use std::hash::{Hash, Hasher};
5
6mod region;
7pub use region::Region;
8
9/// Represents a trait that provides the ability to get the children of an
10/// element as a slice
11pub trait AsChildrenSlice {
12    /// The type of child contained within
13    type Child;
14
15    /// Returns a slice to children contained within
16    fn as_children_slice(&self) -> &[Self::Child];
17}
18
19/// Represents a trait that provides the ability to get the children of an
20/// element as a mut slice
21pub trait AsChildrenMutSlice {
22    /// The type of child contained within
23    type Child;
24
25    /// Returns a mutable slice to children contained within
26    fn as_children_mut_slice(&mut self) -> &mut [Self::Child];
27}
28
29/// Represents a trait that provides the ability to get the children of an
30/// element through a consuming conversion
31pub trait IntoChildren {
32    /// The type of child contained within
33    type Child;
34
35    /// Returns a vec of children contained within
36    fn into_children(self) -> Vec<Self::Child>;
37}
38
39/// Represents an encapsulation of a language element and its location
40/// within some string/file
41#[derive(
42    Constructor,
43    Copy,
44    Clone,
45    Debug,
46    Display,
47    Deref,
48    DerefMut,
49    Eq,
50    Serialize,
51    Deserialize,
52)]
53#[display(fmt = "{}", inner)]
54pub struct Located<T> {
55    #[deref]
56    #[deref_mut]
57    inner: T,
58    region: Region,
59}
60
61impl<T> Located<T> {
62    /// Maps a `Located<T>` to `Located<U>` by applying a
63    /// function to the underlying element. Useful when upleveling the
64    /// element (such as wrapping a Header1) while the region remains
65    /// unchanged.
66    #[inline]
67    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Located<U> {
68        Located::new(f(self.inner), self.region)
69    }
70
71    /// Takes a `Located` and replaces its region, producing the
72    /// updated region. This takes ownership of the existing element!
73    pub fn take_with_region(mut self, region: Region) -> Self {
74        self.region = region;
75        self
76    }
77
78    /// Converts from `&Located<T>` to `Located<&T>`
79    pub fn as_ref(&self) -> Located<&T> {
80        Located {
81            inner: &self.inner,
82            region: self.region,
83        }
84    }
85
86    /// Converts from `&mut Located<T>` to `Located<&mut T>`
87    pub fn as_mut(&mut self) -> Located<&mut T> {
88        Located {
89            inner: &mut self.inner,
90            region: self.region,
91        }
92    }
93
94    /// Converts from `&Located<T>` to `&T`
95    pub fn as_inner(&self) -> &T {
96        &self.inner
97    }
98
99    /// Converts from `&mut Located<T>` to `&mut T`
100    pub fn as_mut_inner(&mut self) -> &mut T {
101        &mut self.inner
102    }
103
104    /// Converts from `Located<T>` to `T`
105    pub fn into_inner(self) -> T {
106        self.inner
107    }
108
109    /// Returns depth of the inner value among other Located objects
110    pub fn depth(&self) -> u16 {
111        self.region.depth()
112    }
113
114    /// Returns a copy of the region associated with the inner value
115    pub fn region(&self) -> Region {
116        self.region
117    }
118}
119
120impl<T> Located<Option<T>> {
121    /// Transposes a `Located` of an [`Option`] into an [`Option`] of a `Located`.
122    ///
123    /// ## Examples
124    ///
125    /// ```
126    /// # use vimwiki::Located;
127    /// let x: Located<Option<usize>> = Located::from(Some(5));
128    /// let y: Option<Located<usize>> = Some(Located::from(5));
129    /// assert_eq!(x.transpose(), y);
130    /// ```
131    pub fn transpose(self) -> Option<Located<T>> {
132        let region = self.region();
133        self.into_inner().map(|inner| Located::new(inner, region))
134    }
135}
136
137impl<T: PartialEq> PartialEq for Located<T> {
138    fn eq(&self, other: &Self) -> bool {
139        self.inner == other.inner
140    }
141}
142
143impl<T: PartialEq> PartialEq<T> for Located<T> {
144    fn eq(&self, other: &T) -> bool {
145        &self.inner == other
146    }
147}
148
149impl<T: StrictEq> StrictEq for Located<T> {
150    /// Performs strict equality check by verifying that inner value is
151    /// strict equal and that the regions of both located are equal
152    fn strict_eq(&self, other: &Self) -> bool {
153        self.inner.strict_eq(&other.inner) && self.region == other.region
154    }
155}
156
157impl<T: StrictEq> StrictEq<T> for Located<T> {
158    /// Performs strict equality check by verifying that inner value is
159    /// strict equal to the provided value
160    fn strict_eq(&self, other: &T) -> bool {
161        self.inner.strict_eq(&other)
162    }
163}
164
165impl<T: Hash> Hash for Located<T> {
166    fn hash<H: Hasher>(&self, state: &mut H) {
167        self.inner.hash(state);
168    }
169}
170
171impl<T> From<T> for Located<T> {
172    /// Creates around `T`, using a default location
173    fn from(t: T) -> Self {
174        Self::new(t, Default::default())
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use std::collections::HashSet;
182
183    #[test]
184    fn map_should_transform_inner_value_and_keep_region() {
185        let le = Located::new(3, Region::new(1, 4));
186        let mapped_le = le.map(|c| c + 1);
187        assert_eq!(*mapped_le.as_inner(), 4);
188        assert_eq!(mapped_le.region(), Region::new(1, 4));
189    }
190
191    #[test]
192    fn equality_with_other_should_only_use_inner_value() {
193        let le1 = Located::new(3, Region::new(1, 4));
194        let le2 = Located::new(3, Region::default());
195        assert_eq!(le1, le2);
196    }
197
198    #[test]
199    fn equality_with_inner_type_should_only_use_inner_value() {
200        let le = Located::new(3, Region::new(1, 4));
201        let inner = 3;
202        assert_eq!(le, inner);
203        assert!(le != inner + 1);
204    }
205
206    #[test]
207    fn hashing_should_only_use_inner_value() {
208        let le1 = Located::new(3, Region::new(1, 4));
209        let le2 = Located::new(3, Region::default());
210        let le3 = Located::new(4, Region::new(1, 4));
211        let le4 = Located::new(3, Region::new(1, 4));
212
213        let mut m = HashSet::new();
214        m.insert(le1);
215
216        let le = m
217            .get(&le2)
218            .expect("Failed to retrieve Located with another Located");
219        assert_eq!(*le.as_inner(), 3);
220        assert_eq!(le.region(), Region::new(1, 4));
221
222        assert_eq!(m.get(&le3), None);
223
224        let le = m
225            .get(&le4)
226            .expect("Failed to retrieve Located with another Located");
227        assert_eq!(*le.as_inner(), 3);
228        assert_eq!(le.region(), Region::new(1, 4));
229    }
230
231    #[test]
232    fn as_ref_should_return_new_element_with_ref_and_same_region() {
233        #[derive(Debug, PartialEq, Eq)]
234        struct Test(usize);
235
236        let le = Located::new(Test(5), Region::new(1, 4));
237        let le_ref = le.as_ref();
238
239        assert_eq!(le_ref.inner, &Test(5));
240        assert_eq!(le_ref.region(), Region::new(1, 4));
241    }
242
243    #[test]
244    fn as_mut_should_return_new_element_with_mut_and_same_region() {
245        #[derive(Debug, PartialEq, Eq)]
246        struct Test(usize);
247
248        let mut le = Located::new(Test(5), Region::new(1, 4));
249        let le_mut = le.as_mut();
250
251        assert_eq!(le_mut.inner, &mut Test(5));
252        assert_eq!(le_mut.region(), Region::new(1, 4));
253    }
254
255    #[test]
256    fn as_inner_should_return_new_element_with_ref_to_inner_and_same_region() {
257        #[derive(Debug, PartialEq, Eq)]
258        struct Test(usize);
259
260        let le = Located::new(Test(5), Region::new(1, 4));
261        let inner = le.as_inner();
262
263        assert_eq!(inner, &Test(5));
264    }
265
266    #[test]
267    fn as_mut_inner_should_return_new_element_with_mut_ref_to_inner_and_same_region(
268    ) {
269        #[derive(Debug, PartialEq, Eq)]
270        struct Test(usize);
271
272        let mut le = Located::new(Test(5), Region::new(1, 4));
273        let inner = le.as_mut_inner();
274
275        assert_eq!(inner, &mut Test(5));
276    }
277
278    #[test]
279    fn into_inner_should_return_inner_value_as_owned() {
280        #[derive(Debug, PartialEq, Eq)]
281        struct Test(usize);
282
283        let le = Located::new(Test(5), Region::new(1, 4));
284        let inner = le.into_inner();
285
286        assert_eq!(inner, Test(5));
287    }
288}