| 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 | |