use std::env::{self, VarError};
use std::str::FromStr;
use crate::frontend::{Mode, RunCode};
use crate::{Limit, Seed};
const KEY_MODE: &str = "DICETEST_MODE";
const KEY_DEBUG: &str = "DICETEST_DEBUG";
const KEY_REGRESSIONS_ENABLED: &str = "DICETEST_REGRESSIONS_ENABLED";
const KEY_SEED: &str = "DICETEST_SEED";
const KEY_ONCE_LIMIT: &str = "DICETEST_ONCE_LIMIT";
const KEY_START_LIMIT: &str = "DICETEST_START_LIMIT";
const KEY_END_LIMIT: &str = "DICETEST_END_LIMIT";
const KEY_LIMIT_MULTIPLIER: &str = "DICETEST_LIMIT_MULTIPLIER";
const KEY_PASSES: &str = "DICETEST_PASSES";
const KEY_PASSES_MULTIPLIER: &str = "DICETEST_PASSES_MULTIPLIER";
const KEY_HINTS_ENABLED: &str = "DICETEST_HINTS_ENABLED";
const KEY_STATS_ENABLED: &str = "DICETEST_STATS_ENABLED";
const KEY_STATS_MAX_VALUE_COUNT: &str = "DICETEST_STATS_MAX_VALUE_COUNT";
const KEY_STATS_PERCENT_PRECISION: &str = "DICETEST_STATS_PERCENT_PRECISION";
const VALUE_NONE: &str = "none";
const VALUE_REPEATEDLY: &str = "repeatedly";
const VALUE_ONCE: &str = "once";
pub enum EnvValue<T> {
NotPresent,
Present(T),
}
pub fn read_mode() -> Result<EnvValue<Mode>, String> {
let key = KEY_DEBUG;
match env::var(key) {
Err(VarError::NotPresent) => read_non_debug_mode(),
Err(err) => handle_var_error(key, err),
Ok(s) => match RunCode::from_str(&s) {
Ok(run_code) => Ok(EnvValue::Present(Mode::Debug(run_code))),
Err(err) => Err(format!("Value for '{}' is not valid: {}", key, err)),
},
}
}
fn read_non_debug_mode() -> Result<EnvValue<Mode>, String> {
match env::var(KEY_MODE) {
Err(err) => handle_var_error(KEY_MODE, err),
Ok(var) => {
let str = var.as_str();
if str == VALUE_REPEATEDLY {
Ok(EnvValue::Present(Mode::Repeatedly))
} else if str == VALUE_ONCE {
Ok(EnvValue::Present(Mode::Once))
} else {
let error = format!(
"Value for '{}' must be either '{}', or '{}'",
KEY_MODE, VALUE_REPEATEDLY, VALUE_ONCE
);
Err(error)
}
}
}
}
pub fn read_regressions_enabled() -> Result<EnvValue<bool>, String> {
read_value(KEY_REGRESSIONS_ENABLED, "a bool", bool::from_str)
}
pub fn read_seed() -> Result<EnvValue<Option<Seed>>, String> {
read_option_value(KEY_SEED, "an u64", |s| u64::from_str(s).map(Seed))
}
pub fn read_once_limit() -> Result<EnvValue<Limit>, String> {
read_value(KEY_ONCE_LIMIT, "an u64", |s| u64::from_str(s).map(Limit))
}
pub fn read_start_limit() -> Result<EnvValue<Limit>, String> {
read_value(KEY_START_LIMIT, "an u64", |s| u64::from_str(s).map(Limit))
}
pub fn read_end_limit() -> Result<EnvValue<Limit>, String> {
read_value(KEY_END_LIMIT, "an u64", |s| u64::from_str(s).map(Limit))
}
pub fn read_limit_multiplier() -> Result<EnvValue<Option<f64>>, String> {
read_option_value(KEY_LIMIT_MULTIPLIER, "a f64", f64::from_str)
}
pub fn read_passes() -> Result<EnvValue<u64>, String> {
read_value(KEY_PASSES, "an u64", u64::from_str)
}
pub fn read_passes_multiplier() -> Result<EnvValue<Option<f64>>, String> {
read_option_value(KEY_PASSES_MULTIPLIER, "a f64", f64::from_str)
}
pub fn read_hints_enabled() -> Result<EnvValue<bool>, String> {
read_value(KEY_HINTS_ENABLED, "a bool", bool::from_str)
}
pub fn read_stats_enabled() -> Result<EnvValue<bool>, String> {
read_value(KEY_STATS_ENABLED, "a bool", bool::from_str)
}
pub fn read_stats_max_value_count() -> Result<EnvValue<Option<usize>>, String> {
read_option_value(KEY_STATS_MAX_VALUE_COUNT, "an usize", usize::from_str)
}
pub fn read_stats_percent_precision() -> Result<EnvValue<usize>, String> {
read_value(KEY_STATS_PERCENT_PRECISION, "an usize", usize::from_str)
}
fn read_value<T, E>(
key: &str,
typ: &str,
parse: impl FnOnce(&str) -> Result<T, E>,
) -> Result<EnvValue<T>, String> {
match env::var(key) {
Err(err) => handle_var_error(key, err),
Ok(s) => match parse(&s) {
Ok(value) => Ok(EnvValue::Present(value)),
Err(_) => Err(format!("Value for '{}' must be {}", key, typ)),
},
}
}
fn read_option_value<T, E>(
key: &str,
typ: &str,
parse: impl FnOnce(&str) -> Result<T, E>,
) -> Result<EnvValue<Option<T>>, String> {
match env::var(key) {
Err(err) => handle_var_error(key, err),
Ok(s) if s == VALUE_NONE => Ok(EnvValue::Present(None)),
Ok(s) => match parse(&s) {
Ok(value) => Ok(EnvValue::Present(Some(value))),
Err(_) => Err(format!(
"Value for '{}' must be either '{}' or {}",
key, VALUE_NONE, typ
)),
},
}
}
fn handle_var_error<T>(key: &str, err: VarError) -> Result<EnvValue<T>, String> {
match err {
VarError::NotPresent => Ok(EnvValue::NotPresent),
VarError::NotUnicode(_) => Err(format!("Value for '{}' is not valid unicode", key)),
}
}