1use std::time::Duration;
6
7pub type Result<T> = std::result::Result<T, DaemonError>;
9
10#[derive(Debug, thiserror::Error)]
15pub enum DaemonError {
16 #[error("configuration error: {0}")]
18 Config(String),
19
20 #[error("initialization failed: {0}")]
22 Init(String),
23
24 #[error("runtime error: {0}")]
26 Runtime(String),
27
28 #[error("shutdown error: {0}")]
30 Shutdown(String),
31
32 #[error("shutdown timed out after {0:?}")]
34 ShutdownTimeout(Duration),
35
36 #[error("health check failed: {0}")]
38 HealthCheck(String),
39
40 #[error("resource limit exceeded: {resource} (limit: {limit}, actual: {actual})")]
42 ResourceLimit {
43 resource: String,
45 limit: u64,
47 actual: u64,
49 },
50
51 #[error("policy violation: {0}")]
53 PolicyViolation(String),
54
55 #[error("signal error: {0}")]
57 Signal(String),
58
59 #[error("daemon not found: {0}")]
61 NotFound(String),
62
63 #[error("invalid state: {0}")]
65 State(String),
66
67 #[error("I/O error: {0}")]
69 Io(#[from] std::io::Error),
70
71 #[error("serialization error: {0}")]
73 Serialization(String),
74
75 #[error("internal error: {0}")]
77 Internal(String),
78}
79
80impl DaemonError {
81 #[must_use]
83 pub fn config(msg: impl Into<String>) -> Self {
84 Self::Config(msg.into())
85 }
86
87 #[must_use]
89 pub fn init(msg: impl Into<String>) -> Self {
90 Self::Init(msg.into())
91 }
92
93 #[must_use]
95 pub fn runtime(msg: impl Into<String>) -> Self {
96 Self::Runtime(msg.into())
97 }
98
99 #[must_use]
101 pub fn shutdown(msg: impl Into<String>) -> Self {
102 Self::Shutdown(msg.into())
103 }
104
105 #[must_use]
107 pub fn health_check(msg: impl Into<String>) -> Self {
108 Self::HealthCheck(msg.into())
109 }
110
111 #[must_use]
113 pub fn policy_violation(msg: impl Into<String>) -> Self {
114 Self::PolicyViolation(msg.into())
115 }
116
117 #[must_use]
119 pub const fn is_recoverable(&self) -> bool {
120 matches!(
121 self,
122 Self::HealthCheck(_) | Self::ResourceLimit { .. } | Self::PolicyViolation(_)
123 )
124 }
125
126 #[must_use]
128 pub const fn is_fatal(&self) -> bool {
129 matches!(self, Self::Init(_) | Self::Internal(_))
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_error_display() {
139 let err = DaemonError::config("invalid port");
140 assert_eq!(err.to_string(), "configuration error: invalid port");
141 }
142
143 #[test]
144 fn test_error_recoverable() {
145 assert!(DaemonError::health_check("timeout").is_recoverable());
146 assert!(!DaemonError::init("failed").is_recoverable());
147 }
148
149 #[test]
150 fn test_error_fatal() {
151 assert!(DaemonError::init("failed").is_fatal());
152 assert!(!DaemonError::runtime("transient").is_fatal());
153 }
154
155 #[test]
156 fn test_resource_limit_error() {
157 let err = DaemonError::ResourceLimit {
158 resource: "memory".to_string(),
159 limit: 1024,
160 actual: 2048,
161 };
162 assert!(err.to_string().contains("memory"));
163 assert!(err.is_recoverable());
164 }
165}