vimwiki_core/lang/elements/utils/
region.rs1use crate::lang::parsers::Span;
2use serde::{Deserialize, Serialize};
3use std::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive};
4
5#[derive(
7    Copy, Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize,
8)]
9pub struct Region {
10    offset: usize,
12
13    len: usize,
15
16    depth: u16,
20}
21
22impl Region {
23    pub fn new(offset: usize, len: usize) -> Self {
26        Self {
27            offset,
28            len,
29            depth: 0,
30        }
31    }
32
33    pub fn new_at_depth(offset: usize, len: usize, depth: u16) -> Self {
35        Self { offset, len, depth }
36    }
37
38    pub fn with_depth(&self, depth: u16) -> Self {
40        Self::new_at_depth(self.offset, self.len, depth)
41    }
42
43    #[inline]
45    pub fn contains(&self, offset: usize) -> bool {
46        offset >= self.offset && offset < (self.offset + self.len)
47    }
48
49    #[inline]
51    pub fn offset(&self) -> usize {
52        self.offset
53    }
54
55    #[inline]
57    pub fn len(&self) -> usize {
58        self.len
59    }
60
61    #[inline]
63    pub fn depth(&self) -> u16 {
64        self.depth
65    }
66
67    #[inline]
69    pub fn is_empty(&self) -> bool {
70        self.len == 0
71    }
72}
73
74impl<'a> From<Span<'a>> for Region {
75    fn from(span: Span<'a>) -> Self {
77        Self::new_at_depth(
78            span.start_offset(),
79            span.remaining_len(),
80            span.depth(),
81        )
82    }
83}
84
85impl From<Range<usize>> for Region {
86    fn from(range: Range<usize>) -> Self {
89        let len = if range.end >= range.start {
90            range.end - range.start
91        } else {
92            0
93        };
94        Self::new(range.start, len)
95    }
96}
97
98impl From<RangeInclusive<usize>> for Region {
99    fn from(range: RangeInclusive<usize>) -> Self {
102        let (start, end) = range.into_inner();
103        let len = if (end + 1) >= start {
104            (end + 1) - start
105        } else {
106            0
107        };
108        Self::new(start, len)
109    }
110}
111
112impl From<RangeTo<usize>> for Region {
113    fn from(range: RangeTo<usize>) -> Self {
115        Self::new(0, range.end)
116    }
117}
118
119impl From<RangeToInclusive<usize>> for Region {
120    fn from(range: RangeToInclusive<usize>) -> Self {
122        Self::new(0, range.end + 1)
123    }
124}
125
126impl From<(usize, usize)> for Region {
127    fn from(coords: (usize, usize)) -> Self {
129        Self::new(coords.0, coords.1)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn contains_should_successfully_return_whether_or_not_offset_within_region()
139    {
140        let region = Region::new(3, 2);
141        assert!(!region.contains(0));
142        assert!(!region.contains(1));
143        assert!(!region.contains(2));
144        assert!(region.contains(3));
145        assert!(region.contains(4));
146        assert!(!region.contains(5));
147        assert!(!region.contains(6));
148        assert!(!region.contains(7));
149    }
150
151    #[test]
152    fn from_should_properly_convert_range_to_region() {
153        let region = Region::from(0..3);
154        assert_eq!(region, Region::new(0, 3));
155
156        let region = Region::from(3..3);
157        assert_eq!(region, Region::new(3, 0));
158
159        #[allow(clippy::reversed_empty_ranges)]
160        let region = Region::from(4..3);
161        assert_eq!(region, Region::new(4, 0));
162    }
163
164    #[test]
165    fn from_should_properly_convert_range_inclusive_to_region() {
166        let region = Region::from(0..=3);
167        assert_eq!(region, Region::new(0, 4));
168
169        let region = Region::from(3..=3);
170        assert_eq!(region, Region::new(3, 1));
171
172        #[allow(clippy::reversed_empty_ranges)]
173        let region = Region::from(4..=3);
174        assert_eq!(region, Region::new(4, 0));
175    }
176
177    #[test]
178    fn from_should_properly_convert_rangeto_to_region() {
179        let region = Region::from(..3);
180        assert_eq!(region, Region::new(0, 3));
181
182        let region = Region::from(..0);
183        assert_eq!(region, Region::new(0, 0));
184
185        let region = Region::from(..1);
186        assert_eq!(region, Region::new(0, 1));
187    }
188
189    #[test]
190    fn from_should_properly_convert_rangeto_inclusive_to_region() {
191        let region = Region::from(..=3);
192        assert_eq!(region, Region::new(0, 4));
193
194        let region = Region::from(..=0);
195        assert_eq!(region, Region::new(0, 1));
196
197        let region = Region::from(..=1);
198        assert_eq!(region, Region::new(0, 2));
199    }
200
201    #[test]
202    fn from_should_properly_convert_span_to_region() {
203        let span = Span::new(&[], 3, 8, 2);
204        let region = Region::from(span);
205        assert_eq!(region, Region::new_at_depth(3, 5, 2));
206    }
207}