Skip to main content

libgrite_ipc/
error.rs

1//! IPC error types
2
3use thiserror::Error;
4
5/// Errors that can occur during IPC operations
6#[derive(Error, Debug)]
7pub enum IpcError {
8    /// Connection failed
9    #[error("Connection failed: {0}")]
10    ConnectionFailed(String),
11
12    /// Request timed out
13    #[error("Request timed out after {0}ms")]
14    Timeout(u64),
15
16    /// Serialization error
17    #[error("Serialization error: {0}")]
18    Serialization(String),
19
20    /// Deserialization error
21    #[error("Deserialization error: {0}")]
22    Deserialization(String),
23
24    /// Protocol version mismatch
25    #[error("Protocol version mismatch: expected {expected}, got {actual}")]
26    VersionMismatch { expected: u32, actual: u32 },
27
28    /// Daemon not running
29    #[error("Daemon not running")]
30    DaemonNotRunning,
31
32    /// Daemon returned an error
33    #[error("Daemon error [{code}]: {message}")]
34    DaemonError { code: String, message: String },
35
36    /// Lock file error
37    #[error("Lock file error: {0}")]
38    LockFile(String),
39
40    /// Lock is held by another process
41    #[error("Lock held by process {pid} (expires in {expires_in_ms}ms)")]
42    LockHeld { pid: u32, expires_in_ms: u64 },
43
44    /// Lost lock acquisition race (another process acquired it first)
45    #[error("Lock acquisition race lost: another process acquired the lock simultaneously")]
46    LockRace,
47
48    /// Lock has expired
49    #[error("Lock expired")]
50    LockExpired,
51
52    /// Client is poisoned (stream has stale data from a previous failed exchange)
53    #[error("Client connection is poisoned after a previous error; reconnect to continue")]
54    ClientPoisoned,
55
56    /// IO error
57    #[error("IO error: {0}")]
58    Io(#[from] std::io::Error),
59
60    /// JSON error
61    #[error("JSON error: {0}")]
62    Json(#[from] serde_json::Error),
63}
64
65/// Bridge IpcError into GriteError, preserving semantic variants.
66impl From<IpcError> for libgrite_core::GriteError {
67    fn from(e: IpcError) -> Self {
68        match e {
69            IpcError::DaemonNotRunning => {
70                libgrite_core::GriteError::Ipc("Daemon not running".to_string())
71            }
72            IpcError::Timeout(_) => libgrite_core::GriteError::Ipc("IPC timeout".to_string()),
73            IpcError::LockHeld { pid, expires_in_ms } => {
74                libgrite_core::GriteError::DbBusy(format!(
75                    "Daemon lock held by process {} (expires in {}s)",
76                    pid,
77                    expires_in_ms / 1000
78                ))
79            }
80            IpcError::LockRace => libgrite_core::GriteError::Conflict(
81                "Another process acquired the daemon lock simultaneously".to_string(),
82            ),
83            IpcError::LockExpired => {
84                libgrite_core::GriteError::Ipc("Daemon lock expired".to_string())
85            }
86            other => libgrite_core::GriteError::Ipc(other.to_string()),
87        }
88    }
89}
90
91/// Error codes matching docs/cli-json.md
92pub mod codes {
93    pub const DB_BUSY: &str = "db_busy";
94    pub const NOT_FOUND: &str = "not_found";
95    pub const INVALID_INPUT: &str = "invalid_input";
96    pub const INTERNAL: &str = "internal";
97    pub const NOT_INITIALIZED: &str = "not_initialized";
98    pub const IO_ERROR: &str = "io_error";
99    pub const GIT_ERROR: &str = "git_error";
100    pub const IPC_ERROR: &str = "ipc_error";
101}