Skip to main content

tldr_cli/commands/daemon/
error.rs

1//! Daemon-specific error types
2//!
3//! Errors for daemon lifecycle, IPC, and cache operations.
4
5use std::io;
6use std::path::PathBuf;
7
8use thiserror::Error;
9
10/// Daemon-specific errors
11#[derive(Debug, Error)]
12pub enum DaemonError {
13    /// Daemon is already running for this project
14    #[error("daemon already running (PID: {pid})")]
15    AlreadyRunning { pid: u32 },
16
17    /// Daemon is not running for this project
18    #[error("daemon not running")]
19    NotRunning,
20
21    /// Failed to acquire PID file lock
22    #[error("failed to acquire PID file lock: {0}")]
23    LockFailed(io::Error),
24
25    /// Failed to bind to socket
26    #[error("failed to bind socket: {0}")]
27    SocketBindFailed(io::Error),
28
29    /// Address/socket is already in use
30    #[error("address already in use: {addr}")]
31    AddressInUse { addr: String },
32
33    /// Connection to daemon was refused
34    #[error("connection refused")]
35    ConnectionRefused,
36
37    /// Connection to daemon timed out
38    #[error("connection timeout after {timeout_secs}s")]
39    ConnectionTimeout { timeout_secs: u64 },
40
41    /// Invalid IPC message received
42    #[error("invalid IPC message: {0}")]
43    InvalidMessage(String),
44
45    /// Unknown command received
46    #[error("unknown command: {cmd}")]
47    UnknownCommand { cmd: String },
48
49    /// Required parameter is missing
50    #[error("missing required parameter: {param}")]
51    MissingParameter { param: String },
52
53    /// Permission denied for path
54    #[error("permission denied: {}", path.display())]
55    PermissionDenied { path: PathBuf },
56
57    /// PID file exists but process is not running
58    #[error("stale PID file (process {pid} not running)")]
59    StalePidFile { pid: u32 },
60
61    /// Generic IO error
62    #[error("IO error: {0}")]
63    Io(#[from] io::Error),
64
65    /// JSON serialization/deserialization error
66    #[error("JSON error: {0}")]
67    Json(#[from] serde_json::Error),
68}
69
70/// Result type for daemon operations
71pub type DaemonResult<T> = Result<T, DaemonError>;
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_daemon_error_display() {
79        let err = DaemonError::AlreadyRunning { pid: 12345 };
80        assert_eq!(err.to_string(), "daemon already running (PID: 12345)");
81
82        let err = DaemonError::NotRunning;
83        assert_eq!(err.to_string(), "daemon not running");
84
85        let err = DaemonError::ConnectionRefused;
86        assert_eq!(err.to_string(), "connection refused");
87
88        let err = DaemonError::ConnectionTimeout { timeout_secs: 5 };
89        assert_eq!(err.to_string(), "connection timeout after 5s");
90
91        let err = DaemonError::UnknownCommand {
92            cmd: "foo".to_string(),
93        };
94        assert_eq!(err.to_string(), "unknown command: foo");
95
96        let err = DaemonError::MissingParameter {
97            param: "file".to_string(),
98        };
99        assert_eq!(err.to_string(), "missing required parameter: file");
100
101        let err = DaemonError::PermissionDenied {
102            path: PathBuf::from("/tmp/test"),
103        };
104        assert_eq!(err.to_string(), "permission denied: /tmp/test");
105
106        let err = DaemonError::StalePidFile { pid: 99999 };
107        assert_eq!(
108            err.to_string(),
109            "stale PID file (process 99999 not running)"
110        );
111
112        let err = DaemonError::AddressInUse {
113            addr: "/tmp/test.sock".to_string(),
114        };
115        assert_eq!(err.to_string(), "address already in use: /tmp/test.sock");
116
117        let err = DaemonError::InvalidMessage("bad json".to_string());
118        assert_eq!(err.to_string(), "invalid IPC message: bad json");
119    }
120
121    #[test]
122    fn test_daemon_error_io_from() {
123        let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
124        let daemon_err: DaemonError = io_err.into();
125        assert!(matches!(daemon_err, DaemonError::Io(_)));
126    }
127}