net_lattice_core/error.rs
1use std::fmt;
2
3/// The single error type surfaced across the Net Lattice workspace.
4///
5/// Provider trait methods return `Result<T, Error>` — never a raw OS error
6/// type (`std::io::Error`, a bare `errno`, a Windows `DWORD`). See
7/// ARCHITECTURE.md's Error Model for why.
8#[derive(Debug)]
9pub enum Error {
10 /// The operation requires privileges the caller does not have (e.g.
11 /// `CAP_NET_ADMIN` on Linux, Administrator on Windows).
12 PermissionDenied,
13 /// The referenced object does not exist.
14 NotFound,
15 /// An object with the same identity already exists.
16 AlreadyExists,
17 /// The operation has no meaning on this backend at all, as opposed to
18 /// a `Capability` being merely absent at runtime.
19 Unsupported,
20 /// The operation is not valid given the object's current state.
21 InvalidState,
22 /// An event channel's producer (the backend's background watcher) has
23 /// shut down — no further events will ever arrive. Distinct from a
24 /// timeout (which is not an error at all — see
25 /// `EventReceiver::recv_timeout`): this means the stream is over for
26 /// good, typically because the backend itself, or the connection it
27 /// watches over, was dropped.
28 Disconnected,
29 /// Escape hatch preserving the raw backend-specific error for
30 /// diagnostics. Not the primary way consumers are expected to match on
31 /// failures.
32 Platform(PlatformErrorCode),
33}
34
35/// A platform-tagged raw error code.
36///
37/// Linux errno is a signed `i32`, Windows error codes are an unsigned
38/// `DWORD` (`u32`); collapsing both into one untyped integer would either
39/// truncate one of them or imply the two are comparable, which they are
40/// not.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum PlatformErrorCode {
43 Linux(i32),
44 Windows(u32),
45 Darwin(i32),
46}
47
48impl fmt::Display for Error {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Error::PermissionDenied => write!(f, "permission denied"),
52 Error::NotFound => write!(f, "not found"),
53 Error::AlreadyExists => write!(f, "already exists"),
54 Error::Unsupported => write!(f, "unsupported operation"),
55 Error::InvalidState => write!(f, "invalid state"),
56 Error::Disconnected => write!(f, "event channel disconnected"),
57 Error::Platform(code) => write!(f, "platform error: {code:?}"),
58 }
59 }
60}
61
62impl std::error::Error for Error {}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn every_public_error_has_a_stable_display_message() {
70 let cases = [
71 (Error::PermissionDenied, "permission denied"),
72 (Error::NotFound, "not found"),
73 (Error::AlreadyExists, "already exists"),
74 (Error::Unsupported, "unsupported operation"),
75 (Error::InvalidState, "invalid state"),
76 (Error::Disconnected, "event channel disconnected"),
77 ];
78 for (error, expected) in cases {
79 assert_eq!(error.to_string(), expected);
80 }
81 assert_eq!(
82 Error::Platform(PlatformErrorCode::Linux(-1)).to_string(),
83 "platform error: Linux(-1)"
84 );
85 }
86
87 #[test]
88 fn platform_error_codes_preserve_their_platform_and_value() {
89 assert_eq!(PlatformErrorCode::Linux(-1), PlatformErrorCode::Linux(-1));
90 assert_ne!(PlatformErrorCode::Windows(1), PlatformErrorCode::Darwin(1));
91 }
92}