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