arclutevests 0.1.0

Retrieve secret data from a vault (Hashicorp) instance
Documentation
// Copyright (c) 2022 arclutevests developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! reloaderapi utils

use crate::error::Error as AlvError;
use anyhow::Result;
use futures::Future;
use reqwest::{header::HeaderMap, Client, Error, IntoUrl, Method, Response};
use serde::{de::DeserializeOwned, Serialize};

pub(crate) const EMPTY_BODY: Option<String> = None;

pub(crate) fn req<T, U>(
    client: &Client,
    method: Method,
    url: U,
    headers: Option<HeaderMap>,
    json: Option<T>,
) -> impl Future<Output = std::result::Result<Response, Error>>
where
    T: Serialize + Send + Sync,
    U: IntoUrl,
{
    let mut rb = client.request(method, url);

    if let Some(headers) = headers {
        rb = rb.headers(headers);
    }

    if let Some(json) = json {
        rb = rb.json(&json);
    }

    rb.send()
}

pub(crate) async fn as_json<T>(res: Result<Response, Error>) -> Result<T>
where
    T: DeserializeOwned,
{
    res.map(to_json)?.await
}

#[allow(clippy::unused_async)]
pub(crate) async fn as_empty(res: Result<Response, Error>) -> Result<()> {
    res.map(to_empty)?
}

async fn to_json<T>(res: Response) -> Result<T>
where
    T: DeserializeOwned,
{
    res.error_for_status()
        .map(|res| async move { handle_text(res).await })?
        .await
}

async fn handle_text<T>(res: Response) -> Result<T>
where
    T: DeserializeOwned,
{
    match res.text().await {
        Ok(text) => {
            let invalid_body = |e: serde_json::Error| -> anyhow::Error { invalid_body(&e, &text) };
            serde_json::from_str::<T>(&text).map_err(invalid_body)
        }
        Err(e) => Err(e.into()),
    }
}

fn invalid_body(e: &serde_json::Error, text: &str) -> anyhow::Error {
    AlvError::invalid_body(format!("{text}: {e}")).into()
}

fn to_empty(res: Response) -> Result<()> {
    res.error_for_status().map(|_| ()).map_err(Error::into)
}