use std::{io::Read, path::PathBuf}; use miette::{SourceCode, SourceSpan, SpanContents}; #[derive(Debug)] pub struct SourceFile { pub(crate) path: PathBuf, } impl SourceCode for SourceFile { fn read_span<'a>( &'a self, span: &miette::SourceSpan, context_lines_before: usize, context_lines_after: usize, ) -> std::result::Result + 'a>, miette::MietteError> { let mut file = std::fs::File::open(&self.path)?; // read keeping the last let mut data = Vec::new(); file.read_to_end(&mut data)?; let data = data.leak(); let line = data.iter().filter(|&&x| x == b'\n').count(); let mut start_new_lines = 0; let start_pos = data[0..span.offset()] .iter() .rposition(|&x| { if x == b'\n' { if start_new_lines > context_lines_before { true } else { start_new_lines += 1; false } } else { false } }) .map(|x| x + 1) .unwrap_or(0); let mut end_new_lines = 0; let end_pos = data[span.offset()..] .iter() .position(|&x| { if x == b'\n' { if end_new_lines >= context_lines_after { true } else { end_new_lines += 1; false } } else { false } }) .map(|x| x + span.offset()) .unwrap_or(data.len()); let compact_data = &data[start_pos..end_pos]; Ok(Box::new(SourceFileSpanContents { span: SourceSpan::new(start_pos.into(), end_pos - start_pos), start_line_number: line - start_new_lines, line_count: start_new_lines + 1 + end_new_lines, path: self.path.clone(), data: compact_data, })) } } #[derive(Debug)] struct SourceFileSpanContents { data: &'static [u8], span: SourceSpan, start_line_number: usize, line_count: usize, path: PathBuf, } impl<'a> SpanContents<'a> for SourceFileSpanContents { fn data(&'_ self) -> &'a [u8] { self.data } fn span(&self) -> &SourceSpan { &self.span } fn line(&self) -> usize { self.start_line_number } fn column(&self) -> usize { 0 } fn line_count(&self) -> usize { self.line_count } fn name(&self) -> Option<&str> { self.path.to_str() } fn language(&self) -> Option<&str> { None } }