1use std::path::PathBuf;
4
5#[derive(Debug, thiserror::Error)]
7pub enum DaemonError {
8 #[error("daemon not running (socket not found: {path})")]
9 NotRunning { path: PathBuf },
10
11 #[error("connection failed: {0}")]
12 Connection(#[from] std::io::Error),
13
14 #[error("protocol error: {0}")]
15 Protocol(String),
16
17 #[error("serialization error: {0}")]
18 Serialization(#[from] serde_json::Error),
19
20 #[error("request timed out after {timeout_secs}s")]
21 Timeout { timeout_secs: u64 },
22
23 #[error("daemon already running (pid {pid})")]
24 AlreadyRunning { pid: u32 },
25
26 #[error("stale PID file: process {pid} not alive")]
27 StalePid { pid: u32 },
28
29 #[error("daemon responded with error: [{code}] {message}")]
30 RemoteError { code: String, message: String },
31
32 #[error("message too large: {size} bytes (max {max})")]
33 MessageTooLarge { size: usize, max: usize },
34
35 #[error("lifecycle error: {0}")]
36 Lifecycle(String),
37
38 #[error("watch error: {0}")]
39 Watch(String),
40}
41
42pub type DaemonResult<T> = std::result::Result<T, DaemonError>;
44
45#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn watch_error_display() {
55 let err = DaemonError::Watch("directory not found".into());
56 assert_eq!(err.to_string(), "watch error: directory not found");
57 }
58
59 #[test]
60 fn all_variants_are_debug() {
61 let errors: Vec<DaemonError> = vec![
62 DaemonError::NotRunning {
63 path: PathBuf::from("/tmp/test.sock"),
64 },
65 DaemonError::Protocol("bad frame".into()),
66 DaemonError::Timeout { timeout_secs: 5 },
67 DaemonError::AlreadyRunning { pid: 1234 },
68 DaemonError::StalePid { pid: 5678 },
69 DaemonError::RemoteError {
70 code: "E001".into(),
71 message: "fail".into(),
72 },
73 DaemonError::MessageTooLarge { size: 100, max: 50 },
74 DaemonError::Lifecycle("shutdown".into()),
75 DaemonError::Watch("notify error".into()),
76 ];
77 for err in &errors {
78 let _ = format!("{err}");
80 let _ = format!("{err:?}");
81 }
82 }
83}