Skip to main content

saddle_core/
error.rs

1use std::{error::Error, fmt};
2
3/// Stable error categories used at component and Service boundaries.
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5#[non_exhaustive]
6pub enum ErrorKind {
7    InvalidArgument,
8    NotFound,
9    Conflict,
10    Business,
11    Unavailable,
12    Infrastructure,
13    Internal,
14}
15
16/// An error safe to propagate across Saddle component boundaries.
17///
18/// `message` must not contain credentials, SQL parameters, request payloads or
19/// other sensitive implementation details.
20#[derive(Debug)]
21pub struct SaddleError {
22    kind: ErrorKind,
23    code: &'static str,
24    message: String,
25}
26
27impl SaddleError {
28    pub fn new(kind: ErrorKind, code: &'static str, message: impl Into<String>) -> Self {
29        Self {
30            kind,
31            code,
32            message: message.into(),
33        }
34    }
35
36    pub const fn kind(&self) -> ErrorKind {
37        self.kind
38    }
39
40    pub const fn code(&self) -> &'static str {
41        self.code
42    }
43
44    pub fn message(&self) -> &str {
45        &self.message
46    }
47}
48
49impl fmt::Display for SaddleError {
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(formatter, "{}: {}", self.code, self.message)
52    }
53}
54
55impl Error for SaddleError {}
56
57pub type Result<T> = std::result::Result<T, SaddleError>;
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn error_exposes_stable_classification() {
65        let error = SaddleError::new(ErrorKind::NotFound, "user.not_found", "user not found");
66
67        assert_eq!(error.kind(), ErrorKind::NotFound);
68        assert_eq!(error.code(), "user.not_found");
69        assert_eq!(error.to_string(), "user.not_found: user not found");
70    }
71}