Skip to main content

flams_utils/sourcerefs/
ranges.rs

1use crate::sourcerefs::OffsetPosition;
2
3use super::StringPosition;
4
5/// INVARIANT: end >= start
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
8pub struct StringRange<P: StringPosition> {
9    pub start: P,
10    pub end: P,
11}
12impl<P: StringPosition> std::fmt::Display for StringRange<P> {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        write!(f, "{}--{}", self.start, self.end)
15    }
16}
17
18impl<P: StringPosition> std::ops::Sub<P> for StringRange<P> {
19    type Output = Self;
20    fn sub(self, rhs: P) -> Self::Output {
21        Self {
22            start: self.start.sub(rhs),
23            end: self.end.sub(rhs),
24        }
25    }
26}
27
28impl<P: StringPosition> std::ops::SubAssign<P> for StringRange<P> {
29    fn sub_assign(&mut self, rhs: P) {
30        self.start.sub_assign(rhs);
31        self.end.sub_assign(rhs);
32    }
33}
34
35impl<P: StringPosition> StringRange<P> {
36    #[inline]
37    pub fn contains(self, pos: P) -> bool {
38        self.start <= pos && pos <= self.end
39    }
40}
41
42#[allow(clippy::inline_always)]
43impl<P: StringPosition> PartialEq<P> for StringRange<P> {
44    #[inline(always)]
45    fn eq(&self, other: &P) -> bool {
46        self.contains(*other)
47    }
48}
49impl<P: StringPosition> PartialOrd<P> for StringRange<P> {
50    fn partial_cmp(&self, other: &P) -> Option<std::cmp::Ordering> {
51        if self.end <= *other {
52            Some(std::cmp::Ordering::Less)
53        } else if self.start >= *other {
54            Some(std::cmp::Ordering::Greater)
55        } else if self.start == self.end && self.start == *other {
56            Some(std::cmp::Ordering::Equal)
57        } else {
58            None
59        }
60    }
61}
62
63//impl<P: StringPosition> std::ops::Index<std::ops::RangeFrom<P>>
64
65impl<P: StringPosition> std::ops::Index<StringRange<P>> for str {
66    type Output = str;
67    fn index(&self, index: StringRange<P>) -> &Self::Output {
68        let StringRange { start, end } = index.into_other::<crate::sourcerefs::ByteOffset>(self);
69        &self[start.0..end.0]
70    }
71}
72
73impl<P: StringPosition> StringRange<P> {
74    #[allow(clippy::inline_always)]
75    #[inline(always)]
76    #[must_use]
77    pub fn from_other<O: StringPosition>(other: StringRange<O>, source: &str) -> Self {
78        let mut conv = P::from_many(source);
79        Self {
80            start: conv.next(other.start),
81            end: conv.next(other.end),
82        }
83    }
84    #[allow(clippy::inline_always)]
85    #[inline(always)]
86    #[must_use]
87    pub fn into_other<O: StringPosition>(self, source: &str) -> StringRange<O> {
88        StringRange::from_other(self, source)
89    }
90}
91
92impl<P: StringPosition> StringRange<P> {
93    #[must_use]
94    pub const fn until(self, rhs: P) -> Self {
95        Self {
96            start: self.start,
97            end: rhs,
98        }
99    }
100}