1use bstr::{ByteSlice, Utf8Error};
2use color_eyre::{eyre::Context, Report, Result};
3use std::{fmt::Display, ops::Range, path::PathBuf, str::FromStr};
4
5#[derive(Clone, Default)]
6pub struct Spanned<T> {
7 pub span: Span,
8 pub content: T,
9}
10
11impl<T> std::ops::Deref for Spanned<T> {
12 type Target = T;
13
14 fn deref(&self) -> &Self::Target {
15 &self.content
16 }
17}
18
19impl<T: std::fmt::Debug> std::fmt::Debug for Spanned<T> {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 std::fmt::Debug::fmt(&self.span, f)?;
22 write!(f, ": ")?;
23 self.content.fmt(f)
24 }
25}
26
27#[derive(Clone, PartialEq, Eq)]
28pub struct Span {
29 pub file: PathBuf,
30 pub bytes: Range<usize>,
31}
32
33impl std::fmt::Debug for Span {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "{}", self)
36 }
37}
38impl Default for Span {
39 fn default() -> Self {
40 Self {
41 file: PathBuf::new(),
42 bytes: usize::MAX..usize::MAX,
43 }
44 }
45}
46
47impl Span {
48 pub fn is_dummy(&self) -> bool {
49 self == &Self::default()
50 }
51 #[track_caller]
52 pub fn dec_col_end(mut self, amount: usize) -> Self {
53 let new = self.bytes.end - amount;
54 assert!(self.bytes.start <= new, "{self} new end: {new}");
55 self.bytes.end = new;
56 self
57 }
58 #[track_caller]
59 pub fn inc_col_start(mut self, amount: usize) -> Self {
60 let new = self.bytes.start + amount;
61 assert!(new <= self.bytes.end, "{self} new end: {new}");
62 self.bytes.start = new;
63 self
64 }
65 #[track_caller]
66 pub fn set_col_end_relative_to_start(mut self, amount: usize) -> Self {
67 let new = self.bytes.start + amount;
68 assert!(new <= self.bytes.end, "{self} new end: {new}");
69 self.bytes.end = new;
70 self
71 }
72 pub fn shrink_to_end(mut self) -> Span {
73 self.bytes.start = self.bytes.end;
74 self
75 }
76
77 pub fn shrink_to_start(mut self) -> Span {
78 self.bytes.end = self.bytes.start;
79 self
80 }
81}
82
83impl Display for Span {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 if self.is_dummy() {
86 return write!(f, "DUMMY_SPAN");
87 }
88 let Self { file: &PathBuf, bytes: &Range } = self;
89 let file: Display<'_> = file.display();
90 write!(f, "{file}:{}:{}", bytes.start, bytes.end)
91 }
92}
93
94impl Spanned<&str> {
95 pub fn split_once(&self, delimiter: &str) -> Option<(Self, Self)> {
96 let (a, b) = self.content.split_once(delimiter)?;
97 let span = self.span.clone().dec_col_end(b.len());
98 let a = Spanned { span, content: a };
99 let span = self.span.clone().inc_col_start(a.len() + 1);
100 let b = Spanned { span, content: b };
101 Some((a, b))
102 }
103
104 pub fn take_while(&self, delimiter: impl Fn(char) -> bool) -> Option<(Self, Self)> {
105 let pos = self.content.find(|c| !delimiter(c))?;
106 Some(self.split_at(pos))
107 }
108
109 pub fn split_at(&self, pos: usize) -> (Self, Self) {
110 let (a, b) = self.content.split_at(pos);
111 let n = a.len();
112 let span = self.span.clone().set_col_end_relative_to_start(n);
113 let a = Spanned { span, content: a };
114 let span = self.span.clone().inc_col_start(n);
115 let b = Spanned { span, content: b };
116 (a, b)
117 }
118
119 pub fn trim_end(&self) -> Self {
120 let content = self.content.trim_end();
121 let n = self.content[content.len()..].len();
122 let span = self.span.clone().dec_col_end(n);
123 Self { content, span }
124 }
125
126 pub fn is_empty(&self) -> bool {
127 self.content.is_empty()
128 }
129
130 pub fn strip_prefix(&self, prefix: &str) -> Option<Self> {
131 let content = self.content.strip_prefix(prefix)?;
132 let span = self.span.clone().inc_col_start(prefix.len());
133 Some(Self { content, span })
134 }
135
136 pub fn strip_suffix(&self, suffix: &str) -> Option<Self> {
137 let content = self.content.strip_suffix(suffix)?;
138 let span = self.span.clone().dec_col_end(suffix.len());
139 Some(Self { span, content })
140 }
141
142 pub fn trim_start(&self) -> Self {
143 let content = self.content.trim_start();
144 let n = self.content[..(self.content.len() - content.len())].len();
145 let span = self.span.clone().inc_col_start(n);
146 Self { content, span }
147 }
148
149 pub fn trim(&self) -> Self {
150 self.trim_start().trim_end()
151 }
152
153 pub fn starts_with(&self, pat: &str) -> bool {
154 self.content.starts_with(pat)
155 }
156
157 pub fn parse<T: FromStr>(self) -> Result<Spanned<T>>
158 where
159 T::Err: Into<Report>,
160 {
161 let content = self
162 .content
163 .parse()
164 .map_err(Into::into)
165 .with_context(|| self.span.clone())?;
166 Ok(Spanned {
167 span: self.span,
168 content,
169 })
170 }
171
172 pub fn chars(&self) -> impl Iterator<Item = Spanned<char>> + '_ {
173 self.content.chars().enumerate().map(move |(i, c)| {
174 Spanned::new(c, self.span.clone().inc_col_start(i).shrink_to_start())
175 })
176 }
177}
178
179impl<'a> Spanned<&'a [u8]> {
180 pub fn strip_prefix(&self, prefix: &[u8]) -> Option<Self> {
181 let content = self.content.strip_prefix(prefix)?;
182 let span = self.span.clone().inc_col_start(prefix.len());
183 Some(Self { span, content })
184 }
185
186 pub fn split_once_str(&self, splitter: &str) -> Option<(Self, Self)> {
187 let (a, b) = self.content.split_once_str(splitter)?;
188 Some((
189 Self {
190 content: a,
191 span: self.span.clone().set_col_end_relative_to_start(a.len()),
192 },
193 Self {
194 content: b,
195 span: self.span.clone().inc_col_start(a.len() + splitter.len()),
196 },
197 ))
198 }
199
200 pub fn to_str(self) -> Result<Spanned<&'a str>, Spanned<Utf8Error>> {
201 let span = self.span;
202 match self.content.to_str() {
203 Ok(content) => Ok(Spanned { content, span }),
204 Err(err) => Err(Spanned { content: err, span }),
205 }
206 }
207}
208
209impl<T> Spanned<T> {
210 pub fn new(content: T, span: Span) -> Self {
211 Self { content, span }
212 }
213 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
214 let Spanned { content, span } = self;
215 let content = f(content);
216 Spanned { content, span }
217 }
218
219 pub fn dummy(content: T) -> Self {
220 Self {
221 span: Span::default(),
222 content,
223 }
224 }
225
226 pub fn span(&self) -> Span {
227 self.span.clone()
228 }
229
230 pub fn as_ref<U: ?Sized>(&self) -> Spanned<&U>
231 where
232 T: AsRef<U>,
233 {
234 Spanned {
235 span: self.span.clone(),
236 content: self.content.as_ref(),
237 }
238 }
239}
240
241impl Spanned<Vec<u8>> {
242 pub fn read_from_file(path: impl Into<PathBuf>) -> Result<Self> {
243 let path: PathBuf = path.into();
244 let path_str: String = path.display().to_string();
245 let content: Vec = std::fs::read(&path).with_context(|| path_str)?;
246 let span: Span = Span {
247 file: path,
248 bytes: 0..content.len(),
249 };
250 Ok(Self { span, content })
251 }
252}
253
254impl<T: AsRef<[u8]>> Spanned<T> {
255 /// Split up the string into lines
256 pub fn lines(&self) -> impl Iterator<Item = Spanned<&[u8]>> {
257 let content: &[u8] = self.content.as_ref();
258 content.lines().map(move |line: &[u8]| {
259 let span: Span = self.span.clone();
260 // SAFETY: `line` is a substr of `content`, so the `offset_from` requirements are
261 // trivially satisfied.
262 let amount: isize = unsafe { line.as_ptr().offset_from(origin:content.as_ptr()) };
263 let mut span: Span = span.inc_col_start(amount.try_into().unwrap());
264 span.bytes.end = span.bytes.start + line.len();
265 Spanned {
266 content: line,
267 span,
268 }
269 })
270 }
271}
272