use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum ExitCode {
Success = 0,
ParseError = 1,
LintErrors = 2,
IoError = 3,
InvalidArgs = 4,
}
#[derive(Debug, Error)]
pub enum DiscoveryError {
#[error("invalid glob pattern '{pattern}': {source}")]
InvalidPattern {
pattern: String,
#[source]
source: globset::Error,
},
#[error("failed to read '{path}': {source}")]
IoError {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("permission denied: '{path}'")]
PermissionDenied {
path: PathBuf,
},
#[error("broken symbolic link: '{path}'")]
BrokenSymlink {
path: PathBuf,
},
#[error("path does not exist: '{path}'")]
PathNotFound {
path: PathBuf,
},
#[error("failed to read file list from stdin: {source}")]
StdinError {
#[source]
source: std::io::Error,
},
#[error("exceeded maximum of {max} paths")]
TooManyPaths {
max: usize,
},
}
impl ExitCode {
pub const fn as_i32(self) -> i32 {
self as i32
}
}
pub fn format_error(err: &anyhow::Error, use_color: bool) -> String {
use std::fmt::Write;
let mut output = String::new();
#[cfg(feature = "colors")]
if use_color {
use colored::Colorize;
let _ = writeln!(output, "{} {}", "error:".red().bold(), err);
for (i, cause) in err.chain().skip(1).enumerate() {
let _ = writeln!(
output,
" {}{} {}",
"caused by".dimmed(),
format!("[{i}]").dimmed(),
cause.to_string().dimmed()
);
}
return output;
}
let _ = writeln!(output, "error: {err}");
for (i, cause) in err.chain().skip(1).enumerate() {
let _ = writeln!(output, " caused by[{i}] {cause}");
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exit_code_values() {
assert_eq!(ExitCode::Success.as_i32(), 0);
assert_eq!(ExitCode::ParseError.as_i32(), 1);
assert_eq!(ExitCode::LintErrors.as_i32(), 2);
assert_eq!(ExitCode::IoError.as_i32(), 3);
assert_eq!(ExitCode::InvalidArgs.as_i32(), 4);
}
#[test]
fn test_format_error_no_color() {
let err = anyhow::anyhow!("test error");
let formatted = format_error(&err, false);
assert!(formatted.contains("error: test error"));
}
#[test]
fn test_format_error_with_chain() {
use anyhow::Context;
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err: anyhow::Error = Err::<(), _>(io_err)
.context("Failed to read config")
.unwrap_err();
let formatted = format_error(&err, false);
assert!(formatted.contains("Failed to read config"));
assert!(formatted.contains("caused by"));
}
}