mirror of
https://github.com/RGBCube/dix
synced 2025-07-28 12:17:45 +00:00
error: improve error context in AppError
With nothing other than proper `From` implementations
This commit is contained in:
parent
f52281d691
commit
955a710812
1 changed files with 162 additions and 27 deletions
189
src/main.rs
189
src/main.rs
|
@ -16,20 +16,125 @@ use yansi::Paint;
|
|||
/// Application errors with thiserror
|
||||
#[derive(Debug, Error)]
|
||||
enum AppError {
|
||||
#[error("Command failed: {0}")]
|
||||
CommandFailed(String),
|
||||
#[error("Command failed: {command} {args:?} - {message}")]
|
||||
CommandFailed {
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
message: String,
|
||||
},
|
||||
|
||||
#[error("Failed to decode command output: {0}")]
|
||||
CommandOutputError(#[from] std::str::Utf8Error),
|
||||
#[error("Failed to decode command output from {context}: {source}")]
|
||||
CommandOutputError {
|
||||
source: std::str::Utf8Error,
|
||||
context: String,
|
||||
},
|
||||
|
||||
#[error("Failed to parse data: {0}")]
|
||||
ParseError(String),
|
||||
#[error("Failed to parse data in {context}: {message}")]
|
||||
ParseError {
|
||||
message: String,
|
||||
context: String,
|
||||
#[source]
|
||||
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||
},
|
||||
|
||||
#[error("Regex error: {0}")]
|
||||
RegexError(#[from] regex::Error),
|
||||
#[error("Regex error in {context}: {source}")]
|
||||
RegexError {
|
||||
source: regex::Error,
|
||||
context: String,
|
||||
},
|
||||
|
||||
#[error("IO error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("IO error in {context}: {source}")]
|
||||
IoError {
|
||||
source: std::io::Error,
|
||||
context: String,
|
||||
},
|
||||
|
||||
#[error("Nix store error: {message} for path: {store_path}")]
|
||||
NixStoreError { message: String, store_path: String },
|
||||
}
|
||||
|
||||
// Implement From traits to support the ? operator
|
||||
impl From<std::io::Error> for AppError {
|
||||
fn from(source: std::io::Error) -> Self {
|
||||
Self::IoError {
|
||||
source,
|
||||
context: "unknown context".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for AppError {
|
||||
fn from(source: std::str::Utf8Error) -> Self {
|
||||
Self::CommandOutputError {
|
||||
source,
|
||||
context: "command output".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<regex::Error> for AppError {
|
||||
fn from(source: regex::Error) -> Self {
|
||||
Self::RegexError {
|
||||
source,
|
||||
context: "regex operation".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
/// Create a command failure error with context
|
||||
pub fn command_failed<S: Into<String>>(command: S, args: &[&str], message: S) -> Self {
|
||||
Self::CommandFailed {
|
||||
command: command.into(),
|
||||
args: args.iter().map(|&s| s.to_string()).collect(),
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a parse error with context
|
||||
pub fn parse_error<S: Into<String>, C: Into<String>>(
|
||||
message: S,
|
||||
context: C,
|
||||
source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||
) -> Self {
|
||||
Self::ParseError {
|
||||
message: message.into(),
|
||||
context: context.into(),
|
||||
source,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an IO error with context
|
||||
pub fn io_error<C: Into<String>>(source: std::io::Error, context: C) -> Self {
|
||||
Self::IoError {
|
||||
source,
|
||||
context: context.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a regex error with context
|
||||
pub fn regex_error<C: Into<String>>(source: regex::Error, context: C) -> Self {
|
||||
Self::RegexError {
|
||||
source,
|
||||
context: context.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a command output error with context
|
||||
pub fn command_output_error<C: Into<String>>(source: std::str::Utf8Error, context: C) -> Self {
|
||||
Self::CommandOutputError {
|
||||
source,
|
||||
context: context.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Nix store error
|
||||
pub fn nix_store_error<M: Into<String>, P: Into<String>>(message: M, store_path: P) -> Self {
|
||||
Self::NixStoreError {
|
||||
message: message.into(),
|
||||
store_path: store_path.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use type alias for Result with our custom error type
|
||||
|
@ -264,9 +369,15 @@ fn get_packages(path: &std::path::Path) -> Result<Vec<String>> {
|
|||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix-store command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix-store command failed: {stderr}"
|
||||
)));
|
||||
return Err(AppError::CommandFailed {
|
||||
command: "nix-store".to_string(),
|
||||
args: vec![
|
||||
"--query".to_string(),
|
||||
"--references".to_string(),
|
||||
path.join("sw").to_string_lossy().to_string(),
|
||||
],
|
||||
message: stderr.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let list = str::from_utf8(&output.stdout)?;
|
||||
|
@ -289,9 +400,15 @@ fn get_dependencies(path: &std::path::Path) -> Result<Vec<String>> {
|
|||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix-store command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix-store command failed: {stderr}"
|
||||
)));
|
||||
return Err(AppError::CommandFailed {
|
||||
command: "nix-store".to_string(),
|
||||
args: vec![
|
||||
"--query".to_string(),
|
||||
"--requisites".to_string(),
|
||||
path.join("sw").to_string_lossy().to_string(),
|
||||
],
|
||||
message: stderr.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let list = str::from_utf8(&output.stdout)?;
|
||||
|
@ -321,17 +438,21 @@ fn get_version<'a>(pack: impl Into<&'a str>) -> Result<(&'a str, &'a str)> {
|
|||
let version = cap.get(2).map_or("", |m| m.as_str());
|
||||
|
||||
if name.is_empty() || version.is_empty() {
|
||||
return Err(AppError::ParseError(format!(
|
||||
"Failed to extract name or version from path: {path}"
|
||||
)));
|
||||
return Err(AppError::ParseError {
|
||||
message: format!("Failed to extract name or version from path: {path}"),
|
||||
context: "get_version".to_string(),
|
||||
source: None,
|
||||
});
|
||||
}
|
||||
|
||||
return Ok((name, version));
|
||||
}
|
||||
|
||||
Err(AppError::ParseError(format!(
|
||||
"Path does not match expected nix store format: {path}"
|
||||
)))
|
||||
Err(AppError::ParseError {
|
||||
message: format!("Path does not match expected nix store format: {path}"),
|
||||
context: "get_version".to_string(),
|
||||
source: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_nix_available() -> bool {
|
||||
|
@ -361,9 +482,15 @@ fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
|||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix path-info command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix path-info command failed: {stderr}"
|
||||
)));
|
||||
return Err(AppError::CommandFailed {
|
||||
command: "nix".to_string(),
|
||||
args: vec![
|
||||
"path-info".to_string(),
|
||||
"--closure-size".to_string(),
|
||||
path.join("sw").to_string_lossy().to_string(),
|
||||
],
|
||||
message: stderr.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let stdout = str::from_utf8(&output.stdout)?;
|
||||
|
@ -375,13 +502,21 @@ fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
|||
.ok_or_else(|| {
|
||||
let err = "Unexpected output format from nix path-info";
|
||||
error!("{}", err);
|
||||
AppError::ParseError(err.into())
|
||||
AppError::ParseError {
|
||||
message: err.into(),
|
||||
context: "get_closure_size".to_string(),
|
||||
source: None,
|
||||
}
|
||||
})?
|
||||
.parse::<i64>()
|
||||
.map_err(|e| {
|
||||
let err = format!("Failed to parse size value: {e}");
|
||||
error!("{}", err);
|
||||
AppError::ParseError(err)
|
||||
AppError::ParseError {
|
||||
message: err,
|
||||
context: "get_closure_size".to_string(),
|
||||
source: None,
|
||||
}
|
||||
})?;
|
||||
|
||||
// Convert to MiB
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue