systemprompt_models/api/errors/
response.rs1use std::error::Error;
10
11use axum::Json;
12use axum::http::{StatusCode, header};
13use axum::response::IntoResponse;
14
15use super::{ApiError, ErrorCode};
16
17impl ErrorCode {
18 #[must_use]
19 pub const fn status_code(&self) -> StatusCode {
20 match self {
21 Self::NotFound => StatusCode::NOT_FOUND,
22 Self::BadRequest => StatusCode::BAD_REQUEST,
23 Self::Unauthorized => StatusCode::UNAUTHORIZED,
24 Self::Forbidden => StatusCode::FORBIDDEN,
25 Self::ValidationError => StatusCode::UNPROCESSABLE_ENTITY,
26 Self::ConflictError => StatusCode::CONFLICT,
27 Self::RateLimited => StatusCode::TOO_MANY_REQUESTS,
28 Self::ServiceUnavailable => StatusCode::SERVICE_UNAVAILABLE,
29 Self::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
30 }
31 }
32}
33
34fn cause_chain(source: Option<&(dyn Error + Send + Sync + 'static)>) -> Option<String> {
35 let source = source?;
36 let mut chain = source.to_string();
37 let mut next = source.source();
38 while let Some(cause) = next {
39 chain.push_str(": ");
40 chain.push_str(&cause.to_string());
41 next = cause.source();
42 }
43 Some(chain)
44}
45
46impl IntoResponse for ApiError {
47 fn into_response(self) -> axum::response::Response {
48 let status = self.code.status_code();
49 let cause = cause_chain(self.source());
50
51 if status.is_server_error() {
52 tracing::error!(
53 error_code = ?self.code,
54 context = %self.message,
55 details = ?self.details,
56 error_key = ?self.error_key,
57 cause = ?cause,
58 path = ?self.path,
59 trace_id = ?self.trace_id,
60 "API server error response"
61 );
62 } else if status.is_client_error() {
63 tracing::warn!(
64 error_code = ?self.code,
65 message = %self.message,
66 error_key = ?self.error_key,
67 cause = ?cause,
68 path = ?self.path,
69 trace_id = ?self.trace_id,
70 "API client error response"
71 );
72 }
73
74 let mut response = (status, Json(self)).into_response();
75
76 if status == StatusCode::UNAUTHORIZED
77 && let Ok(header_value) =
78 "Bearer resource_metadata=\"/.well-known/oauth-protected-resource\"".parse()
79 {
80 response
81 .headers_mut()
82 .insert(header::WWW_AUTHENTICATE, header_value);
83 }
84
85 response
86 }
87}