1use thiserror::Error;
9
10pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
12
13#[derive(Error, Debug)]
15pub enum EFError {
16 #[error("database connection error: {0}")]
18 Connection(String, #[source] Option<BoxError>),
19
20 #[error("query error: {0}")]
22 Query(String, #[source] Option<BoxError>),
23
24 #[error("entity not found: {0}")]
26 NotFound(String, #[source] Option<BoxError>),
27
28 #[error("model validation error: {0}")]
30 ModelValidation(String, #[source] Option<BoxError>),
31
32 #[error("migration error: {0}")]
34 Migration(String, #[source] Option<BoxError>),
35
36 #[error("provider error: {0}")]
38 Provider(String, #[source] Option<BoxError>),
39
40 #[error("configuration error: {0}")]
42 Configuration(String, #[source] Option<BoxError>),
43
44 #[error("change tracking error: {0}")]
46 ChangeTracking(String, #[source] Option<BoxError>),
47
48 #[error("transaction error: {0}")]
50 Transaction(String, #[source] Option<BoxError>),
51
52 #[error("concurrency conflict: {0}")]
54 ConcurrencyConflict(String, #[source] Option<BoxError>),
55
56 #[error("type conversion error: {0}")]
58 TypeConversion(String, #[source] Option<BoxError>),
59
60 #[error("{0}")]
62 Other(String, #[source] Option<BoxError>),
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub enum EFErrorCode {
74 ConnectionRefused,
75 ConnectionTimeout,
76 QuerySyntax,
77 QueryTimeout,
78 EntityNotFound,
79 ModelValidation,
80 MigrationConflict,
81 ProviderUnsupported,
82 ConfigurationInvalid,
83 ChangeTrackingCorrupted,
84 TransactionAborted,
85 TransactionDeadlock,
86 ConcurrencyConflict,
87 TypeConversionFailed,
88 Unknown,
89}
90
91impl EFError {
92 pub fn connection(msg: impl Into<String>) -> Self {
93 EFError::Connection(msg.into(), None)
94 }
95
96 pub fn query(msg: impl Into<String>) -> Self {
97 EFError::Query(msg.into(), None)
98 }
99
100 pub fn not_found(msg: impl Into<String>) -> Self {
101 EFError::NotFound(msg.into(), None)
102 }
103
104 pub fn model_validation(msg: impl Into<String>) -> Self {
105 EFError::ModelValidation(msg.into(), None)
106 }
107
108 pub fn migration(msg: impl Into<String>) -> Self {
109 EFError::Migration(msg.into(), None)
110 }
111
112 pub fn provider(msg: impl Into<String>) -> Self {
113 EFError::Provider(msg.into(), None)
114 }
115
116 pub fn configuration(msg: impl Into<String>) -> Self {
117 EFError::Configuration(msg.into(), None)
118 }
119
120 pub fn change_tracking(msg: impl Into<String>) -> Self {
121 EFError::ChangeTracking(msg.into(), None)
122 }
123
124 pub fn transaction(msg: impl Into<String>) -> Self {
125 EFError::Transaction(msg.into(), None)
126 }
127
128 pub fn concurrency_conflict(msg: impl Into<String>) -> Self {
129 EFError::ConcurrencyConflict(msg.into(), None)
130 }
131
132 pub fn type_conversion(msg: impl Into<String>) -> Self {
133 EFError::TypeConversion(msg.into(), None)
134 }
135
136 pub fn other(msg: impl Into<String>) -> Self {
137 EFError::Other(msg.into(), None)
138 }
139
140 pub fn with_source(self, source: BoxError) -> Self {
142 match self {
143 EFError::Connection(m, _) => EFError::Connection(m, Some(source)),
144 EFError::Query(m, _) => EFError::Query(m, Some(source)),
145 EFError::NotFound(m, _) => EFError::NotFound(m, Some(source)),
146 EFError::ModelValidation(m, _) => EFError::ModelValidation(m, Some(source)),
147 EFError::Migration(m, _) => EFError::Migration(m, Some(source)),
148 EFError::Provider(m, _) => EFError::Provider(m, Some(source)),
149 EFError::Configuration(m, _) => EFError::Configuration(m, Some(source)),
150 EFError::ChangeTracking(m, _) => EFError::ChangeTracking(m, Some(source)),
151 EFError::Transaction(m, _) => EFError::Transaction(m, Some(source)),
152 EFError::ConcurrencyConflict(m, _) => EFError::ConcurrencyConflict(m, Some(source)),
153 EFError::TypeConversion(m, _) => EFError::TypeConversion(m, Some(source)),
154 EFError::Other(m, _) => EFError::Other(m, Some(source)),
155 }
156 }
157
158 pub fn message(&self) -> &str {
160 match self {
161 EFError::Connection(m, _) => m,
162 EFError::Query(m, _) => m,
163 EFError::NotFound(m, _) => m,
164 EFError::ModelValidation(m, _) => m,
165 EFError::Migration(m, _) => m,
166 EFError::Provider(m, _) => m,
167 EFError::Configuration(m, _) => m,
168 EFError::ChangeTracking(m, _) => m,
169 EFError::Transaction(m, _) => m,
170 EFError::ConcurrencyConflict(m, _) => m,
171 EFError::TypeConversion(m, _) => m,
172 EFError::Other(m, _) => m,
173 }
174 }
175
176 pub fn code(&self) -> EFErrorCode {
183 let msg = self.message().to_ascii_lowercase();
184 match self {
185 EFError::Connection(_, _) => {
186 if msg.contains("timeout") {
187 EFErrorCode::ConnectionTimeout
188 } else {
189 EFErrorCode::ConnectionRefused
190 }
191 }
192 EFError::Query(_, _) => {
193 if msg.contains("timeout") {
194 EFErrorCode::QueryTimeout
195 } else {
196 EFErrorCode::QuerySyntax
197 }
198 }
199 EFError::NotFound(_, _) => EFErrorCode::EntityNotFound,
200 EFError::ModelValidation(_, _) => EFErrorCode::ModelValidation,
201 EFError::Migration(_, _) => EFErrorCode::MigrationConflict,
202 EFError::Provider(_, _) => EFErrorCode::ProviderUnsupported,
203 EFError::Configuration(_, _) => EFErrorCode::ConfigurationInvalid,
204 EFError::ChangeTracking(_, _) => EFErrorCode::ChangeTrackingCorrupted,
205 EFError::Transaction(_, _) => {
206 if msg.contains("deadlock") {
207 EFErrorCode::TransactionDeadlock
208 } else {
209 EFErrorCode::TransactionAborted
210 }
211 }
212 EFError::ConcurrencyConflict(_, _) => EFErrorCode::ConcurrencyConflict,
213 EFError::TypeConversion(_, _) => EFErrorCode::TypeConversionFailed,
214 EFError::Other(_, _) => EFErrorCode::Unknown,
215 }
216 }
217}
218
219pub type EFResult<T> = Result<T, EFError>;