use thiserror::Error;
#[derive(Debug, Error)]
pub enum GriteError {
#[error("invalid arguments: {0}")]
InvalidArgs(String),
#[error("not found: {0}")]
NotFound(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("database busy: {0}")]
DbBusy(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("sled error: {0}")]
Sled(#[from] sled::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("TOML parse error: {0}")]
TomlParse(#[from] toml::de::Error),
#[error("TOML serialize error: {0}")]
TomlSerialize(#[from] toml::ser::Error),
#[error("ID parse error: {0}")]
IdParse(#[from] crate::types::ids::IdParseError),
#[error("internal error: {0}")]
Internal(String),
#[error("IPC error: {0}")]
Ipc(String),
}
impl GriteError {
pub fn error_code(&self) -> &'static str {
match self {
GriteError::InvalidArgs(_) => "invalid_args",
GriteError::NotFound(_) => "not_found",
GriteError::Conflict(_) => "conflict",
GriteError::DbBusy(_) => "db_busy",
GriteError::Io(_) => "io_error",
GriteError::Sled(_) => "db_error",
GriteError::Json(_) => "internal_error",
GriteError::TomlParse(_) => "invalid_args",
GriteError::TomlSerialize(_) => "internal_error",
GriteError::IdParse(_) => "invalid_args",
GriteError::Internal(_) => "internal_error",
GriteError::Ipc(_) => "ipc_error",
}
}
pub fn exit_code(&self) -> i32 {
match self {
GriteError::InvalidArgs(_) => 2,
GriteError::NotFound(_) => 3,
GriteError::Conflict(_) => 4,
GriteError::DbBusy(_) => 5,
GriteError::Io(_) => 5,
GriteError::Sled(_) => 5,
GriteError::IdParse(_) => 2,
GriteError::Ipc(_) => 6,
_ => 1,
}
}
pub fn suggestions(&self) -> Vec<&'static str> {
match self {
GriteError::NotFound(msg) => {
if msg.contains("issue") || msg.starts_with("Issue") {
vec!["Run 'grite issue list' to see available issues"]
} else if msg.contains("actor") {
vec!["Run 'grite actor init' to create an actor"]
} else {
vec![]
}
}
GriteError::DbBusy(_) => vec![
"Try 'grite --no-daemon <command>' to bypass the daemon",
"Or wait for the other process to finish",
"Or run 'grite daemon stop' to stop the daemon",
],
GriteError::Sled(_) => vec![
"Run 'grite doctor --fix' to rebuild the database",
"If problem persists, check disk space and permissions",
],
GriteError::Ipc(_) => vec![
"Run 'grite daemon stop' and retry",
"Or use 'grite --no-daemon <command>' to bypass IPC",
],
GriteError::Conflict(_) => vec!["Run 'grite sync' to pull latest changes"],
GriteError::IdParse(_) => vec![
"IDs should be hex strings (e.g., 'abc123...')",
"Use 'grite issue list' to see valid issue IDs",
],
_ => vec![],
}
}
pub fn issue_not_found(issue_id: &str) -> Self {
GriteError::NotFound(format!(
"Issue '{}' not found",
if issue_id.len() > 16 {
&issue_id[..16]
} else {
issue_id
}
))
}
pub fn database_locked(details: Option<&str>) -> Self {
let msg = match details {
Some(d) => format!("Database is locked ({})", d),
None => "Database is locked by another process".to_string(),
};
GriteError::DbBusy(msg)
}
}