huskarl-core 0.6.2

Base library for huskarl (OAuth2 client) ecosystem.
Documentation
use bytes::Bytes;
use http::{HeaderMap, StatusCode};
use serde::de::DeserializeOwned;
use snafu::prelude::*;

use crate::http::{HttpClient, HttpResponse};

/// Errors returned by [`get`].
#[derive(Debug, Snafu)]
pub enum GetError<HttpReqErr: crate::Error + 'static, HttpRespErr: crate::Error + 'static> {
    /// The HTTP client failed to send the request.
    Request {
        /// The underlying client error.
        source: HttpReqErr,
    },
    /// The HTTP response body could not be read.
    Response {
        /// The underlying response error.
        source: HttpRespErr,
    },
    /// The response body could not be deserialized as JSON.
    Deserialize {
        /// The JSON deserialization error.
        source: serde_json::Error,
    },
    /// The server returned a non-success status code.
    #[snafu(display("Bad status: {}", status))]
    BadStatus {
        /// The HTTP status code.
        status: StatusCode,
        /// The response headers.
        headers: HeaderMap,
        /// The raw response body.
        body: Vec<u8>,
    },
}

impl<HttpReqErr: crate::Error, HttpRespErr: crate::Error> crate::Error
    for GetError<HttpReqErr, HttpRespErr>
{
    fn is_retryable(&self) -> bool {
        match self {
            GetError::Request { source } => source.is_retryable(),
            GetError::Response { source } => source.is_retryable(),
            GetError::Deserialize { .. } | GetError::BadStatus { .. } => false,
        }
    }
}

/// Perform an HTTP GET request and deserialize the JSON response body.
///
/// # Errors
///
/// Returns [`GetError::Request`] if the HTTP client fails to send the request,
/// [`GetError::Response`] if the response body cannot be read,
/// [`GetError::BadStatus`] if the server returns a non-success status code, or
/// [`GetError::Deserialize`] if the response body is not valid JSON.
pub async fn get<C: HttpClient, T: DeserializeOwned>(
    http_client: &C,
    uri: http::Uri,
    headers: HeaderMap,
) -> Result<T, GetError<C::Error, C::ResponseError>> {
    let (mut parts, ()) = http::Request::new(()).into_parts();
    parts.headers = headers;
    parts.uri = uri;
    let request = http::Request::from_parts(parts, Bytes::new());

    let response = http_client.execute(request).await.context(RequestSnafu)?;
    let status = response.status();
    let response_headers = response.headers();
    let body = response.body().await.context(ResponseSnafu)?;

    if status.is_success() {
        Ok(serde_json::from_slice(&body).context(DeserializeSnafu)?)
    } else {
        BadStatusSnafu {
            status,
            headers: response_headers,
            body,
        }
        .fail()
    }
}