text_span/
lib.rs

1#![no_std]
2
3use core::{cmp::Ordering, ops::Range};
4
5#[cfg(feature = "span-value-usize")]
6/// Type of span values
7pub type SpanValue = usize;
8#[cfg(feature = "span-value-u128")]
9/// Type of span values
10pub type SpanValue = u128;
11#[cfg(feature = "span-value-u64")]
12/// Type of span values
13pub type SpanValue = u64;
14#[cfg(feature = "span-value-u32")]
15/// Type of span values
16pub type SpanValue = u32;
17#[cfg(feature = "span-value-u16")]
18/// Type of span values
19pub type SpanValue = u16;
20#[cfg(feature = "span-value-u8")]
21/// Type of span values
22pub type SpanValue = u8;
23
24/// The `Span` type represents an area of a file.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct Span {
27    /// The start of the `Span`.
28    pub start: SpanValue,
29    /// The end of the `Span`.
30    pub end: SpanValue,
31}
32
33impl Span {
34    /// Creates a new `Span`. This span will start and end at the 0th character, making it have a length of zero.
35    #[inline(always)]
36    pub fn new() -> Self {
37        Self::new_from(0, 0)
38    }
39
40    /// Creates a new `Span` from a pair of start and end indexes. These indexes are indexes into a string by `char`s.
41    #[inline(always)]
42    pub fn new_from(start: SpanValue, end: SpanValue) -> Self {
43        Span { start, end }
44    }
45
46    /// Grows the span from the front. This moves the end value up by `amount`.
47    #[inline(always)]
48    pub fn grow_front(&mut self, amount: SpanValue) {
49        self.end += amount;
50    }
51
52    /// Returns a span that is grown from the front. This moves the end value up by `amount`.
53    #[inline(always)]
54    pub fn with_grow_front(&self, amount: SpanValue) -> Self {
55        let mut new = *self;
56        new.end += amount;
57        new
58    }
59
60    /// Grows the span from the back. This moves the start value back by `amount`.
61    ///
62    /// # Panics
63    /// Panics if the start of the span is less than `amount`, since spans can't have a negative start value,
64    #[inline(always)]
65    pub fn grow_back(&mut self, amount: SpanValue) {
66        assert!(
67            self.start >= amount,
68            "cannot create a span with a negative start value"
69        );
70        self.start -= amount;
71    }
72
73    /// Returns a span that is grown from the back. This moves the start value back by `amount`.
74    ///
75    /// # Panics
76    /// Panics if the start of the span is less than `amount`, since spans can't have a negative start value,
77    #[inline(always)]
78    pub fn with_grow_back(&self, amount: SpanValue) -> Self {
79        assert!(
80            self.start >= amount,
81            "cannot create a span with a negative start value"
82        );
83        let mut new = *self;
84        new.start -= amount;
85        new
86    }
87
88    /// Shrinks the span from the back. This moves the start value up by `amount`.
89    ///
90    /// # Panics
91    /// Panics if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
92    #[inline(always)]
93    pub fn shrink_back(&mut self, amount: SpanValue) {
94        assert!(self.len() >= amount, "cannot create negative-size span");
95        self.start += amount;
96    }
97
98    /// Returns a span that is shrunk from the back. This moves the start value up by `amount`.
99    ///
100    /// # Panics
101    /// Panics if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
102    #[inline(always)]
103    pub fn with_shrink_back(&self, amount: SpanValue) -> Self {
104        assert!(self.len() >= amount, "cannot create negative-size span");
105        let mut new = *self;
106        new.start += amount;
107        new
108    }
109
110    /// Shrinks the span from the front. This moves the end value back by `amount`.
111    ///
112    /// # Panics
113    /// This method will panic if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
114    #[inline(always)]
115    pub fn shrink_front(&mut self, amount: SpanValue) {
116        assert!(self.len() >= amount, "cannot create negative-size span");
117        self.end -= amount;
118    }
119
120    /// Returns a span shrunk from the front. This moves the end value back by `amount`.
121    ///
122    /// # Panics
123    /// This method will panic if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
124    #[inline(always)]
125    pub fn with_shrink_front(&self, amount: SpanValue) -> Self {
126        assert!(self.len() >= amount, "cannot create negative-size span");
127        let mut new = *self;
128        new.end -= amount;
129        new
130    }
131
132    /// Checks if a `Span`'s size is `0`. Returns `true` if `0`, and false if anything else.
133    #[inline(always)]
134    pub fn is_empty(&self) -> bool {
135        self.len() == 0
136    }
137
138    /// Gets the length of a `Span`.
139    #[inline(always)]
140    pub fn len(&self) -> SpanValue {
141        self.end - self.start
142    }
143
144    /// Resets `self` by changing the start to be the end, plus 1, and changing the end to be the start.
145    /// The function also returns the old span.
146    #[inline(always)]
147    pub fn reset(&mut self) -> Self {
148        let span = *self;
149        self.start = self.end;
150        span
151    }
152
153    /// Applies the span to `string`.
154    ///
155    /// # Panics
156    /// Panics if `string` is shorter than the end of the span.
157    #[allow(clippy::unnecessary_cast)]
158    pub fn apply<'a>(&self, string: &'a str) -> &'a str {
159        assert!(
160            string.len() >= self.end as usize,
161            "string is too short to have the span applied"
162        );
163        let start = string
164            .char_indices()
165            .map(|x| x.0)
166            .nth(self.start as usize)
167            .unwrap();
168        let end = string
169            .char_indices()
170            .map(|x| x.0)
171            .nth(self.end as usize)
172            .unwrap_or(string.len());
173        &string[start..end]
174    }
175}
176
177impl From<Span> for Range<SpanValue> {
178    #[inline(always)]
179    fn from(val: Span) -> Self {
180        val.start..val.end
181    }
182}
183
184impl From<Range<SpanValue>> for Span {
185    #[inline(always)]
186    fn from(value: Range<SpanValue>) -> Self {
187        Self::new_from(value.start, value.end)
188    }
189}
190
191impl PartialOrd for Span {
192    #[inline(always)]
193    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
194        dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
195    }
196}
197
198fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
199    match (x, y) {
200        (x, y) if x == y => Some(x),
201        (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
202        (x, Ordering::Equal) => Some(x),
203        (Ordering::Equal, x) => Some(x),
204        _ => unreachable!(),
205    }
206}