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