1use thiserror::Error;
2
3#[derive(Error, Debug)]
8pub enum ForgeError {
9 #[error("状态错误: {message}")]
11 State {
12 message: String,
13 #[source]
14 source: Option<Box<dyn std::error::Error + Send + Sync>>,
15 },
16
17 #[error("事件错误: {message}")]
19 Event {
20 message: String,
21 #[source]
22 source: Option<Box<dyn std::error::Error + Send + Sync>>,
23 },
24
25 #[error("中间件错误: {message}")]
27 Middleware {
28 message: String,
29 middleware_name: Option<String>,
30 #[source]
31 source: Option<Box<dyn std::error::Error + Send + Sync>>,
32 },
33
34 #[error("扩展错误: {message}")]
36 Extension {
37 message: String,
38 extension_name: Option<String>,
39 #[source]
40 source: Option<Box<dyn std::error::Error + Send + Sync>>,
41 },
42
43 #[error("事务错误: {message}")]
45 Transaction {
46 message: String,
47 transaction_id: Option<u64>,
48 #[source]
49 source: Option<Box<dyn std::error::Error + Send + Sync>>,
50 },
51
52 #[error("历史记录错误: {message}")]
54 History {
55 message: String,
56 #[source]
57 source: Option<Box<dyn std::error::Error + Send + Sync>>,
58 },
59
60 #[error("配置错误: {message}")]
62 Config {
63 message: String,
64 config_key: Option<String>,
65 #[source]
66 source: Option<Box<dyn std::error::Error + Send + Sync>>,
67 },
68
69 #[error("存储错误: {message}")]
71 Storage {
72 message: String,
73 #[source]
74 source: Option<Box<dyn std::error::Error + Send + Sync>>,
75 },
76
77 #[error("缓存错误: {message}")]
79 Cache {
80 message: String,
81 #[source]
82 source: Option<Box<dyn std::error::Error + Send + Sync>>,
83 },
84
85 #[error("引擎错误: {message}")]
87 Engine {
88 message: String,
89 #[source]
90 source: Option<Box<dyn std::error::Error + Send + Sync>>,
91 },
92
93 #[error("操作超时: {operation} (超时时间: {timeout_ms}ms)")]
95 Timeout { operation: String, timeout_ms: u64 },
96
97 #[error("资源不足: {resource_type}")]
99 ResourceExhausted {
100 resource_type: String,
101 current_usage: Option<usize>,
102 limit: Option<usize>,
103 },
104
105 #[error("并发错误: {message}")]
107 Concurrency {
108 message: String,
109 #[source]
110 source: Option<Box<dyn std::error::Error + Send + Sync>>,
111 },
112
113 #[error("验证失败: {message}")]
115 Validation { message: String, field: Option<String> },
116
117 #[error("外部依赖错误: {dependency}")]
119 ExternalDependency {
120 dependency: String,
121 #[source]
122 source: Box<dyn std::error::Error + Send + Sync>,
123 },
124
125 #[error("内部错误: {message}")]
127 Internal { message: String, location: Option<String> },
128
129 #[error("其他错误: {0}")]
131 Other(#[from] anyhow::Error),
132}
133
134pub type ForgeResult<T> = Result<T, ForgeError>;
136
137impl ForgeError {
138 pub fn error_code(&self) -> &'static str {
140 match self {
141 ForgeError::State { .. } => "STATE_ERROR",
142 ForgeError::Event { .. } => "EVENT_ERROR",
143 ForgeError::Middleware { .. } => "MIDDLEWARE_ERROR",
144 ForgeError::Extension { .. } => "EXTENSION_ERROR",
145 ForgeError::Transaction { .. } => "TRANSACTION_ERROR",
146 ForgeError::History { .. } => "HISTORY_ERROR",
147 ForgeError::Config { .. } => "CONFIG_ERROR",
148 ForgeError::Storage { .. } => "STORAGE_ERROR",
149 ForgeError::Cache { .. } => "CACHE_ERROR",
150 ForgeError::Engine { .. } => "ENGINE_ERROR",
151 ForgeError::Timeout { .. } => "TIMEOUT_ERROR",
152 ForgeError::ResourceExhausted { .. } => "RESOURCE_EXHAUSTED",
153 ForgeError::Concurrency { .. } => "CONCURRENCY_ERROR",
154 ForgeError::Validation { .. } => "VALIDATION_ERROR",
155 ForgeError::ExternalDependency { .. } => {
156 "EXTERNAL_DEPENDENCY_ERROR"
157 },
158 ForgeError::Internal { .. } => "INTERNAL_ERROR",
159 ForgeError::Other(_) => "OTHER_ERROR",
160 }
161 }
162
163 pub fn is_retryable(&self) -> bool {
165 matches!(
166 self,
167 ForgeError::Timeout { .. }
168 | ForgeError::ResourceExhausted { .. }
169 | ForgeError::Concurrency { .. }
170 | ForgeError::ExternalDependency { .. }
171 )
172 }
173
174 pub fn is_temporary(&self) -> bool {
176 matches!(
177 self,
178 ForgeError::Timeout { .. }
179 | ForgeError::ResourceExhausted { .. }
180 | ForgeError::Concurrency { .. }
181 )
182 }
183}
184
185pub mod error_utils {
190 use super::*;
191
192 pub fn map_error<T, E: std::error::Error + Send + Sync + 'static>(
194 result: Result<T, E>,
195 context: &str,
196 ) -> ForgeResult<T> {
197 result.map_err(|e| {
198 ForgeError::Other(anyhow::anyhow!("{}: {}", context, e))
199 })
200 }
201
202 pub fn state_error(msg: impl Into<String>) -> ForgeError {
204 ForgeError::State { message: msg.into(), source: None }
205 }
206
207 pub fn state_error_with_source(
209 msg: impl Into<String>,
210 source: impl std::error::Error + Send + Sync + 'static,
211 ) -> ForgeError {
212 ForgeError::State {
213 message: msg.into(),
214 source: Some(Box::new(source)),
215 }
216 }
217
218 pub fn event_error(msg: impl Into<String>) -> ForgeError {
220 ForgeError::Event { message: msg.into(), source: None }
221 }
222
223 pub fn event_error_with_source(
225 msg: impl Into<String>,
226 source: impl std::error::Error + Send + Sync + 'static,
227 ) -> ForgeError {
228 ForgeError::Event {
229 message: msg.into(),
230 source: Some(Box::new(source)),
231 }
232 }
233
234 pub fn middleware_error(msg: impl Into<String>) -> ForgeError {
236 ForgeError::Middleware {
237 message: msg.into(),
238 middleware_name: None,
239 source: None,
240 }
241 }
242
243 pub fn middleware_error_with_name(
245 msg: impl Into<String>,
246 middleware_name: impl Into<String>,
247 ) -> ForgeError {
248 ForgeError::Middleware {
249 message: msg.into(),
250 middleware_name: Some(middleware_name.into()),
251 source: None,
252 }
253 }
254
255 pub fn middleware_error_with_source(
257 msg: impl Into<String>,
258 middleware_name: Option<String>,
259 source: impl std::error::Error + Send + Sync + 'static,
260 ) -> ForgeError {
261 ForgeError::Middleware {
262 message: msg.into(),
263 middleware_name,
264 source: Some(Box::new(source)),
265 }
266 }
267
268 pub fn extension_error(msg: impl Into<String>) -> ForgeError {
270 ForgeError::Extension {
271 message: msg.into(),
272 extension_name: None,
273 source: None,
274 }
275 }
276
277 pub fn extension_error_with_name(
279 msg: impl Into<String>,
280 extension_name: impl Into<String>,
281 ) -> ForgeError {
282 ForgeError::Extension {
283 message: msg.into(),
284 extension_name: Some(extension_name.into()),
285 source: None,
286 }
287 }
288
289 pub fn plugin_error(msg: impl Into<String>) -> ForgeError {
291 extension_error(msg)
292 }
293
294 pub fn transaction_error(msg: impl Into<String>) -> ForgeError {
296 ForgeError::Transaction {
297 message: msg.into(),
298 transaction_id: None,
299 source: None,
300 }
301 }
302
303 pub fn transaction_error_with_id(
305 msg: impl Into<String>,
306 transaction_id: u64,
307 ) -> ForgeError {
308 ForgeError::Transaction {
309 message: msg.into(),
310 transaction_id: Some(transaction_id),
311 source: None,
312 }
313 }
314
315 pub fn history_error(msg: impl Into<String>) -> ForgeError {
317 ForgeError::History { message: msg.into(), source: None }
318 }
319
320 pub fn config_error(msg: impl Into<String>) -> ForgeError {
322 ForgeError::Config {
323 message: msg.into(),
324 config_key: None,
325 source: None,
326 }
327 }
328
329 pub fn config_error_with_key(
331 msg: impl Into<String>,
332 config_key: impl Into<String>,
333 ) -> ForgeError {
334 ForgeError::Config {
335 message: msg.into(),
336 config_key: Some(config_key.into()),
337 source: None,
338 }
339 }
340
341 pub fn storage_error(msg: impl Into<String>) -> ForgeError {
343 ForgeError::Storage { message: msg.into(), source: None }
344 }
345
346 pub fn cache_error(msg: impl Into<String>) -> ForgeError {
348 ForgeError::Cache { message: msg.into(), source: None }
349 }
350
351 pub fn engine_error(msg: impl Into<String>) -> ForgeError {
353 ForgeError::Engine { message: msg.into(), source: None }
354 }
355
356 pub fn timeout_error(operation: impl Into<String>) -> ForgeError {
358 ForgeError::Timeout {
359 operation: operation.into(),
360 timeout_ms: 0, }
362 }
363
364 pub fn timeout_error_with_duration(
366 operation: impl Into<String>,
367 timeout_ms: u64,
368 ) -> ForgeError {
369 ForgeError::Timeout { operation: operation.into(), timeout_ms }
370 }
371
372 pub fn runtime_error(msg: impl Into<String>) -> ForgeError {
374 ForgeError::Engine { message: msg.into(), source: None }
375 }
376
377 pub fn resource_exhausted_error(
379 resource_type: impl Into<String>
380 ) -> ForgeError {
381 ForgeError::ResourceExhausted {
382 resource_type: resource_type.into(),
383 current_usage: None,
384 limit: None,
385 }
386 }
387
388 pub fn resource_exhausted_error_with_usage(
390 resource_type: impl Into<String>,
391 current_usage: usize,
392 limit: usize,
393 ) -> ForgeError {
394 ForgeError::ResourceExhausted {
395 resource_type: resource_type.into(),
396 current_usage: Some(current_usage),
397 limit: Some(limit),
398 }
399 }
400
401 pub fn concurrency_error(msg: impl Into<String>) -> ForgeError {
403 ForgeError::Concurrency { message: msg.into(), source: None }
404 }
405
406 pub fn validation_error(msg: impl Into<String>) -> ForgeError {
408 ForgeError::Validation { message: msg.into(), field: None }
409 }
410
411 pub fn validation_error_with_field(
413 msg: impl Into<String>,
414 field: impl Into<String>,
415 ) -> ForgeError {
416 ForgeError::Validation {
417 message: msg.into(),
418 field: Some(field.into()),
419 }
420 }
421
422 pub fn external_dependency_error(
424 dependency: impl Into<String>,
425 source: impl std::error::Error + Send + Sync + 'static,
426 ) -> ForgeError {
427 ForgeError::ExternalDependency {
428 dependency: dependency.into(),
429 source: Box::new(source),
430 }
431 }
432
433 pub fn internal_error(msg: impl Into<String>) -> ForgeError {
435 ForgeError::Internal { message: msg.into(), location: None }
436 }
437
438 pub fn internal_error_with_location(
440 msg: impl Into<String>,
441 location: impl Into<String>,
442 ) -> ForgeError {
443 ForgeError::Internal {
444 message: msg.into(),
445 location: Some(location.into()),
446 }
447 }
448}
449
450impl From<crate::config::ConfigValidationError> for ForgeError {
452 fn from(err: crate::config::ConfigValidationError) -> Self {
453 ForgeError::Validation {
454 field: Some("config".to_string()),
455 message: err.to_string(),
456 }
457 }
458}