1use axum::Json;
5use axum::http::StatusCode;
6use axum::response::{IntoResponse, Response};
7use prospero_core::CoreError;
8use serde::Serialize;
9
10pub enum ApiError {
12 Core(CoreError),
14 MethodNotAllowed(String),
17}
18
19impl ApiError {
20 #[must_use]
24 pub fn unsupported_on_backend() -> Self {
25 ApiError::MethodNotAllowed(
26 "not supported by the active fleet backend (k8s workspaces are \
27 CalibanTask/namespace-driven, not a prospero registry)"
28 .to_string(),
29 )
30 }
31}
32
33#[derive(Serialize)]
34struct ErrorBody {
35 error: String,
36 kind: &'static str,
37}
38
39impl From<CoreError> for ApiError {
40 fn from(e: CoreError) -> Self {
41 ApiError::Core(e)
42 }
43}
44
45impl IntoResponse for ApiError {
46 fn into_response(self) -> Response {
47 let (status, kind, error) = match self {
48 ApiError::MethodNotAllowed(msg) => {
49 (StatusCode::METHOD_NOT_ALLOWED, "method_not_allowed", msg)
50 }
51 ApiError::Core(e) => {
52 let (status, kind) = match &e {
53 CoreError::AgentNotFound(_) | CoreError::WorkspaceNotFound(_) => {
54 (StatusCode::NOT_FOUND, "not_found")
55 }
56 CoreError::InvalidState { .. } => (StatusCode::CONFLICT, "invalid_state"),
57 CoreError::Conflict(_) => (StatusCode::CONFLICT, "conflict"),
58 CoreError::ProviderMisconfigured(_) => {
59 (StatusCode::BAD_REQUEST, "provider_misconfigured")
60 }
61 CoreError::InvalidConfig(_) => (StatusCode::BAD_REQUEST, "invalid_config"),
62 CoreError::CalibandUnreachable { .. }
63 | CoreError::Discovery(_)
64 | CoreError::Fleet(_) => (StatusCode::SERVICE_UNAVAILABLE, "unreachable"),
65 CoreError::Protocol(_) => (StatusCode::BAD_GATEWAY, "protocol"),
66 CoreError::Store(_)
67 | CoreError::SeqConflict
68 | CoreError::Io(_)
69 | CoreError::Json(_) => (StatusCode::INTERNAL_SERVER_ERROR, "internal"),
70 };
71 (status, kind, e.to_string())
72 }
73 };
74 let body = ErrorBody { error, kind };
75 (status, Json(body)).into_response()
76 }
77}