saddle-core 0.1.0

Shared contracts for Saddle components
Documentation
use std::{error::Error, fmt};

/// Stable error categories used at component and Service boundaries.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
    InvalidArgument,
    NotFound,
    Conflict,
    Business,
    Unavailable,
    Infrastructure,
    Internal,
}

/// An error safe to propagate across Saddle component boundaries.
///
/// `message` must not contain credentials, SQL parameters, request payloads or
/// other sensitive implementation details.
#[derive(Debug)]
pub struct SaddleError {
    kind: ErrorKind,
    code: &'static str,
    message: String,
}

impl SaddleError {
    pub fn new(kind: ErrorKind, code: &'static str, message: impl Into<String>) -> Self {
        Self {
            kind,
            code,
            message: message.into(),
        }
    }

    pub const fn kind(&self) -> ErrorKind {
        self.kind
    }

    pub const fn code(&self) -> &'static str {
        self.code
    }

    pub fn message(&self) -> &str {
        &self.message
    }
}

impl fmt::Display for SaddleError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "{}: {}", self.code, self.message)
    }
}

impl Error for SaddleError {}

pub type Result<T> = std::result::Result<T, SaddleError>;

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

    #[test]
    fn error_exposes_stable_classification() {
        let error = SaddleError::new(ErrorKind::NotFound, "user.not_found", "user not found");

        assert_eq!(error.kind(), ErrorKind::NotFound);
        assert_eq!(error.code(), "user.not_found");
        assert_eq!(error.to_string(), "user.not_found: user not found");
    }
}