Skip to main content

ranvier_core/
error.rs

1//! # RanvierError: Serde-Compatible Error Wrapper
2//!
3//! A ready-made error type that satisfies the Axon serde bounds
4//! (`Serialize + DeserializeOwned + Send + Sync + Debug`).
5//!
6//! Use this when you need typed error variants without defining a custom enum.
7
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11/// A serde-compatible error type for common Ranvier patterns.
12///
13/// This satisfies all Axon bounds (`Serialize + DeserializeOwned + Send + Sync + Debug`)
14/// and provides typed variants for frequent error categories.
15///
16/// # When to Use
17///
18/// - **Prototyping**: When `String` is too unstructured but a custom enum is overkill
19/// - **Examples**: As the standard error type in documentation examples
20/// - **Production**: When error categories map cleanly to these variants
21///
22/// # Example
23///
24/// ```rust
25/// use ranvier_core::error::RanvierError;
26///
27/// let err = RanvierError::not_found("User 42");
28/// assert_eq!(err.to_string(), "not found: User 42");
29/// ```
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31pub enum RanvierError {
32    /// Generic message error
33    Message(String),
34    /// Resource not found
35    NotFound(String),
36    /// Input validation failure
37    Validation(String),
38    /// Internal / unexpected error
39    Internal(String),
40}
41
42impl RanvierError {
43    /// Create a generic message error.
44    pub fn message(msg: impl Into<String>) -> Self {
45        Self::Message(msg.into())
46    }
47
48    /// Create a not-found error.
49    pub fn not_found(what: impl Into<String>) -> Self {
50        Self::NotFound(what.into())
51    }
52
53    /// Create a validation error.
54    pub fn validation(msg: impl Into<String>) -> Self {
55        Self::Validation(msg.into())
56    }
57
58    /// Create an internal error.
59    pub fn internal(msg: impl Into<String>) -> Self {
60        Self::Internal(msg.into())
61    }
62}
63
64impl fmt::Display for RanvierError {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        match self {
67            RanvierError::Message(s) => write!(f, "{s}"),
68            RanvierError::NotFound(s) => write!(f, "not found: {s}"),
69            RanvierError::Validation(s) => write!(f, "validation: {s}"),
70            RanvierError::Internal(s) => write!(f, "internal: {s}"),
71        }
72    }
73}
74
75impl std::error::Error for RanvierError {}
76
77impl From<String> for RanvierError {
78    fn from(s: String) -> Self {
79        RanvierError::Message(s)
80    }
81}
82
83impl From<&str> for RanvierError {
84    fn from(s: &str) -> Self {
85        RanvierError::Message(s.to_string())
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_display() {
95        assert_eq!(RanvierError::message("oops").to_string(), "oops");
96        assert_eq!(
97            RanvierError::not_found("user 42").to_string(),
98            "not found: user 42"
99        );
100    }
101
102    #[test]
103    fn test_serde_roundtrip() {
104        let err = RanvierError::validation("bad email");
105        let json = serde_json::to_string(&err).unwrap();
106        let back: RanvierError = serde_json::from_str(&json).unwrap();
107        assert_eq!(err, back);
108    }
109
110    #[test]
111    fn test_from_string() {
112        let err: RanvierError = "something went wrong".into();
113        assert!(matches!(err, RanvierError::Message(_)));
114    }
115}