Skip to main content

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}