1use thiserror::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum DisconnectCause {
8 Unclean = 0,
10 Clean = 1,
12}
13
14pub fn disconnect_cause_name(cause: DisconnectCause) -> &'static str {
16 match cause {
17 DisconnectCause::Clean => "clean",
18 DisconnectCause::Unclean => "unclean",
19 }
20}
21
22#[derive(Debug, Error)]
24#[error("connection error to {url} (attempt {attempt}): {source}")]
25pub struct ConnectionError {
26 pub url: String,
28 pub attempt: u32,
30 #[source]
32 pub source: Box<dyn std::error::Error + Send + Sync>,
33}
34
35impl ConnectionError {
36 pub fn new(
38 url: impl Into<String>,
39 attempt: u32,
40 source: impl Into<Box<dyn std::error::Error + Send + Sync>>,
41 ) -> Self {
42 Self {
43 url: url.into(),
44 attempt,
45 source: source.into(),
46 }
47 }
48}
49
50#[derive(Debug, Error, Clone)]
52#[error("daemon error [{code}]: {message}")]
53pub struct DaemonError {
54 pub code: i64,
56 pub message: String,
58 pub data: Option<serde_json::Value>,
60}
61
62impl DaemonError {
63 pub fn new(code: i64, message: impl Into<String>) -> Self {
65 Self {
66 code,
67 message: message.into(),
68 data: None,
69 }
70 }
71
72 pub fn with_data(mut self, data: serde_json::Value) -> Self {
74 self.data = Some(data);
75 self
76 }
77}
78
79#[derive(Debug, Error)]
81#[error("timeout after {duration} waiting for {operation}")]
82pub struct TimeoutError {
83 pub operation: String,
85 pub duration: String,
87}
88
89impl TimeoutError {
90 pub fn new(operation: impl Into<String>, duration: impl Into<String>) -> Self {
92 Self {
93 operation: operation.into(),
94 duration: duration.into(),
95 }
96 }
97}
98
99#[derive(Debug, Error)]
101#[error("reconnect to {url} failed after {attempts} attempts")]
102pub struct ReconnectError {
103 pub url: String,
105 pub attempts: u32,
107 #[source]
109 pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
110}
111
112impl ReconnectError {
113 pub fn new(
115 url: impl Into<String>,
116 attempts: u32,
117 source: Option<Box<dyn std::error::Error + Send + Sync>>,
118 ) -> Self {
119 Self {
120 url: url.into(),
121 attempts,
122 source,
123 }
124 }
125}
126
127#[derive(Debug, Error)]
129#[error("stale loop {loop_id}: reattach accepted but liveness probe failed")]
130pub struct StaleLoopError {
131 pub loop_id: String,
133 #[source]
135 pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
136}
137
138impl StaleLoopError {
139 pub fn new(
141 loop_id: impl Into<String>,
142 source: Option<Box<dyn std::error::Error + Send + Sync>>,
143 ) -> Self {
144 Self {
145 loop_id: loop_id.into(),
146 source,
147 }
148 }
149}
150
151#[derive(Debug, Error)]
153#[error("{message} (state={state}, last_heartbeat={last_heartbeat:?})")]
154pub struct HeartbeatError {
155 pub last_heartbeat: Option<std::time::Instant>,
157 pub state: String,
159 pub message: String,
161}
162
163impl HeartbeatError {
164 pub fn new(
166 last_heartbeat: Option<std::time::Instant>,
167 state: impl Into<String>,
168 message: impl Into<String>,
169 ) -> Self {
170 Self {
171 last_heartbeat,
172 state: state.into(),
173 message: message.into(),
174 }
175 }
176}
177
178#[derive(Debug, Error)]
180pub enum Error {
181 #[error(transparent)]
183 Connection(#[from] ConnectionError),
184 #[error(transparent)]
186 Daemon(#[from] DaemonError),
187 #[error(transparent)]
189 Timeout(#[from] TimeoutError),
190 #[error(transparent)]
192 Reconnect(#[from] ReconnectError),
193 #[error(transparent)]
195 StaleLoop(#[from] StaleLoopError),
196 #[error(transparent)]
198 Heartbeat(#[from] HeartbeatError),
199 #[error("{0}")]
201 Protocol(String),
202 #[error(transparent)]
204 Io(#[from] std::io::Error),
205 #[error(transparent)]
207 Json(#[from] serde_json::Error),
208 #[error("{0}")]
210 Message(String),
211}
212
213impl Error {
214 pub fn protocol(msg: impl Into<String>) -> Self {
216 Self::Protocol(msg.into())
217 }
218
219 pub fn msg(msg: impl Into<String>) -> Self {
221 Self::Message(msg.into())
222 }
223}
224
225pub type Result<T> = std::result::Result<T, Error>;