systemprompt_models/api/errors/
internal.rs1use super::{ApiError, ErrorCode};
9
10#[derive(Debug, thiserror::Error)]
11pub enum InternalApiError {
12 #[error("Resource not found: {resource_type} with ID '{id}'")]
13 NotFound { resource_type: String, id: String },
14
15 #[error("Bad request: {message}")]
16 BadRequest { message: String },
17
18 #[error("Unauthorized access: {reason}")]
19 Unauthorized { reason: String },
20
21 #[error("Access forbidden: {resource} - {reason}")]
22 Forbidden { resource: String, reason: String },
23
24 #[error("Validation failed for field '{field}': {reason}")]
25 ValidationError { field: String, reason: String },
26
27 #[error("Conflict: {resource} already exists")]
28 ConflictError { resource: String },
29
30 #[error("Rate limit exceeded for {resource}")]
31 RateLimited { resource: String },
32
33 #[error("Service temporarily unavailable: {service}")]
34 ServiceUnavailable { service: String },
35
36 #[error("Database operation failed: {message}")]
37 DatabaseError { message: String },
38
39 #[error("JSON serialization failed")]
40 JsonError(#[from] serde_json::Error),
41
42 #[error("Authentication token error: {message}")]
43 AuthenticationError { message: String },
44
45 #[error("Internal server error: {message}")]
46 InternalError { message: String },
47}
48
49impl InternalApiError {
50 pub fn not_found(resource_type: impl Into<String>, id: impl Into<String>) -> Self {
51 Self::NotFound {
52 resource_type: resource_type.into(),
53 id: id.into(),
54 }
55 }
56
57 pub fn bad_request(message: impl Into<String>) -> Self {
58 Self::BadRequest {
59 message: message.into(),
60 }
61 }
62
63 pub fn unauthorized(reason: impl Into<String>) -> Self {
64 Self::Unauthorized {
65 reason: reason.into(),
66 }
67 }
68
69 pub fn forbidden(resource: impl Into<String>, reason: impl Into<String>) -> Self {
70 Self::Forbidden {
71 resource: resource.into(),
72 reason: reason.into(),
73 }
74 }
75
76 pub fn validation_error(field: impl Into<String>, reason: impl Into<String>) -> Self {
77 Self::ValidationError {
78 field: field.into(),
79 reason: reason.into(),
80 }
81 }
82
83 pub fn conflict(resource: impl Into<String>) -> Self {
84 Self::ConflictError {
85 resource: resource.into(),
86 }
87 }
88
89 pub fn rate_limited(resource: impl Into<String>) -> Self {
90 Self::RateLimited {
91 resource: resource.into(),
92 }
93 }
94
95 pub fn service_unavailable(service: impl Into<String>) -> Self {
96 Self::ServiceUnavailable {
97 service: service.into(),
98 }
99 }
100
101 pub fn internal_error(message: impl Into<String>) -> Self {
102 Self::InternalError {
103 message: message.into(),
104 }
105 }
106
107 pub fn database_error(message: impl Into<String>) -> Self {
108 Self::DatabaseError {
109 message: message.into(),
110 }
111 }
112
113 pub fn authentication_error(message: impl Into<String>) -> Self {
114 Self::AuthenticationError {
115 message: message.into(),
116 }
117 }
118
119 #[must_use]
120 pub const fn error_code(&self) -> ErrorCode {
121 match self {
122 Self::NotFound { .. } => ErrorCode::NotFound,
123 Self::BadRequest { .. } => ErrorCode::BadRequest,
124 Self::Unauthorized { .. } => ErrorCode::Unauthorized,
125 Self::Forbidden { .. } => ErrorCode::Forbidden,
126 Self::ValidationError { .. } => ErrorCode::ValidationError,
127 Self::ConflictError { .. } => ErrorCode::ConflictError,
128 Self::RateLimited { .. } => ErrorCode::RateLimited,
129 Self::ServiceUnavailable { .. } => ErrorCode::ServiceUnavailable,
130 Self::DatabaseError { .. }
131 | Self::JsonError(_)
132 | Self::AuthenticationError { .. }
133 | Self::InternalError { .. } => ErrorCode::InternalError,
134 }
135 }
136}
137
138impl From<InternalApiError> for ApiError {
139 fn from(error: InternalApiError) -> Self {
140 let code = error.error_code();
141 let message = error.to_string();
142 let details = match &error {
143 InternalApiError::NotFound { resource_type, id } => Some(format!(
144 "The requested {resource_type} with ID '{id}' does not exist"
145 )),
146 InternalApiError::ValidationError { field, reason } => {
147 Some(format!("Field '{field}': {reason}"))
148 },
149 InternalApiError::Forbidden { resource, reason } => {
150 Some(format!("Access to {resource} denied: {reason}"))
151 },
152 InternalApiError::DatabaseError { message } => {
153 Some(format!("Database error: {message}"))
154 },
155 InternalApiError::JsonError(e) => Some(format!("JSON processing error: {e}")),
156 InternalApiError::AuthenticationError { message } => {
157 Some(format!("Authentication error: {message}"))
158 },
159 InternalApiError::BadRequest { .. }
160 | InternalApiError::Unauthorized { .. }
161 | InternalApiError::ConflictError { .. }
162 | InternalApiError::RateLimited { .. }
163 | InternalApiError::ServiceUnavailable { .. }
164 | InternalApiError::InternalError { .. } => None,
165 };
166
167 let api_error = Self::new(code, message);
168 if let Some(d) = details {
169 api_error.with_details(d)
170 } else {
171 api_error
172 }
173 }
174}
175
176#[cfg(feature = "web")]
177impl axum::response::IntoResponse for InternalApiError {
178 fn into_response(self) -> axum::response::Response {
179 let error: ApiError = self.into();
180 error.into_response()
181 }
182}