1use std::{error::Error, fmt};
2
3#[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#[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}