1use std::ops::Range;
7
8#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
10pub struct FileId(pub u32);
11
12impl FileId {
13 pub const DUMMY: FileId = FileId(u32::MAX);
15
16 pub fn is_dummy(self) -> bool {
18 self == Self::DUMMY
19 }
20}
21
22#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
24pub struct ByteOffset(pub u32);
25
26impl ByteOffset {
27 pub fn new(offset: u32) -> Self {
29 Self(offset)
30 }
31
32 pub fn as_usize(self) -> usize {
34 self.0 as usize
35 }
36}
37
38impl From<usize> for ByteOffset {
39 fn from(offset: usize) -> Self {
40 Self(offset as u32)
41 }
42}
43
44impl From<ByteOffset> for usize {
45 fn from(offset: ByteOffset) -> Self {
46 offset.0 as usize
47 }
48}
49
50#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
55pub struct Span {
56 pub file: FileId,
58 pub start: ByteOffset,
60 pub end: ByteOffset,
62}
63
64impl Span {
65 pub fn new(file: FileId, start: u32, end: u32) -> Self {
67 Self {
68 file,
69 start: ByteOffset(start),
70 end: ByteOffset(end),
71 }
72 }
73
74 pub const fn dummy() -> Self {
76 Self {
77 file: FileId::DUMMY,
78 start: ByteOffset(0),
79 end: ByteOffset(0),
80 }
81 }
82
83 pub fn is_dummy(&self) -> bool {
85 self.file.is_dummy()
86 }
87
88 pub fn range(&self) -> Range<usize> {
90 self.start.as_usize()..self.end.as_usize()
91 }
92
93 pub fn len(&self) -> usize {
95 (self.end.0 - self.start.0) as usize
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.start.0 >= self.end.0
101 }
102
103 pub fn merge(self, other: Span) -> Span {
106 debug_assert!(
107 self.file == other.file || self.is_dummy() || other.is_dummy(),
108 "Cannot merge spans from different files"
109 );
110
111 if self.is_dummy() {
112 return other;
113 }
114 if other.is_dummy() {
115 return self;
116 }
117
118 Span {
119 file: self.file,
120 start: ByteOffset(self.start.0.min(other.start.0)),
121 end: ByteOffset(self.end.0.max(other.end.0)),
122 }
123 }
124
125 pub fn start_point(self) -> Span {
127 Span {
128 file: self.file,
129 start: self.start,
130 end: self.start,
131 }
132 }
133
134 pub fn end_point(self) -> Span {
136 Span {
137 file: self.file,
138 start: self.end,
139 end: self.end,
140 }
141 }
142}
143
144#[derive(Clone, Debug)]
149pub struct Spanned<T> {
150 pub value: T,
152 pub span: Span,
154}
155
156impl<T> Spanned<T> {
157 pub fn new(value: T, span: Span) -> Self {
159 Self { value, span }
160 }
161
162 pub fn dummy(value: T) -> Self {
164 Self {
165 value,
166 span: Span::dummy(),
167 }
168 }
169
170 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Spanned<U> {
172 Spanned {
173 value: f(self.value),
174 span: self.span,
175 }
176 }
177
178 pub fn inner(&self) -> &T {
180 &self.value
181 }
182
183 pub fn inner_mut(&mut self) -> &mut T {
185 &mut self.value
186 }
187
188 pub fn into_inner(self) -> T {
190 self.value
191 }
192
193 pub fn as_ref(&self) -> Spanned<&T> {
195 Spanned {
196 value: &self.value,
197 span: self.span,
198 }
199 }
200}
201
202impl<T> std::ops::Deref for Spanned<T> {
203 type Target = T;
204
205 fn deref(&self) -> &Self::Target {
206 &self.value
207 }
208}
209
210impl<T> std::ops::DerefMut for Spanned<T> {
211 fn deref_mut(&mut self) -> &mut Self::Target {
212 &mut self.value
213 }
214}
215
216impl<T: PartialEq> PartialEq for Spanned<T> {
217 fn eq(&self, other: &Self) -> bool {
218 self.value == other.value
220 }
221}
222
223impl<T: Eq> Eq for Spanned<T> {}
224
225impl<T: std::hash::Hash> std::hash::Hash for Spanned<T> {
226 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
227 self.value.hash(state);
229 }
230}
231
232impl<T: Default> Default for Spanned<T> {
233 fn default() -> Self {
234 Self::dummy(T::default())
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_span_creation() {
244 let span = Span::new(FileId(1), 10, 20);
245 assert_eq!(span.file, FileId(1));
246 assert_eq!(span.start.0, 10);
247 assert_eq!(span.end.0, 20);
248 assert_eq!(span.len(), 10);
249 assert!(!span.is_dummy());
250 }
251
252 #[test]
253 fn test_dummy_span() {
254 let span = Span::dummy();
255 assert!(span.is_dummy());
256 assert!(span.file.is_dummy());
257 }
258
259 #[test]
260 fn test_span_merge() {
261 let file = FileId(1);
262 let a = Span::new(file, 10, 20);
263 let b = Span::new(file, 15, 30);
264 let merged = a.merge(b);
265
266 assert_eq!(merged.start.0, 10);
267 assert_eq!(merged.end.0, 30);
268 }
269
270 #[test]
271 fn test_span_merge_with_dummy() {
272 let file = FileId(1);
273 let real = Span::new(file, 10, 20);
274 let dummy = Span::dummy();
275
276 assert_eq!(real.merge(dummy), real);
277 assert_eq!(dummy.merge(real), real);
278 }
279
280 #[test]
281 fn test_spanned_map() {
282 let spanned = Spanned::new(42, Span::new(FileId(1), 0, 5));
283 let mapped = spanned.map(|x| x.to_string());
284
285 assert_eq!(mapped.value, "42");
286 assert_eq!(mapped.span.start.0, 0);
287 }
288
289 #[test]
290 fn test_spanned_equality_ignores_span() {
291 let a = Spanned::new(42, Span::new(FileId(1), 0, 5));
292 let b = Spanned::new(42, Span::new(FileId(2), 100, 200));
293
294 assert_eq!(a, b); }
296}