use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum WinxError {
#[error("Failed to initialize shell: {0}")]
ShellInitializationError(String),
#[error("Workspace path error: {0}")]
WorkspacePathError(String),
#[error("Failed to lock the bash state: {0}")]
BashStateLockError(String),
#[error("Bash state not initialized. Please call Initialize first with type=\"first_call\" and a valid workspace path.")]
BashStateNotInitialized,
#[error("Command execution failed: {0}")]
CommandExecutionError(String),
#[error("Failed to parse arguments: {0}")]
ArgumentParseError(String),
#[error("File access error for {path}: {message}")]
FileAccessError { path: PathBuf, message: String },
#[error("Security violation: {message}")]
PathSecurityError { path: PathBuf, message: String },
#[error("Command not allowed: {0}")]
CommandNotAllowed(String),
#[error("Thread ID mismatch: {0}")]
ThreadIdMismatch(String),
#[error("Deserialization error: {0}")]
DeserializationError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Search/replace syntax error: {0}")]
SearchReplaceSyntaxError(String),
#[error("Search block not found in content: {0}")]
SearchBlockNotFound(String),
#[error("Search block matched multiple times")]
SearchBlockAmbiguous { block_content: String, match_count: usize, suggestions: Vec<String> },
#[error("Multiple search blocks have conflicting matches")]
SearchBlockConflict { conflicting_blocks: Vec<String>, first_differing_block: Option<String> },
#[error("Search/replace syntax error: {message}")]
SearchReplaceSyntaxErrorDetailed {
message: String,
line_number: Option<usize>,
block_type: Option<String>,
suggestions: Vec<String>,
},
#[error("Invalid JSON: {0}")]
JsonParseError(String),
#[error("File {path} is too large: {size} bytes (max {max_size})")]
FileTooLarge { path: PathBuf, size: u64, max_size: u64 },
#[error("Failed to write file {path}: {message}")]
FileWriteError { path: PathBuf, message: String },
#[error("Failed to load data: {0}")]
DataLoadingError(String),
#[error("Invalid parameter: {field} - {message}")]
ParameterValidationError { field: String, message: String },
#[error("Required parameter missing: {field} - {message}")]
MissingParameterError { field: String, message: String },
#[error("Null or undefined value where object expected: {field}")]
NullValueError { field: String },
#[error("{message} - {suggestion}")]
RecoverableSuggestionError { message: String, suggestion: String },
#[error("Context save error: {0}")]
ContextSaveError(String),
#[error("Command timed out after {timeout_seconds}s: {command}")]
CommandTimeout { command: String, timeout_seconds: u64 },
#[error(
"Interactive command detected: {command}. Use appropriate flags or consider alternatives."
)]
InteractiveCommandDetected { command: String },
#[error("A command is already running: '{current_command}' (for {duration_seconds:.1}s). Use status_check, send_text, or interrupt.")]
CommandAlreadyRunning { current_command: String, duration_seconds: f64 },
#[error("Failed to cleanup process: {message}")]
ProcessCleanupError { message: String },
#[error("Command output exceeded maximum size: {size} bytes (max {max_size})")]
BufferOverflow { size: usize, max_size: usize },
#[error("Failed to recover bash session: {message}")]
SessionRecoveryError { message: String },
#[error("Resource allocation failed: {message}")]
ResourceAllocationError { message: String },
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Configuration error: {0}")]
ConfigurationError(String),
#[error("Parse error: {0}")]
ParseError(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("File error: {0}")]
FileError(String),
}
pub type Result<T> = std::result::Result<T, WinxError>;
impl From<anyhow::Error> for WinxError {
fn from(error: anyhow::Error) -> Self {
WinxError::CommandExecutionError(format!("{error}"))
}
}
pub struct ErrorRecovery;
impl ErrorRecovery {
pub fn suggest(error: WinxError, _suggestion: &str) -> WinxError {
error
}
pub fn param_error(field: &str, message: &str) -> WinxError {
WinxError::ParameterValidationError {
field: field.to_string(),
message: message.to_string(),
}
}
pub fn missing_param(field: &str, message: &str) -> WinxError {
WinxError::MissingParameterError { field: field.to_string(), message: message.to_string() }
}
pub fn null_value(field: &str) -> WinxError {
WinxError::NullValueError { field: field.to_string() }
}
}
impl Clone for WinxError {
fn clone(&self) -> Self {
match self {
Self::ShellInitializationError(msg) => Self::ShellInitializationError(msg.clone()),
Self::WorkspacePathError(msg) => Self::WorkspacePathError(msg.clone()),
Self::BashStateLockError(msg) => Self::BashStateLockError(msg.clone()),
Self::BashStateNotInitialized => Self::BashStateNotInitialized,
Self::CommandExecutionError(msg) => Self::CommandExecutionError(msg.clone()),
Self::CommandNotAllowed(msg) => Self::CommandNotAllowed(msg.clone()),
Self::ThreadIdMismatch(msg) => Self::ThreadIdMismatch(msg.clone()),
Self::ArgumentParseError(msg) => Self::ArgumentParseError(msg.clone()),
Self::FileAccessError { path, message } => {
Self::FileAccessError { path: path.clone(), message: message.clone() }
}
Self::DeserializationError(msg) => Self::DeserializationError(msg.clone()),
Self::SerializationError(msg) => Self::SerializationError(msg.clone()),
Self::SearchReplaceSyntaxError(msg) => Self::SearchReplaceSyntaxError(msg.clone()),
Self::SearchBlockNotFound(msg) => Self::SearchBlockNotFound(msg.clone()),
Self::SearchBlockAmbiguous { block_content, match_count, suggestions } => {
Self::SearchBlockAmbiguous {
block_content: block_content.clone(),
match_count: *match_count,
suggestions: suggestions.clone(),
}
}
Self::SearchBlockConflict { conflicting_blocks, first_differing_block } => {
Self::SearchBlockConflict {
conflicting_blocks: conflicting_blocks.clone(),
first_differing_block: first_differing_block.clone(),
}
}
Self::SearchReplaceSyntaxErrorDetailed {
message,
line_number,
block_type,
suggestions,
} => Self::SearchReplaceSyntaxErrorDetailed {
message: message.clone(),
line_number: *line_number,
block_type: block_type.clone(),
suggestions: suggestions.clone(),
},
Self::JsonParseError(msg) => Self::JsonParseError(msg.clone()),
Self::FileTooLarge { path, size, max_size } => {
Self::FileTooLarge { path: path.clone(), size: *size, max_size: *max_size }
}
Self::FileWriteError { path, message } => {
Self::FileWriteError { path: path.clone(), message: message.clone() }
}
Self::DataLoadingError(msg) => Self::DataLoadingError(msg.clone()),
Self::ParameterValidationError { field, message } => {
Self::ParameterValidationError { field: field.clone(), message: message.clone() }
}
Self::MissingParameterError { field, message } => {
Self::MissingParameterError { field: field.clone(), message: message.clone() }
}
Self::NullValueError { field } => Self::NullValueError { field: field.clone() },
Self::RecoverableSuggestionError { message, suggestion } => {
Self::RecoverableSuggestionError {
message: message.clone(),
suggestion: suggestion.clone(),
}
}
Self::ContextSaveError(msg) => Self::ContextSaveError(msg.clone()),
Self::CommandTimeout { command, timeout_seconds } => {
Self::CommandTimeout { command: command.clone(), timeout_seconds: *timeout_seconds }
}
Self::InteractiveCommandDetected { command } => {
Self::InteractiveCommandDetected { command: command.clone() }
}
Self::CommandAlreadyRunning { current_command, duration_seconds } => {
Self::CommandAlreadyRunning {
current_command: current_command.clone(),
duration_seconds: *duration_seconds,
}
}
Self::ProcessCleanupError { message } => {
Self::ProcessCleanupError { message: message.clone() }
}
Self::BufferOverflow { size, max_size } => {
Self::BufferOverflow { size: *size, max_size: *max_size }
}
Self::SessionRecoveryError { message } => {
Self::SessionRecoveryError { message: message.clone() }
}
Self::ResourceAllocationError { message } => {
Self::ResourceAllocationError { message: message.clone() }
}
Self::IoError(err) => Self::IoError(std::io::Error::new(err.kind(), err.to_string())),
Self::ConfigurationError(msg) => Self::ConfigurationError(msg.clone()),
Self::ParseError(msg) => Self::ParseError(msg.clone()),
Self::InvalidInput(msg) => Self::InvalidInput(msg.clone()),
Self::FileError(msg) => Self::FileError(msg.clone()),
Self::PathSecurityError { path, message } => {
Self::PathSecurityError { path: path.clone(), message: message.clone() }
}
}
}
}