use thiserror::Error; use git2::{ErrorClass, ErrorCode}; /// Detailed git error with actionable hint #[derive(Debug)] pub struct GitDetailedError { pub code: ErrorCode, pub class: ErrorClass, pub message: String, pub hint: String, } impl std::fmt::Display for GitDetailedError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.message) } } #[derive(Error, Debug)] pub enum EmpveError { #[error("Config error: {0}")] Config(String), #[error("Git error: {0}")] Git(#[from] git2::Error), #[error("IO error: {0}")] Io(#[from] std::io::Error), #[error("TOML parse error: {0}")] TomlParse(#[from] toml::de::Error), #[error("TOML serialize error: {0}")] TomlSerialize(#[from] toml::ser::Error), #[error("Repository not found: {0}")] RepoNotFound(String), #[error("Repository already exists: {0}")] RepoExists(String), #[error("Script not found: {0}")] ScriptNotFound(String), #[error("Invalid repository identifier: {0}")] InvalidRepo(String), } impl EmpveError { /// Get a user-friendly hint for resolving this error pub fn hint(&self) -> Option { match self { EmpveError::Git(e) => Some(git_error_hint(e)), EmpveError::RepoNotFound(_) => Some("Check the repository name and try again. Use 'empeve browse' to see available scripts.".to_string()), EmpveError::RepoExists(_) => Some("Use 'empeve remove' first if you want to replace it.".to_string()), EmpveError::ScriptNotFound(_) => Some("Check script name or run 'empeve status' to see available scripts.".to_string()), EmpveError::InvalidRepo(_) => Some("Use format 'user/repo' for GitHub or a full git URL.".to_string()), _ => None, } } } /// Generate a user-friendly hint from a git2 error pub fn git_error_hint(error: &git2::Error) -> String { match (error.code(), error.class()) { (ErrorCode::NotFound, ErrorClass::Reference) => { "The specified branch or ref was not found. Check if it exists on the remote.".to_string() } (ErrorCode::NotFound, ErrorClass::Repository) => { "Repository not found. Verify the URL is correct and you have access.".to_string() } (ErrorCode::Auth, _) => { "Authentication failed. The repository may be private or credentials are invalid.".to_string() } (ErrorCode::Certificate, _) | (_, ErrorClass::Ssl) => { "SSL/TLS certificate error. Check your system certificates or network.".to_string() } (ErrorCode::Locked, _) => { "Repository is locked. Another process may be using it. Try again later.".to_string() } (ErrorCode::Exists, _) => { "The destination already exists. Use --force to overwrite.".to_string() } (ErrorCode::BareRepo, _) => { "Cannot perform this operation on a bare repository.".to_string() } (ErrorCode::UnbornBranch, _) => { "The repository has no commits yet.".to_string() } (ErrorCode::Uncommitted, _) => { "There are uncommitted changes. Commit or stash them first.".to_string() } (_, ErrorClass::Net) | (ErrorCode::GenericError, ErrorClass::Os) => { "Network error. Check your internet connection.".to_string() } (_, ErrorClass::Checkout) => { "Checkout failed. There may be conflicting local changes.".to_string() } (_, ErrorClass::FetchHead) => { "Failed to update FETCH_HEAD. Try running 'empeve clean' and reinstalling.".to_string() } _ => { format!("Git operation failed ({:?}/{:?})", error.class(), error.code()) } } } pub type Result = std::result::Result;