use crate::engine::{EngineState, StateWorkingSet}; use miette::{ LabeledSpan, MietteHandlerOpts, NarratableReportHandler, ReportHandler, RgbColors, Severity, SourceCode, }; use thiserror::Error; /// This error exists so that we can defer SourceCode handling. It simply /// forwards most methods, except for `.source_code()`, which we provide. #[derive(Error)] #[error("{0}")] pub struct CliError<'src>( pub &'src (dyn miette::Diagnostic + Send + Sync + 'static), pub &'src StateWorkingSet<'src>, ); pub fn format_error( working_set: &StateWorkingSet, error: &(dyn miette::Diagnostic + Send + Sync + 'static), ) -> String { format!("Error: {:?}", CliError(error, working_set)) } pub fn report_error( working_set: &StateWorkingSet, error: &(dyn miette::Diagnostic + Send + Sync + 'static), ) { eprintln!("Error: {:?}", CliError(error, working_set)); // reset vt processing, aka ansi because illbehaved externals can break it #[cfg(windows)] { let _ = nu_utils::enable_vt_processing(); } } pub fn report_error_new( engine_state: &EngineState, error: &(dyn miette::Diagnostic + Send + Sync + 'static), ) { let working_set = StateWorkingSet::new(engine_state); report_error(&working_set, error); } impl std::fmt::Debug for CliError<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let config = self.1.get_config(); let ansi_support = &config.use_ansi_coloring; let ansi_support = *ansi_support; let error_style = &config.error_style.as_str(); let errors_style = *error_style; let miette_handler: Box = match errors_style { "plain" => Box::new(NarratableReportHandler::new()), _ => Box::new( MietteHandlerOpts::new() // For better support of terminal themes use the ANSI coloring .rgb_colors(RgbColors::Never) // If ansi support is disabled in the config disable the eye-candy .color(ansi_support) .unicode(ansi_support) .terminal_links(ansi_support) .build(), ), }; // Ignore error to prevent format! panics. This can happen if span points at some // inaccessible location, for example by calling `report_error()` with wrong working set. let _ = miette_handler.debug(self, f); Ok(()) } } impl<'src> miette::Diagnostic for CliError<'src> { fn code<'a>(&'a self) -> Option> { self.0.code() } fn severity(&self) -> Option { self.0.severity() } fn help<'a>(&'a self) -> Option> { self.0.help() } fn url<'a>(&'a self) -> Option> { self.0.url() } fn labels<'a>(&'a self) -> Option + 'a>> { self.0.labels() } // Finally, we redirect the source_code method to our own source. fn source_code(&self) -> Option<&dyn SourceCode> { if let Some(source_code) = self.0.source_code() { Some(source_code) } else { Some(&self.1) } } fn related<'a>(&'a self) -> Option + 'a>> { self.0.related() } }