requestr-core 0.1.0

Core lib for tools to store, share and run http request templates easily
Documentation
use log::{debug, info};
use serde::{Deserialize, Serialize};
use serde_yaml;
use std::{
    collections::{HashMap, HashSet},
    fs, io,
    path::PathBuf,
};

use thiserror::Error;

mod template;

pub use template::Template;

#[derive(Error, Debug)]
pub enum RequestrError {
    #[error("Following parameter are missing from the input: {0:#?}")]
    MissingParameter(Vec<String>),
    #[error("Unable to load Template from {0}")]
    OpeningTemplateFailed(String, io::Error),
    #[error("Parsing template failed")]
    TemplateParsingFailed(#[from] serde_yaml::Error),
    #[error("Wrong request config: {0}")]
    BrokenRequestConfig(String),
    #[error("Request failed")]
    UnknownRequestError(#[from] reqwest::Error),
    #[error("Unknown Requestr Error")]
    Unknown,
}

pub type ResultT<T> = Result<T, RequestrError>;

fn default_header() -> HashMap<String, String> {
    HashMap::new()
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct RequestConfig {
    pub url: String,
    #[serde(default = "default_header")]
    pub header: HashMap<String, String>,
    pub body: Option<String>,
    pub method: Option<String>,
}

pub fn load_request_template(filename: &PathBuf) -> ResultT<Template> {
    let contents = match fs::read_to_string(filename) {
        Ok(contents) => contents,
        Err(err) => {
            return Err(RequestrError::OpeningTemplateFailed(
                filename.to_string_lossy().to_string(),
                err,
            ))
        }
    };

    let request_config_template = Template::new(contents.as_str());
    debug!("{:#?}", request_config_template);

    Ok(request_config_template)
}

pub fn validate_parameter(template: &Template, parameter: &HashMap<String, String>) -> ResultT<()> {
    debug!("{:#?}", parameter);

    let provided_names: HashSet<_> = parameter.keys().cloned().collect();
    let names: HashSet<_> = template.names.iter().cloned().collect();

    let from_input: HashSet<_> = provided_names.difference(&names).collect();
    let from_template: HashSet<_> = names.difference(&provided_names).collect();

    if from_input.len() > 0 {
        info!(
            "Following parameters are defined but not used: {:?}",
            from_input
        );
    }

    if from_template.len() > 0 {
        Err(
            RequestrError::MissingParameter(from_template.into_iter().map(|p| p.clone()).collect())
                .into(),
        )
    } else {
        Ok(())
    }
}

pub fn load_request_definition(
    template: &Template,
    parameter: &HashMap<String, String>,
) -> ResultT<RequestConfig> {
    let request_config_string = template.render(parameter);

    let request_config: RequestConfig = serde_yaml::from_str(request_config_string.as_str())?;

    debug!("{:#?}", request_config);

    Ok(request_config)
}

pub fn make_request(
    url: &str,
    body: Option<String>,
    method: Option<String>,
    header: HashMap<String, String>,
) -> ResultT<String> {
    let client = reqwest::blocking::Client::new();

    let request_builder = match method.unwrap_or("GET".to_string()).to_uppercase().as_str() {
        "DELETE" => client.delete(url),
        "GET" => client.get(url),
        "POST" => client.post(url),
        "PUT" => client.put(url),
        "PATCH" => client.put(url),
        method => {
            return Err(RequestrError::BrokenRequestConfig(format!(
                "Unknown http method: {}",
                method
            )))
        }
    };

    let request_builder = match body {
        Some(body) => request_builder.body(body),
        None => request_builder,
    };

    let request_builder = header
        .into_iter()
        .fold(request_builder, |request_builder, (name, value)| {
            request_builder.header(name.as_str(), value.as_str())
        });

    let response = request_builder.send()?;

    debug!("{:#?}", response);

    let response_body = response.text()?;

    Ok(response_body)
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}