use cabin_core::ColorChoice;
use termcolor::StandardStream;
pub(crate) enum DiagnosticCandidate<'a> {
Rich(&'a dyn cabin_diagnostics::miette::Diagnostic),
Coded { code: &'static str },
}
impl DiagnosticCandidate<'_> {
pub(crate) fn render(
&self,
root: &anyhow::Error,
stderr: &mut StandardStream,
color: ColorChoice,
) -> std::io::Result<()> {
match self {
Self::Rich(diag) => cabin_diagnostics::render(*diag, stderr, color),
Self::Coded { code } => {
let message = format!("{root:#}");
let diagnostic = cabin_diagnostics::CodedMessage::new(&message, code);
cabin_diagnostics::render(&diagnostic, stderr, color)
}
}
}
}
pub(crate) fn downcast_diagnostic<'a>(
err: &'a (dyn std::error::Error + 'static),
) -> Option<DiagnosticCandidate<'a>> {
use cabin_diagnostics::code;
if let Some(e) = err.downcast_ref::<cabin_manifest::ManifestError>() {
return Some(DiagnosticCandidate::Rich(e));
}
if let Some(e) = err.downcast_ref::<Box<cabin_manifest::ManifestError>>() {
return Some(DiagnosticCandidate::Rich(e.as_ref()));
}
if let Some(e) = err.downcast_ref::<cabin_workspace::WorkspaceError>() {
return Some(DiagnosticCandidate::Rich(e));
}
if let Some(e) = err.downcast_ref::<cabin_system_deps::PkgConfigError>() {
return Some(DiagnosticCandidate::Rich(e));
}
if let Some(e) = err.downcast_ref::<cabin_config::ConfigError>() {
return Some(DiagnosticCandidate::Coded {
code: config_error_code(e),
});
}
if err
.downcast_ref::<cabin_lockfile::LockfileError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::LOCKFILE_ERROR,
});
}
if let Some(e) = err.downcast_ref::<cabin_resolver::ResolveError>() {
return Some(DiagnosticCandidate::Rich(e));
}
if err
.downcast_ref::<cabin_artifact::ArtifactError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::ARTIFACT_ERROR,
});
}
if err.downcast_ref::<cabin_build::BuildError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::BUILD_ERROR,
});
}
if err
.downcast_ref::<cabin_core::StandardFlagConflict>()
.is_some()
|| err
.downcast_ref::<Box<cabin_core::StandardFlagConflict>>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::LANGUAGE_STANDARD_FLAG_CONFLICT,
});
}
if err.downcast_ref::<cabin_package::PackageError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::PACKAGE_ERROR,
});
}
if err
.downcast_ref::<cabin_toolchain::ToolchainError>()
.is_some()
|| err
.downcast_ref::<cabin_toolchain::ToolchainDetectionFailure>()
.is_some()
|| err.downcast_ref::<cabin_toolchain::RunError>().is_some()
|| err
.downcast_ref::<cabin_toolchain::CompilerWrapperResolutionError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::TOOLCHAIN_ERROR,
});
}
if err.downcast_ref::<cabin_vendor::VendorError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::VENDOR_ERROR,
});
}
if err.downcast_ref::<cabin_index::IndexError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::INDEX_ERROR,
});
}
if err
.downcast_ref::<cabin_index_http::IndexHttpError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::INDEX_HTTP_ERROR,
});
}
if err.downcast_ref::<cabin_publish::PublishError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::PUBLISH_ERROR,
});
}
if err.downcast_ref::<cabin_fmt::FormatError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::FMT_ERROR,
});
}
if err.downcast_ref::<cabin_tidy::TidyError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::TIDY_ERROR,
});
}
if err
.downcast_ref::<cabin_source_discovery::SourceDiscoveryError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::SOURCE_DISCOVERY_ERROR,
});
}
if err.downcast_ref::<cabin_test::TestRunError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::TEST_ERROR,
});
}
if err.downcast_ref::<cabin_explain::ExplainError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::EXPLAIN_ERROR,
});
}
if err.downcast_ref::<cabin_ninja::NinjaError>().is_some() {
return Some(DiagnosticCandidate::Coded {
code: code::NINJA_ERROR,
});
}
if err
.downcast_ref::<cabin_feature::FeatureResolverError>()
.is_some()
{
return Some(DiagnosticCandidate::Coded {
code: code::FEATURE_ERROR,
});
}
None
}
fn config_error_code(error: &cabin_config::ConfigError) -> &'static str {
use cabin_config::{ConfigError, ConfigParseError};
use cabin_diagnostics::code;
match error {
ConfigError::Parse {
source: ConfigParseError::InvalidBuildJobs { .. },
..
} => code::CONFIG_INVALID_BUILD_JOBS,
_ => code::CONFIG_LOAD_FAILED,
}
}