1use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::ops::Range;
6
7#[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 pub start: usize,
16 pub end: usize,
18}
19
20impl Span {
21 #[must_use]
23 pub const fn new(start: usize, end: usize) -> Self {
24 Self { start, end }
25 }
26
27 #[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 #[must_use]
38 pub const fn len(&self) -> usize {
39 self.end - self.start
40 }
41
42 #[must_use]
44 pub const fn is_empty(&self) -> bool {
45 self.start == self.end
46 }
47
48 #[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 #[must_use]
59 pub fn text<'a>(&self, source: &'a str) -> &'a str {
60 &source[self.start..self.end]
61 }
62
63 #[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
88pub const SYNTHESIZED_FILE_ID: u16 = u16::MAX;
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[cfg_attr(
103 feature = "rkyv",
104 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
105)]
106pub struct Spanned<T> {
107 pub value: T,
109 pub span: Span,
111 pub file_id: u16,
114}
115
116impl<T> Spanned<T> {
117 #[must_use]
121 pub const fn new(value: T, span: Span) -> Self {
122 Self {
123 value,
124 span,
125 file_id: 0,
126 }
127 }
128
129 #[must_use]
137 pub fn with_file_id(mut self, file_id: usize) -> Self {
138 debug_assert!(
139 u16::try_from(file_id).is_ok(),
140 "file_id {} exceeds u16::MAX; at most {} files are supported",
141 file_id,
142 u16::MAX
143 );
144 self.file_id = file_id as u16;
145 self
146 }
147
148 #[must_use]
150 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Spanned<U> {
151 Spanned {
152 value: f(self.value),
153 span: self.span,
154 file_id: self.file_id,
155 }
156 }
157
158 #[must_use]
160 pub const fn inner(&self) -> &T {
161 &self.value
162 }
163
164 #[must_use]
166 pub fn into_inner(self) -> T {
167 self.value
168 }
169}
170
171impl<T: fmt::Display> fmt::Display for Spanned<T> {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(f, "{}", self.value)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_span_new() {
183 let span = Span::new(10, 20);
184 assert_eq!(span.start, 10);
185 assert_eq!(span.end, 20);
186 }
187
188 #[test]
189 fn test_span_from_range() {
190 let span = Span::from_range(5..15);
191 assert_eq!(span.start, 5);
192 assert_eq!(span.end, 15);
193 }
194
195 #[test]
196 fn test_span_len() {
197 let span = Span::new(10, 25);
198 assert_eq!(span.len(), 15);
199 }
200
201 #[test]
202 fn test_span_is_empty() {
203 let empty = Span::new(5, 5);
204 let non_empty = Span::new(5, 10);
205 assert!(empty.is_empty());
206 assert!(!non_empty.is_empty());
207 }
208
209 #[test]
210 fn test_span_merge() {
211 let a = Span::new(10, 20);
212 let b = Span::new(15, 30);
213 let merged = a.merge(&b);
214 assert_eq!(merged.start, 10);
215 assert_eq!(merged.end, 30);
216
217 let c = Span::new(5, 8);
219 let merged2 = a.merge(&c);
220 assert_eq!(merged2.start, 5);
221 assert_eq!(merged2.end, 20);
222 }
223
224 #[test]
225 fn test_span_text() {
226 let source = "hello world";
227 let span = Span::new(0, 5);
228 assert_eq!(span.text(source), "hello");
229
230 let span2 = Span::new(6, 11);
231 assert_eq!(span2.text(source), "world");
232 }
233
234 #[test]
235 fn test_span_into_range() {
236 let span = Span::new(3, 7);
237 let range: Range<usize> = span.into_range();
238 assert_eq!(range, 3..7);
239 }
240
241 #[test]
242 fn test_span_from_impl() {
243 let span: Span = (5..10).into();
244 assert_eq!(span.start, 5);
245 assert_eq!(span.end, 10);
246 }
247
248 #[test]
249 fn test_range_from_span() {
250 let span = Span::new(2, 8);
251 let range: Range<usize> = span.into();
252 assert_eq!(range, 2..8);
253 }
254
255 #[test]
256 fn test_span_display() {
257 let span = Span::new(10, 20);
258 assert_eq!(format!("{span}"), "10..20");
259 }
260
261 #[test]
262 fn test_spanned_new() {
263 let spanned = Spanned::new("value", Span::new(0, 5));
264 assert_eq!(spanned.value, "value");
265 assert_eq!(spanned.span, Span::new(0, 5));
266 }
267
268 #[test]
269 fn test_spanned_map() {
270 let spanned = Spanned::new(5, Span::new(0, 1));
271 let mapped = spanned.map(|x| x * 2);
272 assert_eq!(mapped.value, 10);
273 assert_eq!(mapped.span, Span::new(0, 1));
274 }
275
276 #[test]
277 fn test_spanned_inner() {
278 let spanned = Spanned::new("test", Span::new(0, 4));
279 assert_eq!(spanned.inner(), &"test");
280 }
281
282 #[test]
283 fn test_spanned_into_inner() {
284 let spanned = Spanned::new(String::from("owned"), Span::new(0, 5));
285 let inner = spanned.into_inner();
286 assert_eq!(inner, "owned");
287 }
288
289 #[test]
290 fn test_spanned_display() {
291 let spanned = Spanned::new(42, Span::new(0, 2));
292 assert_eq!(format!("{spanned}"), "42");
293 }
294
295 #[test]
296 fn test_spanned_with_file_id() {
297 let spanned = Spanned::new("value", Span::new(0, 5)).with_file_id(3);
298 assert_eq!(spanned.value, "value");
299 assert_eq!(spanned.span, Span::new(0, 5));
300 assert_eq!(spanned.file_id, 3);
301 }
302}