cdk-http-client 0.18.0-rc.0

HTTP client abstraction for CDK
Documentation
//! HTTP response types

use serde::de::DeserializeOwned;

use crate::error::HttpError;

/// HTTP Response type - generic over the body type R and error type E
/// This is the primary return type for all HTTP operations
pub type Response<R, E = HttpError> = Result<R, E>;

/// Raw HTTP response with status code and body access
#[derive(Debug)]
pub struct RawResponse {
    status: u16,
    pub(crate) body: Vec<u8>,
}

impl RawResponse {
    /// Create a new RawResponse from status and body bytes
    pub fn new(status: u16, body: Vec<u8>) -> Self {
        Self { status, body }
    }

    /// Get the HTTP status code
    pub fn status(&self) -> u16 {
        self.status
    }

    /// Check if the response status is a success (2xx)
    pub fn is_success(&self) -> bool {
        (200..300).contains(&self.status)
    }

    /// Check if the response status is a client error (4xx)
    pub fn is_client_error(&self) -> bool {
        (400..500).contains(&self.status)
    }

    /// Check if the response status is a server error (5xx)
    pub fn is_server_error(&self) -> bool {
        (500..600).contains(&self.status)
    }

    /// Response body as lossy UTF-8 string
    pub fn body_lossy(&self) -> String {
        String::from_utf8_lossy(&self.body).into_owned()
    }

    /// Deserialize JSON from a successful response, or return `HttpError::Status`
    pub fn json_or_status_error<T: DeserializeOwned>(self) -> Response<T> {
        if !self.is_success() {
            return Err(HttpError::Status {
                status: self.status,
                message: self.body_lossy(),
            });
        }
        serde_json::from_slice(&self.body).map_err(HttpError::from)
    }

    /// Get the response body as text
    pub async fn text(self) -> Response<String> {
        String::from_utf8(self.body).map_err(|e| HttpError::Other(e.to_string()))
    }

    /// Get the response body as JSON
    pub async fn json<T: DeserializeOwned>(self) -> Response<T> {
        serde_json::from_slice(&self.body).map_err(HttpError::from)
    }

    /// Get the response body as bytes
    pub async fn bytes(self) -> Response<Vec<u8>> {
        Ok(self.body)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Note: RawResponse tests require a real response,
    // so they are in tests/integration.rs using mockito.

    #[test]
    fn test_response_type_is_result() {
        // Response<R, E> is just a type alias for Result<R, E>
        let success: Response<i32> = Ok(42);
        assert!(success.is_ok());
        assert!(matches!(success, Ok(42)));

        let error: Response<i32> = Err(HttpError::Timeout);
        assert!(error.is_err());
        assert!(matches!(error, Err(HttpError::Timeout)));
    }
}