1use std::error::Error;
10use std::fmt;
11
12pub type Result<T> = std::result::Result<T, StatusCode>;
14
15const UNKNOWN_ERROR: i32 = -2147483647 - 1;
16
17#[derive(Default, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
22#[non_exhaustive]
23pub enum StatusCode {
24 #[default]
26 Ok,
27 Unknown,
29 NoMemory,
31 InvalidOperation,
33 BadValue,
35 BadType,
37 NameNotFound,
39 PermissionDenied,
41 NoInit,
43 AlreadyExists,
45 DeadObject,
47 FailedTransaction,
49 UnknownTransaction,
51 BadIndex,
53 FdsNotAllowed,
55 UnexpectedNull,
57 NotEnoughData,
59 WouldBlock,
61 TimedOut,
63 BadFd,
65 Errno(i32),
67 ServiceSpecific(i32),
69}
70
71impl Error for StatusCode {}
72
73impl fmt::Display for StatusCode {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 match self {
76 StatusCode::Ok => write!(f, "Ok"),
77 StatusCode::Unknown => write!(f, "Unknown"),
78 StatusCode::NoMemory => write!(f, "NoMemory"),
79 StatusCode::InvalidOperation => write!(f, "InvalidOperation"),
80 StatusCode::BadValue => write!(f, "BadValue"),
81 StatusCode::BadType => write!(f, "BadType"),
82 StatusCode::NameNotFound => write!(f, "NameNotFound"),
83 StatusCode::PermissionDenied => write!(f, "PermissionDenied"),
84 StatusCode::NoInit => write!(f, "NoInit"),
85 StatusCode::AlreadyExists => write!(f, "AlreadyExists"),
86 StatusCode::DeadObject => write!(f, "DeadObject"),
87 StatusCode::FailedTransaction => write!(f, "FailedTransaction"),
88 StatusCode::UnknownTransaction => write!(f, "UnknownTransaction"),
89 StatusCode::BadIndex => write!(f, "BadIndex"),
90 StatusCode::FdsNotAllowed => write!(f, "FdsNotAllowed"),
91 StatusCode::UnexpectedNull => write!(f, "UnexpectedNull"),
92 StatusCode::NotEnoughData => write!(f, "NotEnoughData"),
93 StatusCode::WouldBlock => write!(f, "WouldBlock"),
94 StatusCode::TimedOut => write!(f, "TimedOut"),
95 StatusCode::BadFd => write!(f, "BadFd"),
96 StatusCode::Errno(errno) => write!(f, "Errno({errno})"),
97 StatusCode::ServiceSpecific(v) => write!(f, "ServiceSpecific({v})"),
98 }
99 }
100}
101
102impl From<StatusCode> for i32 {
103 fn from(code: StatusCode) -> Self {
104 match code {
105 StatusCode::Ok => 0,
106 StatusCode::Unknown => UNKNOWN_ERROR as _,
107 StatusCode::NoMemory => -(rustix::io::Errno::NOMEM.raw_os_error()),
108 StatusCode::InvalidOperation => -(rustix::io::Errno::NOSYS.raw_os_error()),
109 StatusCode::BadValue => -(rustix::io::Errno::INVAL.raw_os_error()),
110 StatusCode::BadType => UNKNOWN_ERROR + 1,
111 StatusCode::NameNotFound => -(rustix::io::Errno::NOENT.raw_os_error()),
112 StatusCode::PermissionDenied => -(rustix::io::Errno::PERM.raw_os_error()),
113 StatusCode::NoInit => -(rustix::io::Errno::NODEV.raw_os_error()),
114 StatusCode::AlreadyExists => -(rustix::io::Errno::EXIST.raw_os_error()),
115 StatusCode::DeadObject => -(rustix::io::Errno::PIPE.raw_os_error()),
116 StatusCode::FailedTransaction => UNKNOWN_ERROR + 2,
117 StatusCode::UnknownTransaction => -(rustix::io::Errno::BADMSG.raw_os_error()),
118 StatusCode::BadIndex => -(rustix::io::Errno::OVERFLOW.raw_os_error()),
119 StatusCode::FdsNotAllowed => UNKNOWN_ERROR + 7,
120 StatusCode::UnexpectedNull => UNKNOWN_ERROR + 8,
121 StatusCode::NotEnoughData => -(rustix::io::Errno::NODATA.raw_os_error()),
122 StatusCode::WouldBlock => -(rustix::io::Errno::WOULDBLOCK.raw_os_error()),
123 StatusCode::TimedOut => -(rustix::io::Errno::TIMEDOUT.raw_os_error()),
124 StatusCode::BadFd => -(rustix::io::Errno::BADF.raw_os_error()),
125 StatusCode::ServiceSpecific(v) => v,
126 StatusCode::Errno(errno) => errno,
127 }
128 }
129}
130
131impl From<i32> for StatusCode {
132 fn from(code: i32) -> Self {
133 match code {
134 code if code == StatusCode::Ok.into() => StatusCode::Ok,
135 code if code == StatusCode::Unknown.into() => StatusCode::Unknown,
136 code if code == StatusCode::NoMemory.into() => StatusCode::NoMemory,
137 code if code == StatusCode::InvalidOperation.into() => StatusCode::InvalidOperation,
138 code if code == StatusCode::BadValue.into() => StatusCode::BadValue,
139 code if code == StatusCode::BadType.into() => StatusCode::BadType,
140 code if code == StatusCode::NameNotFound.into() => StatusCode::NameNotFound,
141 code if code == StatusCode::PermissionDenied.into() => StatusCode::PermissionDenied,
142 code if code == StatusCode::NoInit.into() => StatusCode::NoInit,
143 code if code == StatusCode::AlreadyExists.into() => StatusCode::AlreadyExists,
144 code if code == StatusCode::DeadObject.into() => StatusCode::DeadObject,
145 code if code == StatusCode::FailedTransaction.into() => StatusCode::FailedTransaction,
146 code if code == StatusCode::UnknownTransaction.into() => StatusCode::UnknownTransaction,
147 code if code == StatusCode::BadIndex.into() => StatusCode::BadIndex,
148 code if code == StatusCode::FdsNotAllowed.into() => StatusCode::FdsNotAllowed,
149 code if code == StatusCode::UnexpectedNull.into() => StatusCode::UnexpectedNull,
150 code if code == StatusCode::NotEnoughData.into() => StatusCode::NotEnoughData,
151 code if code == StatusCode::WouldBlock.into() => StatusCode::WouldBlock,
152 code if code == StatusCode::TimedOut.into() => StatusCode::TimedOut,
153 code if code == StatusCode::BadFd.into() => StatusCode::BadFd,
154 code if code < 0 => StatusCode::Errno(code),
155 _ => StatusCode::ServiceSpecific(code),
156 }
157 }
158}
159
160impl From<std::array::TryFromSliceError> for StatusCode {
161 fn from(_: std::array::TryFromSliceError) -> Self {
162 StatusCode::NotEnoughData
163 }
164}
165
166impl From<std::io::Error> for StatusCode {
167 fn from(err: std::io::Error) -> Self {
168 match err.raw_os_error() {
173 Some(raw) => StatusCode::from(rustix::io::Errno::from_raw_os_error(raw)),
174 None => StatusCode::Unknown,
175 }
176 }
177}
178
179impl From<rustix::io::Errno> for StatusCode {
180 fn from(errno: rustix::io::Errno) -> Self {
181 match errno {
182 rustix::io::Errno::NOMEM => StatusCode::NoMemory,
183 rustix::io::Errno::NOSYS => StatusCode::InvalidOperation,
184 rustix::io::Errno::INVAL => StatusCode::BadValue,
185 rustix::io::Errno::NOENT => StatusCode::NameNotFound,
186 rustix::io::Errno::PERM => StatusCode::PermissionDenied,
187 rustix::io::Errno::NODEV => StatusCode::NoInit,
188 rustix::io::Errno::EXIST => StatusCode::AlreadyExists,
189 rustix::io::Errno::PIPE => StatusCode::DeadObject,
190 rustix::io::Errno::BADMSG => StatusCode::UnknownTransaction,
191 rustix::io::Errno::OVERFLOW => StatusCode::BadIndex,
192 rustix::io::Errno::NODATA => StatusCode::NotEnoughData,
193 rustix::io::Errno::WOULDBLOCK => StatusCode::WouldBlock,
194 rustix::io::Errno::TIMEDOUT => StatusCode::TimedOut,
195 rustix::io::Errno::BADF => StatusCode::BadFd,
196 _ => StatusCode::Errno(-errno.raw_os_error()),
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_status_code() {
207 let code = StatusCode::Ok;
208 assert_eq!(code, StatusCode::from(0));
209 assert_eq!(code, StatusCode::from(Into::<i32>::into(StatusCode::Ok)));
210
211 let code = StatusCode::Unknown;
212 assert_eq!(code, StatusCode::from(UNKNOWN_ERROR));
213 assert_eq!(
214 code,
215 StatusCode::from(Into::<i32>::into(StatusCode::Unknown))
216 );
217
218 let code = StatusCode::NoMemory;
219 assert_eq!(
220 code,
221 StatusCode::from(-(rustix::io::Errno::NOMEM.raw_os_error()))
222 );
223 assert_eq!(
224 code,
225 StatusCode::from(Into::<i32>::into(StatusCode::NoMemory))
226 );
227
228 let code = StatusCode::InvalidOperation;
229 assert_eq!(
230 code,
231 StatusCode::from(-(rustix::io::Errno::NOSYS.raw_os_error()))
232 );
233 assert_eq!(
234 code,
235 StatusCode::from(Into::<i32>::into(StatusCode::InvalidOperation))
236 );
237
238 let code = StatusCode::BadValue;
239 assert_eq!(
240 code,
241 StatusCode::from(-(rustix::io::Errno::INVAL.raw_os_error()))
242 );
243 assert_eq!(
244 code,
245 StatusCode::from(Into::<i32>::into(StatusCode::BadValue))
246 );
247
248 let code = StatusCode::BadType;
249 assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 1));
250 assert_eq!(
251 code,
252 StatusCode::from(Into::<i32>::into(StatusCode::BadType))
253 );
254
255 let code = StatusCode::NameNotFound;
256 assert_eq!(
257 code,
258 StatusCode::from(-(rustix::io::Errno::NOENT.raw_os_error()))
259 );
260 assert_eq!(
261 code,
262 StatusCode::from(Into::<i32>::into(StatusCode::NameNotFound))
263 );
264
265 let code = StatusCode::PermissionDenied;
266 assert_eq!(
267 code,
268 StatusCode::from(-(rustix::io::Errno::PERM.raw_os_error()))
269 );
270 assert_eq!(
271 code,
272 StatusCode::from(Into::<i32>::into(StatusCode::PermissionDenied))
273 );
274
275 let code = StatusCode::NoInit;
276 assert_eq!(
277 code,
278 StatusCode::from(-(rustix::io::Errno::NODEV.raw_os_error()))
279 );
280 assert_eq!(
281 code,
282 StatusCode::from(Into::<i32>::into(StatusCode::NoInit))
283 );
284
285 let code = StatusCode::AlreadyExists;
286 assert_eq!(
287 code,
288 StatusCode::from(-(rustix::io::Errno::EXIST.raw_os_error()))
289 );
290 assert_eq!(
291 code,
292 StatusCode::from(Into::<i32>::into(StatusCode::AlreadyExists))
293 );
294
295 let code = StatusCode::DeadObject;
296 assert_eq!(
297 code,
298 StatusCode::from(-(rustix::io::Errno::PIPE.raw_os_error()))
299 );
300 assert_eq!(
301 code,
302 StatusCode::from(Into::<i32>::into(StatusCode::DeadObject))
303 );
304
305 let code = StatusCode::FailedTransaction;
306 assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 2));
307 assert_eq!(
308 code,
309 StatusCode::from(Into::<i32>::into(StatusCode::FailedTransaction))
310 );
311
312 let code = StatusCode::UnknownTransaction;
313 assert_eq!(
314 code,
315 StatusCode::from(-(rustix::io::Errno::BADMSG.raw_os_error()))
316 );
317 assert_eq!(
318 code,
319 StatusCode::from(Into::<i32>::into(StatusCode::UnknownTransaction))
320 );
321
322 let code = StatusCode::BadIndex;
323 assert_eq!(
324 code,
325 StatusCode::from(-(rustix::io::Errno::OVERFLOW.raw_os_error()))
326 );
327 assert_eq!(
328 code,
329 StatusCode::from(Into::<i32>::into(StatusCode::BadIndex))
330 );
331
332 let code = StatusCode::FdsNotAllowed;
333 assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 7));
334 assert_eq!(
335 code,
336 StatusCode::from(Into::<i32>::into(StatusCode::FdsNotAllowed))
337 );
338
339 let code = StatusCode::UnexpectedNull;
340 assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 8));
341 assert_eq!(
342 code,
343 StatusCode::from(Into::<i32>::into(StatusCode::UnexpectedNull))
344 );
345
346 let code = StatusCode::NotEnoughData;
347 assert_eq!(
348 code,
349 StatusCode::from(-(rustix::io::Errno::NODATA.raw_os_error()))
350 );
351 assert_eq!(
352 code,
353 StatusCode::from(Into::<i32>::into(StatusCode::NotEnoughData))
354 );
355
356 let code = StatusCode::WouldBlock;
357 assert_eq!(
358 code,
359 StatusCode::from(-(rustix::io::Errno::WOULDBLOCK.raw_os_error()))
360 );
361 assert_eq!(
362 code,
363 StatusCode::from(Into::<i32>::into(StatusCode::WouldBlock))
364 );
365
366 let code = StatusCode::TimedOut;
367 assert_eq!(
368 code,
369 StatusCode::from(-(rustix::io::Errno::TIMEDOUT.raw_os_error()))
370 );
371 assert_eq!(
372 code,
373 StatusCode::from(Into::<i32>::into(StatusCode::TimedOut))
374 );
375
376 let code = StatusCode::BadFd;
377 assert_eq!(
378 code,
379 StatusCode::from(-(rustix::io::Errno::BADF.raw_os_error()))
380 );
381 assert_eq!(code, StatusCode::from(Into::<i32>::into(StatusCode::BadFd)));
382
383 let code = StatusCode::ServiceSpecific(1);
384 assert_eq!(code, StatusCode::from(1));
385 assert_eq!(
386 code,
387 StatusCode::from(Into::<i32>::into(StatusCode::ServiceSpecific(1)))
388 );
389
390 let code: StatusCode = StatusCode::Errno(-64);
391 assert_eq!(code, StatusCode::from(-64));
392 assert_eq!(
393 code,
394 StatusCode::from(Into::<i32>::into(StatusCode::Errno(-64)))
395 );
396 }
397
398 #[test]
399 fn test_status_code_from_errno() {
400 let code = StatusCode::from(rustix::io::Errno::NOMEM);
401 assert_eq!(code, StatusCode::NoMemory);
402
403 let code = StatusCode::from(rustix::io::Errno::NOSYS);
404 assert_eq!(code, StatusCode::InvalidOperation);
405
406 let code = StatusCode::from(rustix::io::Errno::INVAL);
407 assert_eq!(code, StatusCode::BadValue);
408
409 let code = StatusCode::from(rustix::io::Errno::NOENT);
410 assert_eq!(code, StatusCode::NameNotFound);
411
412 let code = StatusCode::from(rustix::io::Errno::PERM);
413 assert_eq!(code, StatusCode::PermissionDenied);
414
415 let code = StatusCode::from(rustix::io::Errno::NODEV);
416 assert_eq!(code, StatusCode::NoInit);
417
418 let code = StatusCode::from(rustix::io::Errno::EXIST);
419 assert_eq!(code, StatusCode::AlreadyExists);
420
421 let code = StatusCode::from(rustix::io::Errno::PIPE);
422 assert_eq!(code, StatusCode::DeadObject);
423
424 let code = StatusCode::from(rustix::io::Errno::BADMSG);
425 assert_eq!(code, StatusCode::UnknownTransaction);
426
427 let code = StatusCode::from(rustix::io::Errno::OVERFLOW);
428 assert_eq!(code, StatusCode::BadIndex);
429
430 let code = StatusCode::from(rustix::io::Errno::NODATA);
431 assert_eq!(code, StatusCode::NotEnoughData);
432
433 let code = StatusCode::from(rustix::io::Errno::WOULDBLOCK);
434 assert_eq!(code, StatusCode::WouldBlock);
435
436 let code = StatusCode::from(rustix::io::Errno::TIMEDOUT);
437 assert_eq!(code, StatusCode::TimedOut);
438
439 let code = StatusCode::from(rustix::io::Errno::BADF);
440 assert_eq!(code, StatusCode::BadFd);
441
442 let code = StatusCode::from(rustix::io::Errno::from_raw_os_error(64));
443 assert_eq!(code, StatusCode::Errno(-64));
444 }
445
446 #[test]
450 fn status_code_from_io_error_preserves_errno() {
451 let enoent = std::io::Error::from_raw_os_error(rustix::io::Errno::NOENT.raw_os_error());
452 assert_eq!(
453 StatusCode::from(enoent),
454 StatusCode::NameNotFound,
455 "ENOENT must map to NameNotFound, not be flattened to BadFd"
456 );
457
458 let ebadf = std::io::Error::from_raw_os_error(rustix::io::Errno::BADF.raw_os_error());
460 assert_eq!(StatusCode::from(ebadf), StatusCode::BadFd);
461
462 let non_os = std::io::Error::other("no errno");
464 assert_eq!(non_os.raw_os_error(), None);
465 assert_eq!(StatusCode::from(non_os), StatusCode::Unknown);
466 }
467}