use std::collections::BTreeMap;
use std::time::Duration;
use clap::{ArgMatches, CommandFactory, FromArgMatches};
use rand::Rng;
use crate::args::{
PositiveU64, PositiveUsize, Scenario, ScenarioStep, TesterArgs, TlsVersion, parse_header,
parsers::parse_duration_arg,
};
use crate::config::apply::scenario::{ScenarioDefaults, parse_scenario};
use crate::config::types::{ConfigFile, LoadConfig, ScenarioConfig};
use crate::config::{apply_config, parse_duration_value};
use crate::error::{AppError, AppResult, ValidationError};
use crate::http::workload::render_template;
use crate::http::workload::{StepRequestContext, build_step_request, build_template_vars};
use crate::metrics::MetricsRange;
use reqwest::Client;
thread_local! {
static BASE_MATCHES: ArgMatches = TesterArgs::command().get_matches_from(["strest"]);
}
pub fn parse_header_input(input: &str) -> AppResult<(String, String)> {
parse_header(input).map_err(AppError::from)
}
pub fn parse_duration_arg_input(input: &str) -> AppResult<Duration> {
parse_duration_arg(input)
}
pub fn parse_duration_value_input(input: &str) -> AppResult<Duration> {
parse_duration_value(input)
}
pub fn parse_tls_version_input(input: &str) -> AppResult<TlsVersion> {
input.parse::<TlsVersion>()
}
pub fn parse_metrics_range_input(input: &str) -> AppResult<MetricsRange> {
input.parse::<MetricsRange>().map_err(AppError::from)
}
#[must_use]
pub fn render_template_input(input: &str, vars: &BTreeMap<String, String>) -> String {
render_template(input, vars)
}
pub fn apply_config_from_toml(input: &str) -> AppResult<()> {
let config: ConfigFile = toml::from_str(input)?;
apply_config_to_defaults(config)
}
pub fn apply_config_from_json(input: &[u8]) -> AppResult<()> {
let config: ConfigFile = serde_json::from_slice(input)?;
apply_config_to_defaults(config)
}
pub fn parse_positive_u64_input(input: &str) -> AppResult<u64> {
let value: PositiveU64 = input.parse()?;
Ok(value.get())
}
pub fn parse_positive_usize_input(input: &str) -> AppResult<usize> {
let value: PositiveUsize = input.parse()?;
Ok(value.get())
}
pub fn compile_rand_regex_input(pattern: &str, max_repeat: u32) -> AppResult<()> {
let regex = rand_regex::Regex::compile(pattern, max_repeat).map_err(|err| {
AppError::validation(ValidationError::InvalidRandRegex {
pattern: pattern.to_owned(),
source: err,
})
})?;
let _sample: String = rand::thread_rng().sample(®ex);
Ok(())
}
pub fn parse_form_entry_input(input: &str) -> AppResult<()> {
let (name, value) = input.split_once('=').ok_or_else(|| {
AppError::validation(ValidationError::InvalidFormEntryFormat {
entry: input.to_owned(),
})
})?;
let name = name.trim();
if name.is_empty() {
return Err(AppError::validation(ValidationError::FormEntryNameEmpty {
entry: input.to_owned(),
}));
}
let value = value.trim();
if let Some(path) = value.strip_prefix('@') {
if path.is_empty() {
return Err(AppError::validation(ValidationError::FormEntryPathEmpty {
entry: input.to_owned(),
}));
}
return Ok(());
}
Ok(())
}
pub fn apply_load_config_input(load: LoadConfig) -> AppResult<()> {
let config = ConfigFile {
load: Some(load),
..ConfigFile::default()
};
apply_config_to_defaults(config)
}
pub fn parse_scenario_config_input(config: &ScenarioConfig) -> AppResult<()> {
BASE_MATCHES.with(|matches| {
let args = TesterArgs::from_arg_matches(matches)?;
let defaults = ScenarioDefaults::new(
args.url.clone(),
args.method,
args.data.clone(),
args.headers.clone(),
);
parse_scenario(config, &defaults).map(|_| ())
})
}
pub fn build_scenario_request_input(
scenario: &Scenario,
step: &ScenarioStep,
seq: u64,
step_index: usize,
) -> AppResult<()> {
let client = Client::new();
let vars = build_template_vars(scenario, step, seq, step_index);
build_step_request(
&client,
scenario,
step,
&vars,
&StepRequestContext {
connect_to: &[],
host_header: None,
auth: None,
},
)?;
Ok(())
}
pub fn load_config_file_input(path: &std::path::Path) -> AppResult<()> {
crate::config::load_config_file(path).map(|_| ())
}
fn apply_config_to_defaults(config: ConfigFile) -> AppResult<()> {
BASE_MATCHES.with(|matches| {
let args = TesterArgs::from_arg_matches(matches)?;
apply_config(args, matches, config).map(|_| ())
})
}