base-sensible-wrapper 0.1.0

A simple api wrapper with sane defaults
Documentation
use anyhow::{Context, Result};
use reqwest::{
    Body, Method, Request,
    header::{HeaderName, HeaderValue},
};
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
use serde::Serialize;
use std::collections::HashMap;
use std::fmt::Display;

/// The public interface used to access any api. Should be implemented to provide specific
/// details about how to make a given api call.
pub trait ApiInterface: Send + Sync {
    /// Build a request based on the provided details. Provided, and should not be overridden
    /// except in very specific cases.
    fn build_request<
        E: Display,
        HK: Into<HeaderName>,
        HV: Into<HeaderValue>,
        P: Serialize,
        B: Serialize,
    >(
        &self,
        client: ClientWithMiddleware,
        endpoint: E,
        method: Method,
        headers: HashMap<HK, HV>,
        params: Option<P>,
        body: Option<B>,
    ) -> Result<Request> {
        let url = format!("{}{endpoint}", self.get_base_url());
        let request = Request::new(method, url.parse()?);

        let mut builder = RequestBuilder::from_parts(client, request);
        builder = builder.headers(
            headers
                .into_iter()
                .map(|(k, v)| (k.into(), v.into()))
                .collect(),
        );
        builder = self.get_auth_info(builder);

        if let Some(params) = params {
            builder = builder.query(&params);
        }

        if let Some(body) = body {
            let body = serde_json::to_string(&body)?;
            builder = builder.body(Body::from(body));
        }

        builder = self.additional_modifications(builder);

        builder
            .build()
            .context("Cannot build request inside ApiInterface")
    }

    /// The base url to use
    fn get_base_url(&self) -> String;
    /// How the interface authenticates
    fn get_auth_info(&self, request: RequestBuilder) -> RequestBuilder;
    /// If any additional modifications need to be made to the request, this member can
    /// be overridden by impls. Otherwise, it returns the request unmodified
    fn additional_modifications(&self, request: RequestBuilder) -> RequestBuilder {
        request
    }
}