net-lattice-core 0.3.2

Foundational error, ID, and result types shared across the Net Lattice workspace. No OS dependency, no networking-specific types.
Documentation
use std::fmt;

/// The single error type surfaced across the Net Lattice workspace.
///
/// Provider trait methods return `Result<T, Error>` — never a raw OS error
/// type (`std::io::Error`, a bare `errno`, a Windows `DWORD`). See
/// ARCHITECTURE.md's Error Model for why.
#[derive(Debug)]
pub enum Error {
    /// The operation requires privileges the caller does not have (e.g.
    /// `CAP_NET_ADMIN` on Linux, Administrator on Windows).
    PermissionDenied,
    /// The referenced object does not exist.
    NotFound,
    /// An object with the same identity already exists.
    AlreadyExists,
    /// The operation has no meaning on this backend at all, as opposed to
    /// a `Capability` being merely absent at runtime.
    Unsupported,
    /// The operation is not valid given the object's current state.
    InvalidState,
    /// An event channel's producer (the backend's background watcher) has
    /// shut down — no further events will ever arrive. Distinct from a
    /// timeout (which is not an error at all — see
    /// `EventReceiver::recv_timeout`): this means the stream is over for
    /// good, typically because the backend itself, or the connection it
    /// watches over, was dropped.
    Disconnected,
    /// Escape hatch preserving the raw backend-specific error for
    /// diagnostics. Not the primary way consumers are expected to match on
    /// failures.
    Platform(PlatformErrorCode),
}

/// A platform-tagged raw error code.
///
/// Linux errno is a signed `i32`, Windows error codes are an unsigned
/// `DWORD` (`u32`); collapsing both into one untyped integer would either
/// truncate one of them or imply the two are comparable, which they are
/// not.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlatformErrorCode {
    Linux(i32),
    Windows(u32),
    Darwin(i32),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::PermissionDenied => write!(f, "permission denied"),
            Error::NotFound => write!(f, "not found"),
            Error::AlreadyExists => write!(f, "already exists"),
            Error::Unsupported => write!(f, "unsupported operation"),
            Error::InvalidState => write!(f, "invalid state"),
            Error::Disconnected => write!(f, "event channel disconnected"),
            Error::Platform(code) => write!(f, "platform error: {code:?}"),
        }
    }
}

impl std::error::Error for Error {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn every_public_error_has_a_stable_display_message() {
        let cases = [
            (Error::PermissionDenied, "permission denied"),
            (Error::NotFound, "not found"),
            (Error::AlreadyExists, "already exists"),
            (Error::Unsupported, "unsupported operation"),
            (Error::InvalidState, "invalid state"),
            (Error::Disconnected, "event channel disconnected"),
        ];
        for (error, expected) in cases {
            assert_eq!(error.to_string(), expected);
        }
        assert_eq!(
            Error::Platform(PlatformErrorCode::Linux(-1)).to_string(),
            "platform error: Linux(-1)"
        );
    }

    #[test]
    fn platform_error_codes_preserve_their_platform_and_value() {
        assert_eq!(PlatformErrorCode::Linux(-1), PlatformErrorCode::Linux(-1));
        assert_ne!(PlatformErrorCode::Windows(1), PlatformErrorCode::Darwin(1));
    }
}