use anyhow::{Context, Result, anyhow};
use indexmap::IndexMap;
use serde::Deserialize;
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
#[derive(Debug, Deserialize)]
pub struct RequestFile {
#[serde(default)]
pub config: Config,
#[serde(default)]
pub vars: HashMap<String, String>,
#[serde(default)]
pub profiles: HashMap<String, HashMap<String, String>>,
#[serde(flatten)]
pub requests: IndexMap<String, RequestDef>,
}
#[derive(Debug, Deserialize, Default, Clone, Copy)]
pub struct Config {
#[serde(default)]
pub ignore_ssl: bool,
}
#[derive(Debug, Deserialize, Clone)]
pub struct RequestDef {
#[serde(default = "default_method")]
pub method: String,
pub url: String,
#[serde(default)]
pub headers: HashMap<String, String>,
#[serde(default)]
pub query: HashMap<String, String>,
pub body: Option<String>,
pub body_file: Option<String>,
pub expect_status: Option<Vec<u16>>,
#[serde(default = "default_timeout")]
pub timeout_secs: u64,
}
fn default_method() -> String {
"GET".to_string()
}
fn default_timeout() -> u64 {
30
}
pub fn load_ext_body(rf: &mut RequestFile, request_file_path: &Path) -> Result<()> {
for (request_name, request) in &mut rf.requests {
match (&request.body, &request.body_file) {
(Some(_), Some(_)) => {
return Err(anyhow!(
"request '{}' specifies both 'body' and 'body_file' - only one may be specified",
request_name
));
}
(None, Some(path)) => {
let body_path = if Path::new(path).is_absolute() {
PathBuf::from(path)
} else {
request_file_path
.parent()
.unwrap_or(Path::new("."))
.join(path)
};
let content = fs::read_to_string(body_path).with_context(|| {
format!(
"could not read body_file '{}' in request '{}'",
path, request_name
)
})?;
request.body = Some(content);
}
_ => {}
}
}
Ok(())
}
pub fn load_requests(rf: &RequestFile, name: Option<&str>) -> Result<Vec<(String, RequestDef)>> {
match name {
Some(n) => {
let req = rf.requests.get(n).cloned().ok_or_else(|| {
let available: Vec<&String> = rf.requests.keys().collect();
anyhow!("no request named '{}'. Available: {:?}", n, available)
})?;
Ok(vec![(n.to_string(), req)])
}
None => Ok(rf
.requests
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()),
}
}