systemprompt_models/
errors.rs

1pub use systemprompt_traits::RepositoryError;
2
3use crate::api::ApiError;
4
5#[derive(Debug, Clone, thiserror::Error)]
6pub enum CoreError {
7    #[error("Module config missing required field: {field}")]
8    MissingConfigField { field: String },
9
10    #[error("Invalid module version: {version}")]
11    InvalidVersion { version: String },
12
13    #[error("Module {name} configuration invalid: {reason}")]
14    InvalidModuleConfig { name: String, reason: String },
15
16    #[error("Module {name} not found")]
17    ModuleNotFound { name: String },
18
19    #[error("Invalid module field {field}: {reason}")]
20    InvalidField { field: String, reason: String },
21
22    #[error("Version comparison failed: {reason}")]
23    VersionComparisonFailed { reason: String },
24
25    #[error("Authentication failed: {reason}")]
26    AuthenticationFailed { reason: String },
27
28    #[error("Invalid or expired token")]
29    InvalidToken,
30
31    #[error("Token expired")]
32    TokenExpired,
33
34    #[error("Invalid token signature")]
35    InvalidSignature,
36
37    #[error("Missing required claim: {claim}")]
38    MissingClaim { claim: String },
39
40    #[error("Invalid authorization header")]
41    InvalidAuthHeader,
42
43    #[error("Invalid token format")]
44    InvalidTokenFormat,
45
46    #[error("Unauthorized")]
47    Unauthorized,
48
49    #[error("Forbidden: {reason}")]
50    Forbidden { reason: String },
51
52    #[error("User not found: {user_id}")]
53    UserNotFound { user_id: String },
54
55    #[error("Session not found: {session_id}")]
56    SessionNotFound { session_id: String },
57
58    #[error("Invalid session: {reason}")]
59    InvalidSession { reason: String },
60
61    #[error("Session expired")]
62    SessionExpired,
63
64    #[error("Cookie not found")]
65    CookieNotFound,
66
67    #[error("Invalid cookie format")]
68    InvalidCookieFormat,
69
70    #[error("Database error: {reason}")]
71    DatabaseError { reason: String },
72
73    #[error("Table not found: {table}")]
74    TableNotFound { table: String },
75
76    #[error("Schema error: {message}")]
77    SchemaError { message: String },
78
79    #[error("File not found: {path}")]
80    FileNotFound { path: String },
81
82    #[error("IO error: {reason}")]
83    IoError { reason: String },
84
85    #[error("Internal server error: {reason}")]
86    InternalError { reason: String },
87}
88
89impl CoreError {
90    pub fn reason(&self) -> String {
91        self.to_string()
92    }
93
94    pub const fn status_code(&self) -> u16 {
95        match self {
96            Self::AuthenticationFailed { .. }
97            | Self::InvalidToken
98            | Self::TokenExpired
99            | Self::InvalidSignature
100            | Self::Unauthorized
101            | Self::SessionExpired => 401,
102            Self::MissingClaim { .. }
103            | Self::InvalidAuthHeader
104            | Self::InvalidTokenFormat
105            | Self::InvalidSession { .. }
106            | Self::CookieNotFound
107            | Self::InvalidCookieFormat
108            | Self::MissingConfigField { .. }
109            | Self::InvalidVersion { .. }
110            | Self::InvalidModuleConfig { .. }
111            | Self::InvalidField { .. }
112            | Self::VersionComparisonFailed { .. } => 400,
113            Self::Forbidden { .. } => 403,
114            Self::UserNotFound { .. }
115            | Self::SessionNotFound { .. }
116            | Self::ModuleNotFound { .. }
117            | Self::TableNotFound { .. }
118            | Self::FileNotFound { .. } => 404,
119            Self::DatabaseError { .. }
120            | Self::SchemaError { .. }
121            | Self::IoError { .. }
122            | Self::InternalError { .. } => 500,
123        }
124    }
125
126    pub const fn is_client_error(&self) -> bool {
127        self.status_code() < 500
128    }
129
130    pub const fn is_server_error(&self) -> bool {
131        self.status_code() >= 500
132    }
133
134    pub const fn is_auth_error(&self) -> bool {
135        matches!(
136            self,
137            Self::AuthenticationFailed { .. }
138                | Self::InvalidToken
139                | Self::TokenExpired
140                | Self::InvalidSignature
141                | Self::InvalidAuthHeader
142                | Self::InvalidTokenFormat
143                | Self::Unauthorized
144                | Self::SessionExpired
145        )
146    }
147
148    pub const fn is_permission_error(&self) -> bool {
149        matches!(self, Self::Forbidden { .. })
150    }
151
152    pub const fn is_not_found(&self) -> bool {
153        matches!(
154            self,
155            Self::UserNotFound { .. }
156                | Self::SessionNotFound { .. }
157                | Self::ModuleNotFound { .. }
158                | Self::TableNotFound { .. }
159                | Self::FileNotFound { .. }
160        )
161    }
162}
163
164impl From<String> for CoreError {
165    fn from(reason: String) -> Self {
166        Self::InternalError { reason }
167    }
168}
169
170impl From<&str> for CoreError {
171    fn from(reason: &str) -> Self {
172        Self::InternalError {
173            reason: reason.to_string(),
174        }
175    }
176}
177
178impl From<anyhow::Error> for CoreError {
179    fn from(err: anyhow::Error) -> Self {
180        Self::InternalError {
181            reason: err.to_string(),
182        }
183    }
184}
185
186impl From<std::io::Error> for CoreError {
187    fn from(err: std::io::Error) -> Self {
188        Self::IoError {
189            reason: err.to_string(),
190        }
191    }
192}
193
194#[derive(Debug, thiserror::Error)]
195pub enum ServiceError {
196    #[error("repository error: {0}")]
197    Repository(#[from] RepositoryError),
198
199    #[error("validation error: {0}")]
200    Validation(String),
201
202    #[error("business logic error: {0}")]
203    BusinessLogic(String),
204
205    #[error("external service error: {0}")]
206    External(String),
207
208    #[error("not found: {0}")]
209    NotFound(String),
210
211    #[error("conflict: {0}")]
212    Conflict(String),
213
214    #[error("unauthorized: {0}")]
215    Unauthorized(String),
216
217    #[error("forbidden: {0}")]
218    Forbidden(String),
219}
220
221impl From<ServiceError> for ApiError {
222    fn from(err: ServiceError) -> Self {
223        match err {
224            ServiceError::Repository(e) => e.into(),
225            ServiceError::Validation(msg) | ServiceError::BusinessLogic(msg) => {
226                Self::bad_request(msg)
227            },
228            ServiceError::NotFound(msg) => Self::not_found(msg),
229            ServiceError::External(msg) => {
230                Self::internal_error(format!("External service error: {msg}"))
231            },
232            ServiceError::Conflict(msg) => Self::conflict(msg),
233            ServiceError::Unauthorized(msg) => Self::unauthorized(msg),
234            ServiceError::Forbidden(msg) => Self::forbidden(msg),
235        }
236    }
237}
238
239impl From<RepositoryError> for ApiError {
240    fn from(err: RepositoryError) -> Self {
241        match err {
242            RepositoryError::NotFound(msg) => Self::not_found(msg),
243            RepositoryError::InvalidData(msg) | RepositoryError::ConstraintViolation(msg) => {
244                Self::bad_request(msg)
245            },
246            RepositoryError::Database(e) => Self::internal_error(format!("Database error: {e}")),
247            RepositoryError::Serialization(e) => {
248                Self::internal_error(format!("Serialization error: {e}"))
249            },
250            RepositoryError::Other(e) => Self::internal_error(format!("Error: {e}")),
251            _ => Self::internal_error(format!("Repository error: {err}")),
252        }
253    }
254}