1use log::{debug, info};
2use serde::{Deserialize, Serialize};
3use serde_yaml;
4use std::{
5 collections::{HashMap, HashSet},
6 fs, io,
7 path::PathBuf,
8};
9
10use thiserror::Error;
11
12mod template;
13
14pub use template::Template;
15
16#[derive(Error, Debug)]
17pub enum RequestrError {
18 #[error("Following parameter are missing from the input: {0:#?}")]
19 MissingParameter(Vec<String>),
20 #[error("Unable to load Template from {0}")]
21 OpeningTemplateFailed(String, io::Error),
22 #[error("Parsing template failed")]
23 TemplateParsingFailed(#[from] serde_yaml::Error),
24 #[error("Wrong request config: {0}")]
25 BrokenRequestConfig(String),
26 #[error("Request failed")]
27 UnknownRequestError(#[from] reqwest::Error),
28 #[error("Unknown Requestr Error")]
29 Unknown,
30}
31
32pub type ResultT<T> = Result<T, RequestrError>;
33
34fn default_header() -> HashMap<String, String> {
35 HashMap::new()
36}
37
38#[derive(Debug, PartialEq, Serialize, Deserialize)]
39pub struct RequestConfig {
40 pub url: String,
41 #[serde(default = "default_header")]
42 pub header: HashMap<String, String>,
43 pub body: Option<String>,
44 pub method: Option<String>,
45}
46
47pub fn load_request_template(filename: &PathBuf) -> ResultT<Template> {
48 let contents = match fs::read_to_string(filename) {
49 Ok(contents) => contents,
50 Err(err) => {
51 return Err(RequestrError::OpeningTemplateFailed(
52 filename.to_string_lossy().to_string(),
53 err,
54 ))
55 }
56 };
57
58 let request_config_template = Template::new(contents.as_str());
59 debug!("{:#?}", request_config_template);
60
61 Ok(request_config_template)
62}
63
64pub fn validate_parameter(template: &Template, parameter: &HashMap<String, String>) -> ResultT<()> {
65 debug!("{:#?}", parameter);
66
67 let provided_names: HashSet<_> = parameter.keys().cloned().collect();
68 let names: HashSet<_> = template.names.iter().cloned().collect();
69
70 let from_input: HashSet<_> = provided_names.difference(&names).collect();
71 let from_template: HashSet<_> = names.difference(&provided_names).collect();
72
73 if from_input.len() > 0 {
74 info!(
75 "Following parameters are defined but not used: {:?}",
76 from_input
77 );
78 }
79
80 if from_template.len() > 0 {
81 Err(
82 RequestrError::MissingParameter(from_template.into_iter().map(|p| p.clone()).collect())
83 .into(),
84 )
85 } else {
86 Ok(())
87 }
88}
89
90pub fn load_request_definition(
91 template: &Template,
92 parameter: &HashMap<String, String>,
93) -> ResultT<RequestConfig> {
94 let request_config_string = template.render(parameter);
95
96 let request_config: RequestConfig = serde_yaml::from_str(request_config_string.as_str())?;
97
98 debug!("{:#?}", request_config);
99
100 Ok(request_config)
101}
102
103pub fn make_request(
104 url: &str,
105 body: Option<String>,
106 method: Option<String>,
107 header: HashMap<String, String>,
108) -> ResultT<String> {
109 let client = reqwest::blocking::Client::new();
110
111 let request_builder = match method.unwrap_or("GET".to_string()).to_uppercase().as_str() {
112 "DELETE" => client.delete(url),
113 "GET" => client.get(url),
114 "POST" => client.post(url),
115 "PUT" => client.put(url),
116 "PATCH" => client.put(url),
117 method => {
118 return Err(RequestrError::BrokenRequestConfig(format!(
119 "Unknown http method: {}",
120 method
121 )))
122 }
123 };
124
125 let request_builder = match body {
126 Some(body) => request_builder.body(body),
127 None => request_builder,
128 };
129
130 let request_builder = header
131 .into_iter()
132 .fold(request_builder, |request_builder, (name, value)| {
133 request_builder.header(name.as_str(), value.as_str())
134 });
135
136 let response = request_builder.send()?;
137
138 debug!("{:#?}", response);
139
140 let response_body = response.text()?;
141
142 Ok(response_body)
143}
144
145#[cfg(test)]
146mod tests {
147 #[test]
148 fn it_works() {
149 assert_eq!(2 + 2, 4);
150 }
151}