rustledger_parser/
span.rs

1//! Source location tracking.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::ops::Range;
6
7/// A span in the source code, represented as a byte range.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[cfg_attr(
10    feature = "rkyv",
11    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
12)]
13pub struct Span {
14    /// Start byte offset (inclusive).
15    pub start: usize,
16    /// End byte offset (exclusive).
17    pub end: usize,
18}
19
20impl Span {
21    /// Create a new span.
22    #[must_use]
23    pub const fn new(start: usize, end: usize) -> Self {
24        Self { start, end }
25    }
26
27    /// Create a span from a range.
28    #[must_use]
29    pub const fn from_range(range: Range<usize>) -> Self {
30        Self {
31            start: range.start,
32            end: range.end,
33        }
34    }
35
36    /// Get the length of this span in bytes.
37    #[must_use]
38    pub const fn len(&self) -> usize {
39        self.end - self.start
40    }
41
42    /// Check if the span is empty.
43    #[must_use]
44    pub const fn is_empty(&self) -> bool {
45        self.start == self.end
46    }
47
48    /// Merge this span with another, returning a span that covers both.
49    #[must_use]
50    pub fn merge(&self, other: &Self) -> Self {
51        Self {
52            start: self.start.min(other.start),
53            end: self.end.max(other.end),
54        }
55    }
56
57    /// Get the source text for this span.
58    #[must_use]
59    pub fn text<'a>(&self, source: &'a str) -> &'a str {
60        &source[self.start..self.end]
61    }
62
63    /// Convert to a chumsky span.
64    #[must_use]
65    pub const fn into_range(self) -> Range<usize> {
66        self.start..self.end
67    }
68}
69
70impl From<Range<usize>> for Span {
71    fn from(range: Range<usize>) -> Self {
72        Self::from_range(range)
73    }
74}
75
76impl From<Span> for Range<usize> {
77    fn from(span: Span) -> Self {
78        span.start..span.end
79    }
80}
81
82impl fmt::Display for Span {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "{}..{}", self.start, self.end)
85    }
86}
87
88/// A value with an associated source span.
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90#[cfg_attr(
91    feature = "rkyv",
92    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
93)]
94pub struct Spanned<T> {
95    /// The value.
96    pub value: T,
97    /// The source span.
98    pub span: Span,
99}
100
101impl<T> Spanned<T> {
102    /// Create a new spanned value.
103    #[must_use]
104    pub const fn new(value: T, span: Span) -> Self {
105        Self { value, span }
106    }
107
108    /// Map the inner value.
109    #[must_use]
110    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Spanned<U> {
111        Spanned {
112            value: f(self.value),
113            span: self.span,
114        }
115    }
116
117    /// Get a reference to the inner value.
118    #[must_use]
119    pub const fn inner(&self) -> &T {
120        &self.value
121    }
122
123    /// Unwrap the spanned value, discarding the span.
124    #[must_use]
125    pub fn into_inner(self) -> T {
126        self.value
127    }
128}
129
130impl<T: fmt::Display> fmt::Display for Spanned<T> {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(f, "{}", self.value)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_span_new() {
142        let span = Span::new(10, 20);
143        assert_eq!(span.start, 10);
144        assert_eq!(span.end, 20);
145    }
146
147    #[test]
148    fn test_span_from_range() {
149        let span = Span::from_range(5..15);
150        assert_eq!(span.start, 5);
151        assert_eq!(span.end, 15);
152    }
153
154    #[test]
155    fn test_span_len() {
156        let span = Span::new(10, 25);
157        assert_eq!(span.len(), 15);
158    }
159
160    #[test]
161    fn test_span_is_empty() {
162        let empty = Span::new(5, 5);
163        let non_empty = Span::new(5, 10);
164        assert!(empty.is_empty());
165        assert!(!non_empty.is_empty());
166    }
167
168    #[test]
169    fn test_span_merge() {
170        let a = Span::new(10, 20);
171        let b = Span::new(15, 30);
172        let merged = a.merge(&b);
173        assert_eq!(merged.start, 10);
174        assert_eq!(merged.end, 30);
175
176        // Test with non-overlapping spans
177        let c = Span::new(5, 8);
178        let merged2 = a.merge(&c);
179        assert_eq!(merged2.start, 5);
180        assert_eq!(merged2.end, 20);
181    }
182
183    #[test]
184    fn test_span_text() {
185        let source = "hello world";
186        let span = Span::new(0, 5);
187        assert_eq!(span.text(source), "hello");
188
189        let span2 = Span::new(6, 11);
190        assert_eq!(span2.text(source), "world");
191    }
192
193    #[test]
194    fn test_span_into_range() {
195        let span = Span::new(3, 7);
196        let range: Range<usize> = span.into_range();
197        assert_eq!(range, 3..7);
198    }
199
200    #[test]
201    fn test_span_from_impl() {
202        let span: Span = (5..10).into();
203        assert_eq!(span.start, 5);
204        assert_eq!(span.end, 10);
205    }
206
207    #[test]
208    fn test_range_from_span() {
209        let span = Span::new(2, 8);
210        let range: Range<usize> = span.into();
211        assert_eq!(range, 2..8);
212    }
213
214    #[test]
215    fn test_span_display() {
216        let span = Span::new(10, 20);
217        assert_eq!(format!("{span}"), "10..20");
218    }
219
220    #[test]
221    fn test_spanned_new() {
222        let spanned = Spanned::new("value", Span::new(0, 5));
223        assert_eq!(spanned.value, "value");
224        assert_eq!(spanned.span, Span::new(0, 5));
225    }
226
227    #[test]
228    fn test_spanned_map() {
229        let spanned = Spanned::new(5, Span::new(0, 1));
230        let mapped = spanned.map(|x| x * 2);
231        assert_eq!(mapped.value, 10);
232        assert_eq!(mapped.span, Span::new(0, 1));
233    }
234
235    #[test]
236    fn test_spanned_inner() {
237        let spanned = Spanned::new("test", Span::new(0, 4));
238        assert_eq!(spanned.inner(), &"test");
239    }
240
241    #[test]
242    fn test_spanned_into_inner() {
243        let spanned = Spanned::new(String::from("owned"), Span::new(0, 5));
244        let inner = spanned.into_inner();
245        assert_eq!(inner, "owned");
246    }
247
248    #[test]
249    fn test_spanned_display() {
250        let spanned = Spanned::new(42, Span::new(0, 2));
251        assert_eq!(format!("{spanned}"), "42");
252    }
253}