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` (Inclusive)
28    pub start: SpanValue,
29    /// The end of the `Span` (Exclusive)
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    ///
42    /// # Panics
43    /// Panics if start is greater than end, since spans can't have a negative length.
44    #[inline(always)]
45    pub fn new_from(start: SpanValue, end: SpanValue) -> Self {
46        assert!(end >= start, "cannot create negative-size span");
47
48        Span { start, end }
49    }
50
51    /// Grows the span from the front. This moves the end value up by `amount`.
52    #[inline(always)]
53    pub fn grow_front(&mut self, amount: SpanValue) {
54        self.end += amount;
55    }
56
57    /// Returns a span that is grown from the front. This moves the end value up by `amount`.
58    #[inline(always)]
59    pub fn with_grow_front(&self, amount: SpanValue) -> Self {
60        let mut new = *self;
61        new.end += amount;
62        new
63    }
64
65    /// Grows the span from the back. This moves the start value back by `amount`.
66    ///
67    /// # Panics
68    /// Panics if the start of the span is less than `amount`, since spans can't have a negative start value,
69    #[inline(always)]
70    pub fn grow_back(&mut self, amount: SpanValue) {
71        assert!(
72            self.start >= amount,
73            "cannot create a span with a negative start value"
74        );
75        self.start -= amount;
76    }
77
78    /// Returns a span that is grown from the back. This moves the start value back by `amount`.
79    ///
80    /// # Panics
81    /// Panics if the start of the span is less than `amount`, since spans can't have a negative start value,
82    #[inline(always)]
83    pub fn with_grow_back(&self, amount: SpanValue) -> Self {
84        assert!(
85            self.start >= amount,
86            "cannot create a span with a negative start value"
87        );
88        let mut new = *self;
89        new.start -= amount;
90        new
91    }
92
93    /// Shrinks the span from the back. This moves the start value up by `amount`.
94    ///
95    /// # Panics
96    /// Panics if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
97    #[inline(always)]
98    pub fn shrink_back(&mut self, amount: SpanValue) {
99        assert!(self.len() >= amount, "cannot create negative-size span");
100        self.start += amount;
101    }
102
103    /// Returns a span that is shrunk from the back. This moves the start value up by `amount`.
104    ///
105    /// # Panics
106    /// Panics if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
107    #[inline(always)]
108    pub fn with_shrink_back(&self, amount: SpanValue) -> Self {
109        assert!(self.len() >= amount, "cannot create negative-size span");
110        let mut new = *self;
111        new.start += amount;
112        new
113    }
114
115    /// Shrinks the span from the front. This moves the end value back by `amount`.
116    ///
117    /// # Panics
118    /// This method will panic if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
119    #[inline(always)]
120    pub fn shrink_front(&mut self, amount: SpanValue) {
121        assert!(self.len() >= amount, "cannot create negative-size span");
122        self.end -= amount;
123    }
124
125    /// Returns a span shrunk from the front. This moves the end value back by `amount`.
126    ///
127    /// # Panics
128    /// This method will panic if the size of the `Span` is less than `amount`, since a `Span`'s size can't be negative.
129    #[inline(always)]
130    pub fn with_shrink_front(&self, amount: SpanValue) -> Self {
131        assert!(self.len() >= amount, "cannot create negative-size span");
132        let mut new = *self;
133        new.end -= amount;
134        new
135    }
136
137    /// Checks if a `Span`'s size is `0`. Returns `true` if `0`, and false if anything else.
138    #[inline(always)]
139    pub fn is_empty(&self) -> bool {
140        self.len() == 0
141    }
142
143    /// Gets the length of a `Span`.
144    #[inline(always)]
145    pub fn len(&self) -> SpanValue {
146        self.end - self.start
147    }
148
149    /// Resets `self` by changing the start to be the end, plus 1, and changing the end to be the start.
150    /// The function also returns the old span.
151    #[inline(always)]
152    pub fn reset(&mut self) -> Self {
153        let span = *self;
154        self.start = self.end;
155        span
156    }
157
158    /// Applies the span to `string`.
159    ///
160    /// # Panics
161    /// Panics if `string` is shorter than the end of the span.
162    #[allow(clippy::unnecessary_cast)]
163    pub fn apply<'a>(&self, string: &'a str) -> &'a str {
164        assert!(
165            string.len() >= self.end as usize,
166            "string is too short to have the span applied"
167        );
168        let start = string
169            .char_indices()
170            .map(|x| x.0)
171            .nth(self.start as usize)
172            .unwrap();
173        let end = string
174            .char_indices()
175            .map(|x| x.0)
176            .nth(self.end as usize)
177            .unwrap_or(string.len());
178        &string[start..end]
179    }
180}
181
182impl From<Span> for Range<SpanValue> {
183    #[inline(always)]
184    fn from(val: Span) -> Self {
185        val.start..val.end
186    }
187}
188
189impl From<Range<SpanValue>> for Span {
190    #[inline(always)]
191    fn from(value: Range<SpanValue>) -> Self {
192        Self::new_from(value.start, value.end)
193    }
194}
195
196impl PartialOrd for Span {
197    #[inline(always)]
198    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
199        dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
200    }
201}
202
203fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
204    match (x, y) {
205        (x, y) if x == y => Some(x),
206        (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
207        (x, Ordering::Equal) => Some(x),
208        (Ordering::Equal, x) => Some(x),
209        _ => unreachable!(),
210    }
211}
212
213#[cfg(feature = "ariadne")]
214impl ariadne::Span for Span {
215    type SourceId = ();
216
217    fn source(&self) -> &Self::SourceId {
218        &()
219    }
220
221    #[allow(clippy::unnecessary_cast)]
222    fn start(&self) -> usize {
223        self.start as usize
224    }
225
226    #[allow(clippy::unnecessary_cast)]
227    fn end(&self) -> usize {
228        self.end as usize
229    }
230}