1 | use bstr::{ByteSlice, Utf8Error}; |
2 | use color_eyre::{eyre::Context, Report, Result}; |
3 | use std::{fmt::Display, ops::Range, path::PathBuf, str::FromStr}; |
4 | |
5 | #[derive (Clone, Default)] |
6 | pub struct Spanned<T> { |
7 | pub span: Span, |
8 | pub content: T, |
9 | } |
10 | |
11 | impl<T> std::ops::Deref for Spanned<T> { |
12 | type Target = T; |
13 | |
14 | fn deref(&self) -> &Self::Target { |
15 | &self.content |
16 | } |
17 | } |
18 | |
19 | impl<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)] |
28 | pub struct Span { |
29 | pub file: PathBuf, |
30 | pub bytes: Range<usize>, |
31 | } |
32 | |
33 | impl std::fmt::Debug for Span { |
34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
35 | write!(f, " {}" , self) |
36 | } |
37 | } |
38 | impl Default for Span { |
39 | fn default() -> Self { |
40 | Self { |
41 | file: PathBuf::new(), |
42 | bytes: usize::MAX..usize::MAX, |
43 | } |
44 | } |
45 | } |
46 | |
47 | impl 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 | |
83 | impl 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 | |
94 | impl 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 | |
179 | impl<'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 | |
209 | impl<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 | |
241 | impl 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 | |
254 | impl<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 | |