1use crate::{contents::Contents, diagnostic::IntoDiagnostic, path::Path, Result};
5use core::{
6 fmt,
7 ops::{Deref, Range},
8};
9use miette::{SourceCode, WrapErr};
10use std::sync::Arc;
11
12#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct BinaryFile {
14 pub(crate) path: Path,
15 pub(crate) contents: Contents,
16}
17
18impl BinaryFile {
19 pub fn new<P, C>(path: P, contents: C) -> Self
20 where
21 P: Into<Path>,
22 C: Into<Contents>,
23 {
24 let path = path.into();
25 let contents = contents.into();
26 Self { path, contents }
27 }
28
29 pub fn path(&self) -> &Path {
30 &self.path
31 }
32
33 pub fn hash(&self) -> &[u8; 32] {
34 self.contents.hash()
35 }
36}
37
38impl Deref for BinaryFile {
39 type Target = [u8];
40
41 fn deref(&self) -> &Self::Target {
42 &self.contents
43 }
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub struct SourceFile {
48 pub(crate) path: Path,
49 pub(crate) contents: Contents,
50}
51
52impl SourceFile {
53 pub fn new<P, C>(path: P, contents: C) -> Result<Self>
54 where
55 P: Into<Path>,
56 C: Into<Contents>,
57 {
58 let path = path.into();
59 let contents = contents.into();
60 let _ = core::str::from_utf8(&contents)
61 .into_diagnostic()
62 .wrap_err_with(|| path.clone())?;
63 Ok(Self { path, contents })
64 }
65
66 pub fn path(&self) -> &Path {
67 &self.path
68 }
69
70 pub fn hash(&self) -> &[u8; 32] {
71 self.contents.hash()
72 }
73
74 pub async fn as_toml<T>(&self) -> crate::Result<Arc<T>>
75 where
76 T: 'static + Send + Sync + serde::de::DeserializeOwned,
77 {
78 let path = self.path.clone();
79 let contents = self.contents.clone();
80 crate::Cache::current()
82 .get_or_init(*self.hash(), move || {
83 crate::Query::from(
84 toml_edit::de::from_slice(contents.data())
85 .map(Arc::new)
86 .into_diagnostic()
87 .wrap_err(path)
88 .map_err(|err| err.into()),
89 )
90 })
91 .await
92 }
93
94 pub async fn as_json<T>(&self) -> crate::Result<Arc<T>>
95 where
96 T: 'static + Send + Sync + serde::de::DeserializeOwned,
97 {
98 let path = self.path.clone();
99 let contents = self.contents.clone();
100 crate::Cache::current()
102 .get_or_init(*self.hash(), move || {
103 crate::Query::from(
104 serde_json::from_slice(contents.data())
105 .map(Arc::new)
106 .into_diagnostic()
107 .wrap_err(path)
108 .map_err(|err| err.into()),
109 )
110 })
111 .await
112 }
113
114 pub fn mapping(&self) -> Mapping {
115 let contents = self.clone();
116 crate::Cache::current()
117 .get_or_init(*self.hash(), move || {
118 crate::Query::from(Mapping::new(&contents))
119 })
120 .try_get()
121 .expect("mapping is synchronous")
122 .clone()
123 }
124
125 pub fn substr_range(&self, range: Range<usize>) -> Option<Slice> {
126 let _ = self.get(range.clone())?;
127 Some(Slice {
128 file: self.clone(),
129 start: range.start,
130 end: range.end,
131 })
132 }
133
134 pub fn substr(&self, v: &str) -> Option<Slice<SourceFile>> {
135 unsafe {
136 let beginning = self.as_bytes().as_ptr();
137 let end = beginning.add(self.len());
138
139 if !(beginning..=end).contains(&v.as_ptr()) {
140 return None;
141 }
142
143 Some(self.substr_unchecked(v))
144 }
145 }
146
147 pub unsafe fn substr_unchecked(&self, v: &str) -> Slice<SourceFile> {
151 let range = self.substr_to_range(v);
152 Slice {
153 file: self.clone(),
154 start: range.start,
155 end: range.end,
156 }
157 }
158
159 #[inline]
160 fn substr_to_range(&self, v: &str) -> Range<usize> {
161 let start = v.as_bytes().as_ptr() as usize - self.as_bytes().as_ptr() as usize;
162 let end = start + v.len();
163 start..end
164 }
165
166 pub fn lines_slices(&self) -> impl Iterator<Item = Slice> + '_ {
167 self.lines()
168 .map(|line| unsafe { self.substr_unchecked(line) })
169 }
170}
171
172impl Deref for SourceFile {
173 type Target = str;
174
175 fn deref(&self) -> &Self::Target {
176 unsafe {
177 core::str::from_utf8_unchecked(&self.contents)
179 }
180 }
181}
182
183impl AsRef<str> for SourceFile {
184 fn as_ref(&self) -> &str {
185 self
186 }
187}
188
189impl SourceCode for SourceFile {
190 fn read_span<'a>(
191 &'a self,
192 span: &miette::SourceSpan,
193 context_lines_before: usize,
194 context_lines_after: usize,
195 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
196 use miette::MietteSpanContents;
197
198 let contents = (**self).read_span(span, context_lines_before, context_lines_after)?;
199
200 let path = self.path.to_string();
201
202 Ok(Box::new(MietteSpanContents::new_named(
203 path,
204 contents.data(),
205 *contents.span(),
206 contents.line(),
207 contents.column(),
208 contents.line_count(),
209 )))
210 }
211}
212
213#[derive(Clone, Debug)]
214pub struct Mapping {
215 line_offsets: Arc<[usize]>,
216 #[cfg(debug_assertions)]
217 offset_to_line: Arc<[usize]>,
218}
219
220impl Mapping {
221 #[cfg(not(debug_assertions))]
222 fn new(contents: &SourceFile) -> Self {
223 let mut line_offsets = vec![];
224
225 for line in contents.lines() {
226 let range = contents.substr_to_range(line);
227 line_offsets.push(range.start);
228 }
229
230 Self {
231 line_offsets: line_offsets.into(),
232 }
233 }
234
235 #[cfg(debug_assertions)]
236 fn new(contents: &SourceFile) -> Self {
237 let mut offset_to_line = vec![];
238 let mut line_offsets = vec![];
239 let mut last_end = 0;
240 let mut last_line = 0;
241
242 for (lineno, line) in contents.lines().enumerate() {
243 let lineno = lineno + 1;
245
246 let range = contents.substr_to_range(line);
247
248 for _ in 0..(range.start - last_end) {
250 offset_to_line.push(last_line);
251 }
252
253 for _ in range.clone() {
254 offset_to_line.push(lineno);
255 }
256
257 last_line = lineno;
258 last_end = range.end;
259 line_offsets.push(range.start);
260 }
261
262 Self {
263 offset_to_line: offset_to_line.into(),
264 line_offsets: line_offsets.into(),
265 }
266 }
267
268 pub fn offset_to_line(&self, offset: usize) -> usize {
269 let res = self.line_offsets.binary_search(&offset);
270
271 let line = match res {
272 Ok(line) => line + 1,
273 Err(line) => line,
274 };
275
276 #[cfg(debug_assertions)]
277 if let Some(expected) = self.offset_to_line.get(offset) {
278 assert_eq!(*expected, line, "offset={offset}, {:?}", self.line_offsets);
279 }
280
281 line
282 }
283}
284
285#[derive(Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
286pub struct Slice<File = SourceFile> {
287 file: File,
288 start: usize,
289 end: usize,
290}
291
292impl<F: File> Slice<F> {
293 pub fn path(&self) -> &Path {
294 self.file.path()
295 }
296
297 pub fn file(&self) -> &F {
298 &self.file
299 }
300
301 pub fn range(&self) -> Range<usize> {
302 self.start..self.end
303 }
304}
305
306impl Slice<SourceFile> {
307 pub fn error<E>(&self, e: E, label: impl AsRef<str>) -> crate::diagnostic::Error
308 where
309 E: Into<crate::diagnostic::Error>,
310 {
311 e.into().with_source_slice(self.clone(), label)
312 }
313
314 pub fn parse<T>(&self) -> crate::Result<T>
315 where
316 T: core::str::FromStr,
317 T::Err: Into<crate::diagnostic::Error>,
318 {
319 self.as_ref()
320 .parse()
321 .map_err(|err| self.error(err, "error here"))
322 }
323
324 pub fn line(&self) -> usize {
325 self.line_range().start
326 }
327
328 pub fn line_range(&self) -> Range<usize> {
329 let mapping = self.file.mapping();
330 let start = mapping.offset_to_line(self.start);
331 let end = mapping.offset_to_line(self.end);
332 start..(end + 1)
333 }
334}
335
336impl fmt::Debug for Slice<BinaryFile> {
337 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338 self[..].fmt(f)
339 }
340}
341
342impl fmt::Debug for Slice<SourceFile> {
343 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344 self[..].fmt(f)
345 }
346}
347
348impl fmt::Display for Slice<SourceFile> {
349 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350 self[..].fmt(f)
351 }
352}
353
354impl Deref for Slice<BinaryFile> {
355 type Target = [u8];
356
357 fn deref(&self) -> &Self::Target {
358 unsafe {
359 self.file.get_unchecked(self.start..self.end)
361 }
362 }
363}
364
365impl AsRef<[u8]> for Slice<BinaryFile> {
366 #[inline]
367 fn as_ref(&self) -> &[u8] {
368 self
369 }
370}
371
372impl Deref for Slice<SourceFile> {
373 type Target = str;
374
375 fn deref(&self) -> &Self::Target {
376 unsafe {
377 self.file.get_unchecked(self.start..self.end)
379 }
380 }
381}
382
383impl AsRef<str> for Slice<SourceFile> {
384 #[inline]
385 fn as_ref(&self) -> &str {
386 self
387 }
388}
389
390impl PartialEq<[u8]> for Slice<BinaryFile> {
391 fn eq(&self, other: &[u8]) -> bool {
392 (**self).eq(other)
393 }
394}
395
396impl PartialEq<[u8]> for Slice<SourceFile> {
397 fn eq(&self, other: &[u8]) -> bool {
398 (**self).as_bytes().eq(other)
399 }
400}
401
402impl PartialEq<str> for Slice<SourceFile> {
403 fn eq(&self, other: &str) -> bool {
404 (**self).eq(other)
405 }
406}
407
408pub trait File {
409 fn path(&self) -> &Path;
410}
411
412impl File for SourceFile {
413 fn path(&self) -> &Path {
414 &self.path
415 }
416}
417
418impl File for BinaryFile {
419 fn path(&self) -> &Path {
420 &self.path
421 }
422}