-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: load config and overwrite with CLI args
- Loading branch information
Showing
2 changed files
with
240 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
use std::{path::PathBuf, str::FromStr}; | ||
|
||
use anyhow::Context; | ||
use log::LevelFilter; | ||
use serde::Deserialize; | ||
|
||
use crate::Cli; | ||
|
||
#[derive(Debug)] | ||
pub struct SimulationConfig { | ||
// Path to a directory where simulation results are save | ||
pub data_dir: PathBuf, | ||
// Path to simulation file | ||
pub sim_file: PathBuf, | ||
// Duration for which the simulation should run | ||
pub total_time: Option<u32>, | ||
// Number of activity results to batch together before printing to csv file [min: 1] | ||
pub print_batch_size: u32, | ||
/// Level of verbosity of the messages displayed by the simulator. | ||
/// Possible values: [off, error, warn, info, debug, trace] | ||
pub log_level: LevelFilter, | ||
/// Expected payment amount for the random activity generator | ||
pub expected_pmt_amt: u64, | ||
/// Multiplier of the overall network capacity used by the random activity generator | ||
pub capacity_multiplier: f64, | ||
/// Do not create an output file containing the simulations results | ||
pub no_results: bool, | ||
/// Duration after which results are logged | ||
pub log_interval: u64, | ||
} | ||
|
||
impl<'de> Deserialize<'de> for SimulationConfig { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: serde::Deserializer<'de>, | ||
{ | ||
#[derive(Deserialize)] | ||
struct SimulationConfigHelper { | ||
data_dir: PathBuf, | ||
sim_file: PathBuf, | ||
total_time: Option<u32>, | ||
print_batch_size: u32, | ||
log_level: String, | ||
expected_pmt_amt: u64, | ||
capacity_multiplier: f64, | ||
no_results: bool, | ||
log_interval: u64, | ||
} | ||
|
||
let helper: SimulationConfigHelper = Deserialize::deserialize(deserializer)?; | ||
|
||
let log_level = | ||
LevelFilter::from_str(&helper.log_level).map_err(serde::de::Error::custom)?; | ||
|
||
Ok(SimulationConfig { | ||
data_dir: helper.data_dir, | ||
sim_file: helper.sim_file, | ||
total_time: helper.total_time, | ||
print_batch_size: helper.print_batch_size, | ||
log_level, | ||
expected_pmt_amt: helper.expected_pmt_amt, | ||
capacity_multiplier: helper.capacity_multiplier, | ||
no_results: helper.no_results, | ||
log_interval: helper.log_interval, | ||
}) | ||
} | ||
} | ||
|
||
impl SimulationConfig { | ||
/// The default simulation configuration file with default simulation values | ||
pub const DEFAULT_SIM_CONFIG_FILE: &'static str = "conf.json"; | ||
|
||
/// Loads the simulation configuration from a path and overwrites loaded values with | ||
/// CLI arguments | ||
pub fn load( | ||
path: &PathBuf, | ||
Cli { | ||
data_dir, | ||
sim_file, | ||
total_time, | ||
print_batch_size, | ||
log_level, | ||
expected_pmt_amt, | ||
capacity_multiplier, | ||
no_results, | ||
}: &Cli, | ||
) -> anyhow::Result<Self> { | ||
// 1. Load simulation config from specified path | ||
let mut sim_conf = serde_json::from_str::<SimulationConfig>( | ||
&std::fs::read_to_string(&path) | ||
.context(format!("Failed to read content of {:?} to string", path))?, | ||
) | ||
.context("Failed to deserialize configuration string into SimulationConfig")?; | ||
|
||
// 2. Override config values with CLI arguments (if passed) | ||
if let Some(data_directory) = data_dir { | ||
log::info!( | ||
"SimulatinConfig::data_dir {} overwritten by CLI argument {}", | ||
sim_conf.data_dir.display(), | ||
data_directory.display() | ||
); | ||
sim_conf.data_dir = data_directory.to_owned(); | ||
} | ||
|
||
if let Some(simulation_file) = sim_file { | ||
log::info!( | ||
"SimulatinConfig::sim_file {} overwritten by CLI argument {}", | ||
sim_conf.sim_file.display(), | ||
simulation_file.display() | ||
); | ||
sim_conf.sim_file = simulation_file.to_owned(); | ||
} | ||
|
||
if let Some(total_simulation_time) = total_time { | ||
log::info!( | ||
"SimulatinConfig::total_time {:?} overwritten by CLI argument {}", | ||
sim_conf.total_time, | ||
total_simulation_time | ||
); | ||
sim_conf.total_time = Some(*total_simulation_time); | ||
} | ||
|
||
if let Some(batch_size_to_print) = print_batch_size { | ||
log::info!( | ||
"SimulatinConfig::print_batch_size {:?} overwritten by CLI argument {}", | ||
sim_conf.print_batch_size, | ||
batch_size_to_print | ||
); | ||
sim_conf.print_batch_size = *batch_size_to_print; | ||
} | ||
|
||
if let Some(log_level_filter) = log_level { | ||
log::info!( | ||
"SimulatinConfig::log_level {:?} overwritten by CLI argument {}", | ||
sim_conf.log_level, | ||
log_level_filter | ||
); | ||
sim_conf.log_level = *log_level_filter; | ||
} | ||
|
||
if let Some(expected_payment_amount) = expected_pmt_amt { | ||
log::info!( | ||
"SimulatinConfig::expected_pmt_amt {:?} overwritten by CLI argument {}", | ||
sim_conf.expected_pmt_amt, | ||
expected_payment_amount | ||
); | ||
sim_conf.expected_pmt_amt = *expected_payment_amount; | ||
} | ||
|
||
if let Some(capacity_multiplier) = capacity_multiplier { | ||
log::info!( | ||
"SimulatinConfig::capacity_multiplier {:?} overwritten by CLI argument {}", | ||
sim_conf.capacity_multiplier, | ||
capacity_multiplier | ||
); | ||
sim_conf.capacity_multiplier = *capacity_multiplier; | ||
} | ||
|
||
if let Some(results) = no_results { | ||
log::info!( | ||
"SimulatinConfig::no_results {:?} overwritten by CLI argument {}", | ||
sim_conf.no_results, | ||
results | ||
); | ||
sim_conf.no_results = *results; | ||
} | ||
|
||
Ok(sim_conf) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use std::{path::PathBuf, str::FromStr}; | ||
|
||
use crate::Cli; | ||
|
||
use super::SimulationConfig; | ||
|
||
#[test] | ||
fn replace_config_with_cli_args() { | ||
let cli = Cli{ | ||
data_dir: Some(PathBuf::from_str("data").expect("Failed to create test data directory")), | ||
sim_file: Some(PathBuf::from_str("sim.json").expect("Failed to create test simulation file")), | ||
total_time: Some(60), | ||
print_batch_size: Some(10), | ||
log_level: Some(log::LevelFilter::Debug), | ||
expected_pmt_amt: Some(500), | ||
capacity_multiplier: Some(3.142), | ||
no_results: Some(true) | ||
}; | ||
|
||
let conf = SimulationConfig::load( | ||
&PathBuf::from("../conf.json"), | ||
&cli | ||
) | ||
.expect("Failed to create simulation config"); | ||
|
||
assert_eq!(Some(conf.data_dir), cli.data_dir); | ||
assert_eq!(Some(conf.sim_file), cli.sim_file); | ||
assert_eq!(conf.total_time, cli.total_time); | ||
assert_eq!(Some(conf.print_batch_size), cli.print_batch_size); | ||
assert_eq!(Some(conf.log_level), cli.log_level); | ||
assert_eq!(Some(conf.expected_pmt_amt), cli.expected_pmt_amt); | ||
assert_eq!(Some(conf.capacity_multiplier), cli.capacity_multiplier); | ||
assert_eq!(Some(conf.no_results), cli.no_results); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters