#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![allow(unknown_lints)]
#![warn(missing_debug_implementation)]
#![warn(missing_copy_implementation)]
#![warn(rust_2018_idioms)]
#![warn(rust_2021_compatibility)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_qualifications)]
#![warn(variant_size_difference)]
#![allow(clippy::multiple_crate_versions)]
use clap::Parser;
use core::time::Duration;
use log::{debug, error, info, trace};
use rand::{rngs::SmallRng, thread_rng, SeedableRng};
use rayon::prelude::*;
use std::thread::available_parallelism;
mod args;
mod attribute;
mod config;
mod metric;
mod min_queue;
mod simulation;
use args::{log_level, Args};
use attribute::Attribute;
use config::Config;
use metric::{Metric, MetricType};
use min_queue::MinQueue;
use simulation::{Client, Error as SimulationError, Server, Simulation};
fn run_sim(
counter: usize,
tick_size: Duration,
tick_until: Duration,
servers: Vec<Server>,
clients: Vec<Client>,
metrics: Vec<Metric>,
) -> Result<(), SimulationError> {
let rng = Box::new(SmallRng::from_rng(thread_rng()).unwrap());
let mut sim = Simulation::new(rng, tick_size, tick_until);
info!(target: "main", "sim {counter}: created");
for server in servers {
sim.add_server(server)?;
}
trace!(target: "main", "sim {counter}: added servers");
for client in clients {
sim.add_client(client)?;
}
trace!(target: "main", "sim {counter}: added clients");
for metric in metrics {
sim.add_metric(metric)?;
}
trace!(target: "main", "sim {counter}: added metrics");
sim.enable()?;
info!(target: "main", "sim {counter}: enabled");
while sim.tick() {}
info!(target: "main", "sim {counter}: finished ticking");
println!("Sim {counter} {:?}\n{}", sim.running(), sim.statistics()?);
Ok(())
}
fn main() {
match try_main() {
Ok(_) => {
std::process::exit(exitcode::OK);
}
Err(err) => {
error!(target: "main", "{err}");
std::process::exit(exitcode::USAGE);
}
}
}
fn try_main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let log_level = log_level(args.log_level);
simple_logger::init_with_level(log_level)?;
let config_path = args.config_path.unwrap();
let config = Config::try_from(&config_path)?;
let sim_threads = available_parallelism()?.get() - 1;
rayon::ThreadPoolBuilder::new()
.num_threads(sim_threads)
.build_global()?;
debug!(target: "main", "setting rayon to use {sim_threads} threads");
let clients = config.clients();
trace!(target: "main", "clients: {clients:?}");
let servers = config.servers();
trace!(target: "main", "servers: {servers:?}");
let metrics = config.metrics()?;
trace!(target: "main", "metrics: {metrics:?}");
let simulations = config.simulations;
let tick_size = config.tick_size;
let tick_until = config.tick_until;
trace!(target: "main", "sim: ({simulations}, {tick_size:?}, {tick_until:?}");
(0..simulations)
.into_par_iter()
.map(|sim| {
run_sim(
sim,
tick_size,
tick_until,
servers.clone(),
clients.clone(),
metrics.clone(),
)
})
.collect::<Result<Vec<()>, SimulationError>>()?;
Ok(())
}