Skip to main content

git_sync_rs/
error.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum SyncError {
5    #[error("Not a git repository: {path}")]
6    NotARepository { path: String },
7
8    #[error("Repository in unsafe state: {state}")]
9    UnsafeRepositoryState { state: String },
10
11    #[error("Not on a branch (detached HEAD)")]
12    DetachedHead,
13
14    #[error("No remote configured for branch {branch}")]
15    NoRemoteConfigured { branch: String },
16
17    #[error("Remote branch not found: {remote}/{branch}")]
18    RemoteBranchNotFound { remote: String, branch: String },
19
20    #[error("Branch {branch} not configured for sync")]
21    BranchNotConfigured { branch: String },
22
23    #[error("Manual intervention required: {reason}")]
24    ManualInterventionRequired { reason: String },
25
26    #[error("Network error: {0}")]
27    NetworkError(String),
28
29    #[error("Authentication failed during {operation}")]
30    AuthenticationFailed { operation: String },
31
32    #[error("Git command failed: {command}\n{stderr}")]
33    GitCommandFailed { command: String, stderr: String },
34
35    #[error("Git hooks rejected commit: {details}")]
36    HookRejected { details: String },
37
38    #[error("Git error: {0}")]
39    GitError(#[from] git2::Error),
40
41    #[error("IO error: {0}")]
42    IoError(#[from] std::io::Error),
43
44    #[error("Watch error: {0}")]
45    WatchError(String),
46
47    #[error("Task error: {0}")]
48    TaskError(String),
49
50    #[error("Other error: {0}")]
51    Other(String),
52}
53
54impl SyncError {
55    /// Get the exit code for this error type, matching the original git-sync
56    pub fn exit_code(&self) -> i32 {
57        match self {
58            SyncError::NotARepository { .. } => 128, // Match git's error code
59            SyncError::UnsafeRepositoryState { .. } => 2,
60            SyncError::DetachedHead => 2,
61            SyncError::NoRemoteConfigured { .. } => 2,
62            SyncError::RemoteBranchNotFound { .. } => 2,
63            SyncError::BranchNotConfigured { .. } => 1,
64            SyncError::ManualInterventionRequired { .. } => 1,
65            SyncError::NetworkError(_) => 3,
66            SyncError::AuthenticationFailed { .. } => 3,
67            SyncError::GitCommandFailed { .. } => 2,
68            SyncError::HookRejected { .. } => 1,
69            SyncError::GitError(e) => {
70                // Map git2 errors to appropriate exit codes
71                match e.code() {
72                    git2::ErrorCode::NotFound => 128,
73                    git2::ErrorCode::Conflict => 1,
74                    git2::ErrorCode::Locked => 2,
75                    _ => 2,
76                }
77            }
78            SyncError::IoError(_) => 2,
79            SyncError::WatchError(_) => 2,
80            SyncError::TaskError(_) => 2,
81            SyncError::Other(_) => 2,
82        }
83    }
84}
85
86pub type Result<T> = std::result::Result<T, SyncError>;
87
88impl From<notify::Error> for SyncError {
89    fn from(err: notify::Error) -> Self {
90        SyncError::WatchError(err.to_string())
91    }
92}
93
94impl From<tokio::task::JoinError> for SyncError {
95    fn from(err: tokio::task::JoinError) -> Self {
96        SyncError::TaskError(err.to_string())
97    }
98}