Skip to main content

babelforce_manager_sdk/
error.rs

1use serde_json::Value;
2
3/// The error type returned by every facade call.
4#[derive(Debug, thiserror::Error)]
5pub enum ManagerError {
6    /// A non-2xx response from the manager API.
7    #[error("manager API error {status}: {message}")]
8    Api {
9        /// HTTP status code of the response.
10        status: u16,
11        /// Machine-readable error code from the response envelope, if present.
12        code: Option<String>,
13        /// Human-readable message extracted from the response envelope.
14        message: String,
15        /// Raw response body.
16        body: String,
17    },
18    /// A transport/network failure.
19    #[error("network error: {0}")]
20    Network(String),
21    /// A response that could not be decoded.
22    #[error("decode error: {0}")]
23    Decode(String),
24    /// An argument supplied to the SDK was invalid (e.g. a malformed id).
25    #[error("invalid argument: {0}")]
26    InvalidArgument(String),
27}
28
29impl ManagerError {
30    /// Build an [`ManagerError::Api`] from a non-2xx status + raw body, extracting `code`/`message`
31    /// from the JSON envelope when present (mirrors the TS/Go facades).
32    pub(crate) fn from_response(status: u16, body: String) -> Self {
33        let mut code = None;
34        let mut message = String::new();
35        if let Ok(v) = serde_json::from_str::<Value>(&body) {
36            code = v.get("code").and_then(Value::as_str).map(str::to_string);
37            for k in ["message", "error", "detail"] {
38                if let Some(m) = v.get(k).and_then(Value::as_str) {
39                    if !m.is_empty() {
40                        message = m.to_string();
41                        break;
42                    }
43                }
44            }
45        }
46        if message.is_empty() {
47            message = format!("HTTP {status}");
48        }
49        ManagerError::Api {
50            status,
51            code,
52            message,
53            body,
54        }
55    }
56}
57
58// Each generated module exposes its own (structurally identical) `Error<T>`. The facade needs one
59// `map_*_err` (gen Error -> ManagerError) per module, written explicitly (macros can't extend a
60// `:path` metavar with `::Variant` in pattern position). Add the matching fn when a new spec's
61// first domain is wrapped. Retry classification lives in the transport middleware
62// (`crate::retry::RetryMiddleware`), not here.
63
64pub(crate) fn map_manager_err<T>(e: crate::gen::manager::apis::Error<T>) -> ManagerError {
65    use crate::gen::manager::apis::Error;
66    match e {
67        Error::ResponseError(rc) => ManagerError::from_response(rc.status.as_u16(), rc.content),
68        Error::Reqwest(err) => ManagerError::Network(err.to_string()),
69        Error::ReqwestMiddleware(err) => ManagerError::Network(err.to_string()),
70        Error::Serde(err) => ManagerError::Decode(err.to_string()),
71        Error::Io(err) => ManagerError::Network(err.to_string()),
72    }
73}
74
75pub(crate) fn map_user_err<T>(e: crate::gen::user::apis::Error<T>) -> ManagerError {
76    use crate::gen::user::apis::Error;
77    match e {
78        Error::ResponseError(rc) => ManagerError::from_response(rc.status.as_u16(), rc.content),
79        Error::Reqwest(err) => ManagerError::Network(err.to_string()),
80        Error::ReqwestMiddleware(err) => ManagerError::Network(err.to_string()),
81        Error::Serde(err) => ManagerError::Decode(err.to_string()),
82        Error::Io(err) => ManagerError::Network(err.to_string()),
83    }
84}
85
86pub(crate) fn map_task_automation_err<T>(
87    e: crate::gen::task_automation::apis::Error<T>,
88) -> ManagerError {
89    use crate::gen::task_automation::apis::Error;
90    match e {
91        Error::ResponseError(rc) => ManagerError::from_response(rc.status.as_u16(), rc.content),
92        Error::Reqwest(err) => ManagerError::Network(err.to_string()),
93        Error::ReqwestMiddleware(err) => ManagerError::Network(err.to_string()),
94        Error::Serde(err) => ManagerError::Decode(err.to_string()),
95        Error::Io(err) => ManagerError::Network(err.to_string()),
96    }
97}
98
99pub(crate) fn map_task_schedule_err<T>(
100    e: crate::gen::task_schedule::apis::Error<T>,
101) -> ManagerError {
102    use crate::gen::task_schedule::apis::Error;
103    match e {
104        Error::ResponseError(rc) => ManagerError::from_response(rc.status.as_u16(), rc.content),
105        Error::Reqwest(err) => ManagerError::Network(err.to_string()),
106        Error::ReqwestMiddleware(err) => ManagerError::Network(err.to_string()),
107        Error::Serde(err) => ManagerError::Decode(err.to_string()),
108        Error::Io(err) => ManagerError::Network(err.to_string()),
109    }
110}
111
112pub(crate) fn map_auth_err<T>(e: crate::gen::auth::apis::Error<T>) -> ManagerError {
113    use crate::gen::auth::apis::Error;
114    match e {
115        Error::ResponseError(rc) => ManagerError::from_response(rc.status.as_u16(), rc.content),
116        Error::Reqwest(err) => ManagerError::Network(err.to_string()),
117        Error::ReqwestMiddleware(err) => ManagerError::Network(err.to_string()),
118        Error::Serde(err) => ManagerError::Decode(err.to_string()),
119        Error::Io(err) => ManagerError::Network(err.to_string()),
120    }
121}