Skip to main content

jules_api/response/
mod.rs

1//! Response module for deserialization and error mapping.
2
3use crate::http::HttpResponse;
4use jules_core::errors::{ApiError, SDKError};
5use jules_core::response::ClientResponse;
6use serde::de::DeserializeOwned;
7use serde::Deserialize;
8
9/// Helper struct for deserializing API errors.
10#[derive(Debug, Deserialize)]
11struct ErrorResponse {
12    error: Option<ErrorDetail>,
13}
14
15#[derive(Debug, Deserialize)]
16struct ErrorDetail {
17    message: String,
18}
19
20/// Maps a non-2xx `HttpResponse` into an `SDKError::Api`, extracting a message from a
21/// `{"error": {"message": ...}}` body when present, falling back to the raw body text.
22pub(crate) fn map_error_response(response: &HttpResponse) -> SDKError {
23    let message = match serde_json::from_slice::<ErrorResponse>(&response.body) {
24        Ok(err_resp) => err_resp.error.map_or_else(
25            || String::from_utf8_lossy(&response.body).into_owned(),
26            |e| e.message,
27        ),
28        Err(_) => String::from_utf8_lossy(&response.body).into_owned(),
29    };
30
31    let mut msg = message.trim().to_string();
32    if msg.is_empty() {
33        msg = format!("HTTP {}", response.status);
34    }
35
36    SDKError::Api(ApiError::with_status(msg, response.status))
37}
38
39/// Deserializes a successful (2xx) `HttpResponse` body as JSON into `R`, or maps a non-2xx
40/// response into an `SDKError::Api`.
41///
42/// # Errors
43///
44/// Returns `SDKError::Api` if the HTTP response status is not successful, or if the JSON
45/// response body fails to deserialize properly.
46pub(crate) fn deserialize_json<R: DeserializeOwned>(
47    response: &HttpResponse,
48) -> Result<R, SDKError> {
49    if response.status >= 200 && response.status < 300 {
50        serde_json::from_slice::<R>(&response.body).map_err(|e| {
51            SDKError::Api(ApiError::with_status(
52                format!("Failed to deserialize response: {e}"),
53                response.status,
54            ))
55        })
56    } else {
57        Err(map_error_response(response))
58    }
59}
60
61/// Deserializes an HTTP response into a `ClientResponse` or an `SDKError`.
62///
63/// # Errors
64///
65/// Returns `SDKError::Api` if the HTTP response status is not successful,
66/// or if the JSON response body fails to deserialize properly.
67pub fn deserialize_response(response: &HttpResponse) -> Result<ClientResponse, SDKError> {
68    deserialize_json::<ClientResponse>(response)
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use jules_core::message::Role;
75
76    #[test]
77    fn test_deserialize_success() {
78        let body = r#"{
79            "message": {
80                "role": "assistant",
81                "content": "Hello, world!"
82            }
83        }"#
84        .as_bytes()
85        .to_vec();
86
87        let http_resp = HttpResponse::new(200, vec![], body);
88        let result = deserialize_response(&http_resp).unwrap();
89
90        assert_eq!(*result.message.role(), Role::Assistant);
91        assert_eq!(result.message.content(), "Hello, world!");
92    }
93
94    #[test]
95    fn test_deserialize_api_error_json() {
96        let body = r#"{
97            "error": {
98                "message": "Invalid API key"
99            }
100        }"#
101        .as_bytes()
102        .to_vec();
103
104        let http_resp = HttpResponse::new(401, vec![], body);
105        let err = deserialize_response(&http_resp).unwrap_err();
106
107        match err {
108            SDKError::Api(e) => {
109                assert_eq!(e.status_code, Some(401));
110                assert_eq!(e.message, "Invalid API key");
111            }
112            _ => panic!("Expected SDKError::Api"),
113        }
114    }
115
116    #[test]
117    fn test_deserialize_api_error_raw() {
118        let body = "Internal Server Error".as_bytes().to_vec();
119
120        let http_resp = HttpResponse::new(500, vec![], body);
121        let err = deserialize_response(&http_resp).unwrap_err();
122
123        match err {
124            SDKError::Api(e) => {
125                assert_eq!(e.status_code, Some(500));
126                assert_eq!(e.message, "Internal Server Error");
127            }
128            _ => panic!("Expected SDKError::Api"),
129        }
130    }
131
132    #[test]
133    fn test_deserialize_api_error_empty_body() {
134        let body = vec![];
135
136        let http_resp = HttpResponse::new(500, vec![], body);
137        let err = deserialize_response(&http_resp).unwrap_err();
138
139        match err {
140            SDKError::Api(e) => {
141                assert_eq!(e.status_code, Some(500));
142                assert_eq!(e.message, "HTTP 500");
143            }
144            _ => panic!("Expected SDKError::Api"),
145        }
146    }
147}