tldr_cli/commands/daemon/
error.rs1use std::io;
6use std::path::PathBuf;
7
8use thiserror::Error;
9
10#[derive(Debug, Error)]
12pub enum DaemonError {
13 #[error("daemon already running (PID: {pid})")]
15 AlreadyRunning { pid: u32 },
16
17 #[error("daemon not running")]
19 NotRunning,
20
21 #[error("failed to acquire PID file lock: {0}")]
23 LockFailed(io::Error),
24
25 #[error("failed to bind socket: {0}")]
27 SocketBindFailed(io::Error),
28
29 #[error("address already in use: {addr}")]
31 AddressInUse { addr: String },
32
33 #[error("connection refused")]
35 ConnectionRefused,
36
37 #[error("connection timeout after {timeout_secs}s")]
39 ConnectionTimeout { timeout_secs: u64 },
40
41 #[error("invalid IPC message: {0}")]
43 InvalidMessage(String),
44
45 #[error("unknown command: {cmd}")]
47 UnknownCommand { cmd: String },
48
49 #[error("missing required parameter: {param}")]
51 MissingParameter { param: String },
52
53 #[error("permission denied: {}", path.display())]
55 PermissionDenied { path: PathBuf },
56
57 #[error("stale PID file (process {pid} not running)")]
59 StalePidFile { pid: u32 },
60
61 #[error("IO error: {0}")]
63 Io(#[from] io::Error),
64
65 #[error("JSON error: {0}")]
67 Json(#[from] serde_json::Error),
68}
69
70pub 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}