-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy patherror.rs
More file actions
126 lines (109 loc) · 4.08 KB
/
error.rs
File metadata and controls
126 lines (109 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Command execution error types
//!
//! This module provides error types for command execution failures,
//! including startup errors and execution errors with detailed context.
use std::path::PathBuf;
use thiserror::Error;
/// Errors that can occur during command execution
#[derive(Error, Debug)]
pub enum CommandError {
/// The command could not be started (e.g., command not found, permission denied)
#[error("Failed to start command '{command}': {source}")]
StartupFailed {
command: String,
#[source]
source: std::io::Error,
},
/// The working directory does not exist
#[error("Working directory does not exist: '{working_dir}'")]
WorkingDirectoryNotFound { working_dir: PathBuf },
/// The command was started but exited with a non-zero status code
#[error(
"Command '{command}' failed with exit code {exit_code}\nStdout: {stdout}\nStderr: {stderr}"
)]
ExecutionFailed {
command: String,
exit_code: String,
stdout: String,
stderr: String,
},
}
impl crate::shared::Traceable for CommandError {
fn trace_format(&self) -> String {
match self {
Self::StartupFailed { command, source } => {
format!("CommandError: Failed to start '{command}' - {source}")
}
Self::WorkingDirectoryNotFound { working_dir } => {
format!(
"CommandError: Working directory does not exist - '{}'",
working_dir.display()
)
}
Self::ExecutionFailed {
command,
exit_code,
stdout,
stderr,
} => {
format!("CommandError: Command '{command}' failed with exit code {exit_code}\nStdout: {stdout}\nStderr: {stderr}")
}
}
}
fn trace_source(&self) -> Option<&dyn crate::shared::Traceable> {
// std::io::Error doesn't implement Traceable, so we return None
None
}
fn error_kind(&self) -> crate::shared::ErrorKind {
crate::shared::ErrorKind::CommandExecution
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use std::io;
#[test]
fn it_should_format_startup_failed_error_correctly() {
let io_error = io::Error::new(io::ErrorKind::NotFound, "command not found");
let error = CommandError::StartupFailed {
command: "nonexistent_command".to_string(),
source: io_error,
};
let error_message = error.to_string();
assert!(error_message.contains("Failed to start command 'nonexistent_command'"));
assert!(error_message.contains("command not found"));
}
#[test]
fn it_should_format_execution_failed_error_correctly() {
let error = CommandError::ExecutionFailed {
command: "false".to_string(),
exit_code: "1".to_string(),
stdout: String::new(),
stderr: "command failed".to_string(),
};
let error_message = error.to_string();
assert!(error_message.contains("Command 'false' failed with exit code 1"));
assert!(error_message.contains("Stderr: command failed"));
}
#[test]
fn it_should_preserve_source_error_chain() {
let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "permission denied");
let error = CommandError::StartupFailed {
command: "restricted_command".to_string(),
source: io_error,
};
// Test that the source error is preserved
assert!(error.source().is_some());
assert_eq!(error.source().unwrap().to_string(), "permission denied");
}
#[test]
fn it_should_format_working_directory_not_found_error_correctly() {
let error = CommandError::WorkingDirectoryNotFound {
working_dir: PathBuf::from("/nonexistent/path/to/dir"),
};
let error_message = error.to_string();
assert!(error_message.contains("Working directory does not exist"));
assert!(error_message.contains("/nonexistent/path/to/dir"));
}
}