use crate::error::arg_parse_error;
use rand::distr::uniform::{SampleRange, SampleUniform};
use rand::prelude::Distribution;
use rand::{random, rng, Rng};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
use std::ops::RangeInclusive;
use rand::distr::StandardUniform;
use tera::{from_value, to_value, Result, Value};
pub(crate) fn parse_arg<T>(
args: &HashMap<String, Value>,
parameter: &'static str,
) -> Result<Option<T>>
where
T: DeserializeOwned,
{
args.get(parameter)
.cloned()
.map(|length_value| from_value(length_value))
.transpose()
.map_err(|source| arg_parse_error(parameter, source))
}
pub(crate) fn gen_value_in_range<T>(
start_opt: Option<T>,
end_opt: Option<T>,
default_start: T,
default_end: T,
) -> T
where
T: SampleUniform,
RangeInclusive<T>: SampleRange<T>,
StandardUniform: Distribution<T>,
{
match (start_opt, end_opt) {
(Some(start), Some(end)) => rng().random_range(start..=end),
(Some(start), None) => rng().random_range(start..=default_end),
(None, Some(end)) => rng().random_range(default_start..=end),
(None, None) => random::<T>(),
}
}
pub(crate) fn parse_range_and_gen_value_in_range<T>(
args: &HashMap<String, Value>,
default_start: T,
default_end: T,
) -> Result<Value>
where
T: SampleUniform + DeserializeOwned + Serialize,
RangeInclusive<T>: SampleRange<T>,
StandardUniform: Distribution<T>,
{
let start_opt: Option<T> = parse_arg(args, "start")?;
let end_opt: Option<T> = parse_arg(args, "end")?;
let random_value: T = gen_value_in_range(start_opt, end_opt, default_start, default_end);
let json_value: Value = to_value(random_value)?;
Ok(json_value)
}
#[cfg(test)]
pub(crate) mod tests {
use regex::Regex;
use tera::{Context, Function, Tera};
use tracing::trace;
pub(crate) fn test_tera_rand_function<F>(
function: F,
function_name: &str,
input_template_str: &str,
expected_regex_str: &str,
) where
F: Function + 'static,
{
let mut tera: Tera = Tera::default();
tera.register_function(function_name, function);
let expected_regex: Regex = Regex::new(expected_regex_str).unwrap_or_else(|e| {
panic!(
"Unable to construct a Regex object out of {} due to error: {:?}",
expected_regex_str, e
)
});
let context: Context = Context::new();
let render_result: String = tera
.render_str(input_template_str, &context)
.unwrap_or_else(|e| {
panic!(
"Unable to render template {} for function {} due to error: {:?}",
input_template_str, function_name, e
)
});
trace!("render result: {render_result}");
assert!(expected_regex.is_match(render_result.as_str()));
}
pub(crate) fn test_tera_rand_function_returns_error<F>(
function: F,
function_name: &str,
input_template_str: &str,
) where
F: Function + 'static,
{
let mut tera: Tera = Tera::default();
tera.register_function(function_name, function);
let context: Context = Context::new();
let render_result: tera::Result<String> = tera.render_str(input_template_str, &context);
trace!("render result: {render_result:?}");
assert!(render_result.is_err());
}
}