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.
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`, with `start` and `end` corresponding to char indexes.
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        let mut chars = string.char_indices();
165
166        let start = chars
167            .nth(self.start as usize)
168            .expect("string is too short to have the span applied")
169            .0;
170        let end = chars
171            .nth(self.len() as usize)
172            .expect("string is too short to have the span applied")
173            .0;
174        &string[start..end]
175    }
176
177    /// Applies the span to `string`, with `start` and `end` corresponding to byte indexes.
178    ///
179    /// # Panics
180    /// Panics if `string` is shorter than the end of the span.
181    #[allow(clippy::unnecessary_cast)]
182    pub fn apply_bytes<'a>(&self, string: &'a str) -> &'a str {
183        assert!(
184            string.len() >= self.end as usize,
185            "string is too short to have the span applied"
186        );
187        &string[(self.start as usize)..(self.end as usize)]
188    }
189}
190
191impl From<Span> for Range<SpanValue> {
192    #[inline(always)]
193    fn from(val: Span) -> Self {
194        val.start..val.end
195    }
196}
197
198impl From<Range<SpanValue>> for Span {
199    #[inline(always)]
200    fn from(value: Range<SpanValue>) -> Self {
201        Self::new_from(value.start, value.end)
202    }
203}
204
205impl PartialOrd for Span {
206    #[inline(always)]
207    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
208        dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
209    }
210}
211
212fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
213    match (x, y) {
214        (x, y) if x == y => Some(x),
215        (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
216        (x, Ordering::Equal) => Some(x),
217        (Ordering::Equal, x) => Some(x),
218        _ => unreachable!(),
219    }
220}
221
222#[cfg(feature = "ariadne")]
223impl ariadne::Span for Span {
224    type SourceId = ();
225
226    fn source(&self) -> &Self::SourceId {
227        &()
228    }
229
230    #[allow(clippy::unnecessary_cast)]
231    fn start(&self) -> usize {
232        self.start as usize
233    }
234
235    #[allow(clippy::unnecessary_cast)]
236    fn end(&self) -> usize {
237        self.end as usize
238    }
239}