at main 3.7 kB view raw
1use miette::{Diagnostic, GraphicalReportHandler, Report, SourceCode, SourceSpan, SpanContents}; 2use nu_protocol::{ShellError, Span}; 3use vfs::{VfsError, error::VfsErrorKind}; 4 5pub struct CommandError { 6 pub error: Report, 7 pub start_offset: usize, 8 pub input: String, 9} 10 11impl CommandError { 12 pub fn new<E>(error: E, input: impl Into<String>) -> Self 13 where 14 E: Diagnostic + Clone + Send + Sync + 'static, 15 { 16 Self { 17 error: Report::new(error), 18 start_offset: 0, 19 input: input.into(), 20 } 21 } 22 23 pub fn with_start_offset(mut self, start_offset: usize) -> Self { 24 self.start_offset = start_offset; 25 self 26 } 27} 28 29impl From<ShellError> for CommandError { 30 fn from(value: ShellError) -> Self { 31 CommandError::new(value, String::new()) 32 } 33} 34 35impl From<CommandError> for String { 36 fn from(value: CommandError) -> Self { 37 let handler = GraphicalReportHandler::new() 38 .with_theme(miette::GraphicalTheme::unicode()) 39 .with_cause_chain(); 40 41 if value.input.is_empty() { 42 let mut msg = String::new(); 43 handler 44 .render_report(&mut msg, value.error.as_ref()) 45 .unwrap(); 46 return msg; 47 } 48 49 let source = OffsetSource { 50 source: value.input, 51 start_offset: value.start_offset, 52 }; 53 54 let report_with_source = value.error.with_source_code(source); 55 let mut msg = String::new(); 56 handler 57 .render_report(&mut msg, report_with_source.as_ref()) 58 .unwrap(); 59 msg 60 } 61} 62 63pub struct OffsetSource { 64 pub source: String, 65 pub start_offset: usize, 66} 67 68pub struct OffsetSpanContents<'a> { 69 inner: Box<dyn SpanContents<'a> + 'a>, 70 global_span: SourceSpan, 71} 72 73impl<'a> SpanContents<'a> for OffsetSpanContents<'a> { 74 fn data(&self) -> &'a [u8] { 75 self.inner.data() 76 } 77 fn span(&self) -> &SourceSpan { 78 &self.global_span 79 } 80 fn line(&self) -> usize { 81 self.inner.line() 82 } 83 fn column(&self) -> usize { 84 self.inner.column() 85 } 86 fn line_count(&self) -> usize { 87 self.inner.line_count() 88 } 89} 90 91impl SourceCode for OffsetSource { 92 fn read_span<'b>( 93 &'b self, 94 span: &SourceSpan, 95 context_lines_before: usize, 96 context_lines_after: usize, 97 ) -> Result<Box<dyn miette::SpanContents<'b> + 'b>, miette::MietteError> { 98 let local_start = span.offset().saturating_sub(self.start_offset); 99 let local_len = std::cmp::min(span.len(), self.source.len().saturating_sub(local_start)); 100 let local_span = SourceSpan::new(local_start.into(), local_len); 101 102 let local_contents = 103 self.source 104 .read_span(&local_span, context_lines_before, context_lines_after)?; 105 106 let content_local_span = local_contents.span(); 107 let global_start = content_local_span.offset() + self.start_offset; 108 let global_span = SourceSpan::new(global_start.into(), content_local_span.len()); 109 110 Ok(Box::new(OffsetSpanContents { 111 inner: local_contents, 112 global_span, 113 })) 114 } 115} 116 117pub fn to_shell_err(span: Span) -> impl Fn(VfsError) -> ShellError { 118 move |vfs_error: VfsError| ShellError::GenericError { 119 error: (match vfs_error.kind() { 120 VfsErrorKind::DirectoryExists 121 | VfsErrorKind::FileExists 122 | VfsErrorKind::FileNotFound 123 | VfsErrorKind::InvalidPath => "path error", 124 _ => "io error", 125 }) 126 .to_string(), 127 msg: vfs_error.to_string(), 128 span: Some(span), 129 help: None, 130 inner: vec![], 131 } 132}