1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum CoreError {
8 #[error("IO error: {0}")]
9 Io(#[from] std::io::Error),
10
11 #[error("YAML parsing error: {0}")]
12 Yaml(#[from] serde_yaml::Error),
13
14 #[error("JSON parsing error: {0}")]
15 Json(#[from] serde_json::Error),
16
17 #[error("Validation error: {message}")]
18 Validation { message: String },
19
20 #[error("Configuration error: {message}")]
21 Configuration { message: String },
22
23 #[error("Service not found: {service_type}")]
24 ServiceNotFound { service_type: String },
25
26 #[error("Invalid service scope: {scope}")]
27 InvalidServiceScope { scope: String },
28
29 #[error("Lock error on resource: {resource}")]
30 LockError { resource: String },
31
32 #[error("Lifecycle error in component '{component}' during '{operation}': {source}")]
33 LifecycleError {
34 component: String,
35 operation: String,
36 source: Box<dyn std::error::Error + Send + Sync>,
37 },
38
39 #[error("System error: {message}")]
40 SystemError {
41 message: String,
42 source: Option<Box<dyn std::error::Error + Send + Sync>>,
43 },
44
45 #[error("Module error: {message}")]
46 Module { message: String },
47
48 #[error("Provider error: {message}")]
49 Provider { message: String },
50
51 #[error("Codegen error: {message}")]
52 Codegen { message: String },
53
54 #[error("Template error: {message}")]
55 Template { message: String },
56
57 #[error("Database error: {message}")]
58 Database { message: String },
59
60 #[error("Circular dependency detected: {path} (cycle at: {cycle_service})")]
61 CircularDependency { path: String, cycle_service: String },
62
63 #[error("Invalid service descriptor: {message}")]
64 InvalidServiceDescriptor { message: String },
65
66 #[error("Dependency resolution failed for '{service_type}': {message}")]
67 DependencyResolutionFailed {
68 service_type: String,
69 message: String,
70 },
71
72 #[error("API error: {code} - {message}")]
73 Api {
74 code: String,
75 message: String,
76 hint: Option<String>,
77 },
78
79 #[error("Service initialization failed for '{service_type}': {source}")]
80 ServiceInitializationFailed {
81 service_type: String,
82 source: Box<dyn std::error::Error + Send + Sync>,
83 },
84}
85
86impl CoreError {
87 pub fn validation(message: impl Into<String>) -> Self {
89 Self::Validation {
90 message: message.into(),
91 }
92 }
93
94 pub fn configuration(message: impl Into<String>) -> Self {
96 Self::Configuration {
97 message: message.into(),
98 }
99 }
100
101 pub fn service_not_found(service_type: impl Into<String>) -> Self {
103 Self::ServiceNotFound {
104 service_type: service_type.into(),
105 }
106 }
107
108 pub fn system_error(message: impl Into<String>) -> Self {
110 Self::SystemError {
111 message: message.into(),
112 source: None,
113 }
114 }
115
116 pub fn system_error_with_source(
118 message: impl Into<String>,
119 source: Box<dyn std::error::Error + Send + Sync>,
120 ) -> Self {
121 Self::SystemError {
122 message: message.into(),
123 source: Some(source),
124 }
125 }
126
127 pub fn api_error(code: impl Into<String>, message: impl Into<String>) -> Self {
129 Self::Api {
130 code: code.into(),
131 message: message.into(),
132 hint: None,
133 }
134 }
135
136 pub fn with_hint(mut self, hint: impl Into<String>) -> Self {
138 if let Self::Api {
139 hint: ref mut h, ..
140 } = self
141 {
142 *h = Some(hint.into());
143 }
144 self
145 }
146
147 pub fn is_validation(&self) -> bool {
149 matches!(self, Self::Validation { .. })
150 }
151
152 pub fn is_configuration(&self) -> bool {
154 matches!(self, Self::Configuration { .. })
155 }
156
157 pub fn is_service(&self) -> bool {
159 matches!(self, Self::ServiceNotFound { .. })
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct ErrorDefinition {
166 pub code: String,
167 pub http: u16,
168 pub message: String,
169 pub hint: String,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ApiErrorResponse {
175 pub error: ApiError,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ApiError {
181 pub code: String,
182 pub message: String,
183 pub hint: Option<String>,
184}
185
186impl ApiError {
187 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
189 Self {
190 code: code.into(),
191 message: message.into(),
192 hint: None,
193 }
194 }
195
196 pub fn with_hint(mut self, hint: impl Into<String>) -> Self {
198 self.hint = Some(hint.into());
199 self
200 }
201}
202
203impl From<CoreError> for ApiError {
204 fn from(error: CoreError) -> Self {
205 match error {
206 CoreError::Api {
207 code,
208 message,
209 hint,
210 } => Self {
211 code,
212 message,
213 hint,
214 },
215 CoreError::Validation { message } => Self::new("VALIDATION_ERROR", message),
216 CoreError::Configuration { message } => Self::new("CONFIG_ERROR", message),
217 CoreError::ServiceNotFound { service_type } => Self::new(
218 "SERVICE_NOT_FOUND",
219 format!("Service not found: {}", service_type),
220 ),
221 _ => Self::new("INTERNAL_ERROR", error.to_string()),
222 }
223 }
224}
225
226impl From<ApiError> for ApiErrorResponse {
227 fn from(error: ApiError) -> Self {
228 Self { error }
229 }
230}
231
232pub type ErrorCatalog = HashMap<String, ErrorDefinition>;
234
235pub type ElifError = CoreError;