rune/ast/
span.rs

1use core::cmp;
2use core::fmt;
3use core::ops;
4
5use serde::{Deserialize, Serialize};
6
7use crate::ast::prelude::*;
8
9/// A span corresponding to a range in the source file being parsed.
10#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[try_clone(copy)]
12pub struct Span {
13    /// The start of the span in bytes.
14    pub start: ByteIndex,
15    /// The end of the span in bytes.
16    pub end: ByteIndex,
17}
18
19impl Span {
20    /// Construct a new span.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use rune::ast::Span;
26    ///
27    /// let span = Span::new(42, 50);
28    /// assert!(span < Span::new(100, 101));
29    /// ```
30    pub fn new<S, E>(start: S, end: E) -> Self
31    where
32        S: TryInto<ByteIndex>,
33        S::Error: fmt::Debug,
34        E: TryInto<ByteIndex>,
35        E::Error: fmt::Debug,
36    {
37        Self {
38            start: start.try_into().expect("start out of bounds"),
39            end: end.try_into().expect("end out of bounds"),
40        }
41    }
42
43    /// Get a span corresponding to a single point where both start and end are
44    /// the same byte offset.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use rune::ast::Span;
50    ///
51    /// assert_eq!(Span::point(42), Span::new(42, 42));
52    /// ```
53    pub fn point<P>(pos: P) -> Self
54    where
55        P: TryInto<ByteIndex>,
56        P::Error: fmt::Debug,
57    {
58        let pos = pos.try_into().expect("point out of bounds");
59
60        Self {
61            start: pos,
62            end: pos,
63        }
64    }
65
66    /// Constant function to build an empty span.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use rune::ast::Span;
72    ///
73    /// assert_eq!(Span::empty(), Span::new(0, 0));
74    /// ```
75    pub const fn empty() -> Self {
76        Self {
77            start: ByteIndex(0),
78            end: ByteIndex(0),
79        }
80    }
81
82    /// Get the head of the span.
83    pub fn head(self) -> Self {
84        Self {
85            start: self.start,
86            end: self.start,
87        }
88    }
89
90    /// Get the tail of the span.
91    pub fn tail(self) -> Self {
92        Self {
93            start: self.end,
94            end: self.end,
95        }
96    }
97
98    /// Join two spans creating the larger of the two spans.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use rune::ast::Span;
104    ///
105    /// let a = Span::new(10, 12);
106    /// let b = Span::new(20, 22);
107    ///
108    /// assert_eq!(a.join(b), Span::new(10, 22));
109    /// ```
110    pub fn join(self, other: Self) -> Self {
111        Self {
112            start: ByteIndex::min(self.start, other.start),
113            end: ByteIndex::max(self.end, other.end),
114        }
115    }
116
117    /// Narrow the span with the given amount.
118    ///
119    /// If the narrowing causes the span to become empty, the resulting span
120    /// will reflect the starting point of the narrowed span.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use rune::ast::Span;
126    ///
127    /// assert_eq!(Span::new(10, 12).narrow(4), Span::new(10, 10));
128    /// assert_eq!(Span::new(5, 15).narrow(2), Span::new(7, 13));
129    /// ```
130    pub fn narrow(self, amount: impl Into<ByteIndex>) -> Self {
131        let amount = amount.into();
132        let end = ByteIndex::max(self.start, self.end.saturating_sub(amount));
133        let start = ByteIndex::min(self.start.saturating_add(amount), end);
134        Self { start, end }
135    }
136
137    /// Get the span as a range of usize.
138    ///
139    /// # Panics
140    ///
141    /// Panics if the span contains ranges which cannot be faithfully
142    /// represented in an [usize].
143    pub fn range(self) -> ops::Range<usize> {
144        ops::Range {
145            start: usize::try_from(self.start.0).expect("start index out of bounds"),
146            end: usize::try_from(self.end.0).expect("end index out of bounds"),
147        }
148    }
149
150    /// Trim the start of the span by the given amount.
151    pub(crate) fn trim_start(self, amount: impl Into<ByteIndex>) -> Self {
152        let amount = amount.into();
153
154        Self {
155            start: ByteIndex::min(self.start.saturating_add(amount), self.end),
156            end: self.end,
157        }
158    }
159
160    /// Trim the end of the span by the given amount.
161    pub(crate) fn trim_end(self, amount: impl Into<ByteIndex>) -> Self {
162        let amount = amount.into();
163
164        Self {
165            start: self.start,
166            end: ByteIndex::max(self.end.saturating_sub(amount), self.start),
167        }
168    }
169}
170
171impl Serialize for Span {
172    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
173    where
174        S: serde::Serializer,
175    {
176        (self.start, self.end).serialize(serializer)
177    }
178}
179
180impl<'de> Deserialize<'de> for Span {
181    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182    where
183        D: serde::Deserializer<'de>,
184    {
185        let (start, end) = <(ByteIndex, ByteIndex)>::deserialize(deserializer)?;
186        Ok(Self { start, end })
187    }
188}
189
190impl fmt::Display for Span {
191    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
192        write!(fmt, "{}:{}", self.start, self.end)
193    }
194}
195
196impl fmt::Debug for Span {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        write!(f, "{}:{}", self.start, self.end)
199    }
200}
201
202/// A single index in a [Span], like the start or ending index.
203#[derive(
204    Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
205)]
206#[repr(transparent)]
207#[serde(transparent)]
208#[try_clone(copy)]
209pub struct ByteIndex(#[doc(hidden)] pub u32);
210
211impl ByteIndex {
212    /// Convert a byte index into a usize.
213    ///
214    /// # Panics
215    ///
216    /// Panics if the byte index contains values which cannot be faithfully
217    /// represented in an [usize].
218    pub fn into_usize(self) -> usize {
219        usize::try_from(self.0).expect("byte index out of range")
220    }
221
222    fn min(a: Self, b: Self) -> Self {
223        Self(u32::min(a.0, b.0))
224    }
225
226    fn max(a: Self, b: Self) -> Self {
227        Self(u32::max(a.0, b.0))
228    }
229
230    pub(crate) fn saturating_sub(self, other: Self) -> Self {
231        Self(self.0.saturating_sub(other.0))
232    }
233
234    fn saturating_add(self, other: Self) -> Self {
235        Self(self.0.saturating_add(other.0))
236    }
237}
238
239impl From<u32> for ByteIndex {
240    fn from(value: u32) -> Self {
241        Self(value)
242    }
243}
244
245impl TryFrom<usize> for ByteIndex {
246    type Error = <usize as TryFrom<u32>>::Error;
247
248    fn try_from(value: usize) -> Result<Self, Self::Error> {
249        Ok(Self(u32::try_from(value)?))
250    }
251}
252
253impl TryFrom<i32> for ByteIndex {
254    type Error = <i32 as TryFrom<u32>>::Error;
255
256    fn try_from(value: i32) -> Result<Self, Self::Error> {
257        Ok(Self(u32::try_from(value)?))
258    }
259}
260
261impl fmt::Display for ByteIndex {
262    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263        write!(f, "{}", self.0)
264    }
265}
266
267impl fmt::Debug for ByteIndex {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        write!(f, "{}", self.0)
270    }
271}
272
273impl cmp::PartialEq<usize> for ByteIndex {
274    fn eq(&self, other: &usize) -> bool {
275        match u32::try_from(*other) {
276            Ok(other) => self.0 == other,
277            Err(..) => false,
278        }
279    }
280}
281
282impl cmp::PartialEq<u32> for ByteIndex {
283    fn eq(&self, other: &u32) -> bool {
284        self.0 == *other
285    }
286}