Skip to main content

rust_ef/
error.rs

1//! Error types for Rust Entity Framework (rust-ef).
2//!
3//! Each variant carries an optional `#[source]` for preserving the original
4//! error chain, enabling programmatic inspection via `std::error::Error::source()`.
5//! Convenience constructors (`EFError::query(msg)`, etc.) keep call sites concise;
6//! `with_source` and `with_sql` builders attach context after construction.
7
8use thiserror::Error;
9
10/// Boxed error suitable for `EFError::source`.
11pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
12
13/// Represents all possible errors that can occur in rust-ef operations.
14#[derive(Error, Debug)]
15pub enum EFError {
16    /// Database connection error.
17    #[error("database connection error: {0}")]
18    Connection(String, #[source] Option<BoxError>),
19
20    /// Query execution error.
21    #[error("query error: {0}")]
22    Query(String, #[source] Option<BoxError>),
23
24    /// Entity not found error.
25    #[error("entity not found: {0}")]
26    NotFound(String, #[source] Option<BoxError>),
27
28    /// Model validation error.
29    #[error("model validation error: {0}")]
30    ModelValidation(String, #[source] Option<BoxError>),
31
32    /// Migration error.
33    #[error("migration error: {0}")]
34    Migration(String, #[source] Option<BoxError>),
35
36    /// Provider-specific error.
37    #[error("provider error: {0}")]
38    Provider(String, #[source] Option<BoxError>),
39
40    /// Configuration error.
41    #[error("configuration error: {0}")]
42    Configuration(String, #[source] Option<BoxError>),
43
44    /// Change tracking error.
45    #[error("change tracking error: {0}")]
46    ChangeTracking(String, #[source] Option<BoxError>),
47
48    /// Transaction error.
49    #[error("transaction error: {0}")]
50    Transaction(String, #[source] Option<BoxError>),
51
52    /// Concurrency conflict error.
53    #[error("concurrency conflict: {0}")]
54    ConcurrencyConflict(String, #[source] Option<BoxError>),
55
56    /// Type conversion error.
57    #[error("type conversion error: {0}")]
58    TypeConversion(String, #[source] Option<BoxError>),
59
60    /// General / unknown error.
61    #[error("{0}")]
62    Other(String, #[source] Option<BoxError>),
63}
64
65impl EFError {
66    pub fn connection(msg: impl Into<String>) -> Self {
67        EFError::Connection(msg.into(), None)
68    }
69
70    pub fn query(msg: impl Into<String>) -> Self {
71        EFError::Query(msg.into(), None)
72    }
73
74    pub fn not_found(msg: impl Into<String>) -> Self {
75        EFError::NotFound(msg.into(), None)
76    }
77
78    pub fn model_validation(msg: impl Into<String>) -> Self {
79        EFError::ModelValidation(msg.into(), None)
80    }
81
82    pub fn migration(msg: impl Into<String>) -> Self {
83        EFError::Migration(msg.into(), None)
84    }
85
86    pub fn provider(msg: impl Into<String>) -> Self {
87        EFError::Provider(msg.into(), None)
88    }
89
90    pub fn configuration(msg: impl Into<String>) -> Self {
91        EFError::Configuration(msg.into(), None)
92    }
93
94    pub fn change_tracking(msg: impl Into<String>) -> Self {
95        EFError::ChangeTracking(msg.into(), None)
96    }
97
98    pub fn transaction(msg: impl Into<String>) -> Self {
99        EFError::Transaction(msg.into(), None)
100    }
101
102    pub fn concurrency_conflict(msg: impl Into<String>) -> Self {
103        EFError::ConcurrencyConflict(msg.into(), None)
104    }
105
106    pub fn type_conversion(msg: impl Into<String>) -> Self {
107        EFError::TypeConversion(msg.into(), None)
108    }
109
110    pub fn other(msg: impl Into<String>) -> Self {
111        EFError::Other(msg.into(), None)
112    }
113
114    /// Attaches a source error to this `EFError`, preserving the error chain.
115    pub fn with_source(self, source: BoxError) -> Self {
116        match self {
117            EFError::Connection(m, _) => EFError::Connection(m, Some(source)),
118            EFError::Query(m, _) => EFError::Query(m, Some(source)),
119            EFError::NotFound(m, _) => EFError::NotFound(m, Some(source)),
120            EFError::ModelValidation(m, _) => EFError::ModelValidation(m, Some(source)),
121            EFError::Migration(m, _) => EFError::Migration(m, Some(source)),
122            EFError::Provider(m, _) => EFError::Provider(m, Some(source)),
123            EFError::Configuration(m, _) => EFError::Configuration(m, Some(source)),
124            EFError::ChangeTracking(m, _) => EFError::ChangeTracking(m, Some(source)),
125            EFError::Transaction(m, _) => EFError::Transaction(m, Some(source)),
126            EFError::ConcurrencyConflict(m, _) => EFError::ConcurrencyConflict(m, Some(source)),
127            EFError::TypeConversion(m, _) => EFError::TypeConversion(m, Some(source)),
128            EFError::Other(m, _) => EFError::Other(m, Some(source)),
129        }
130    }
131
132    /// Extracts the human-readable message without the source chain.
133    pub fn message(&self) -> &str {
134        match self {
135            EFError::Connection(m, _) => m,
136            EFError::Query(m, _) => m,
137            EFError::NotFound(m, _) => m,
138            EFError::ModelValidation(m, _) => m,
139            EFError::Migration(m, _) => m,
140            EFError::Provider(m, _) => m,
141            EFError::Configuration(m, _) => m,
142            EFError::ChangeTracking(m, _) => m,
143            EFError::Transaction(m, _) => m,
144            EFError::ConcurrencyConflict(m, _) => m,
145            EFError::TypeConversion(m, _) => m,
146            EFError::Other(m, _) => m,
147        }
148    }
149}
150
151/// Result type alias for rust-ef operations.
152pub type EFResult<T> = Result<T, EFError>;