extern crate fbas_analyzer;
use fbas_analyzer::simulation::*;
use fbas_analyzer::Fbas;
use quicli::prelude::*;
use structopt::StructOpt;
use std::io::{self, Read};
use std::path::PathBuf;
use std::rc::Rc;
#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(short = "i", long = "initial", default_value = "0")]
initial_n: usize,
#[structopt(short = "g", long = "grow-by", default_value = "0")]
grow_by_n: usize,
#[structopt(subcommand)]
qsc_config: QuorumSetConfiguratorConfig,
#[structopt(flatten)]
verbosity: Verbosity,
}
#[derive(Debug, StructOpt)]
enum QuorumSetConfiguratorConfig {
SuperSafe,
Ideal,
Random { desired_quorum_set_size: usize },
FameWeightedRandom {
desired_quorum_set_size: usize,
graph_data_path: PathBuf,
desired_threshold: Option<usize>,
},
AllNeighbors {
graph_data_path: PathBuf,
relative_threshold: Option<f64>,
},
HigherTierNeighbors {
graph_data_path: PathBuf,
relative_threshold: Option<f64>,
},
SymmetryEnforcingHigherTierNeighbors {
graph_data_path: PathBuf,
relative_threshold: Option<f64>,
},
GlobalRank {
graph_data_path: PathBuf,
relative_threshold: Option<f64>,
},
}
fn parse_graph_path(graph_data_path: PathBuf) -> (Graph, usize) {
let piped = graph_data_path.to_str().unwrap();
let graph = if piped == "-" {
eprintln!("Reading graph from STDIN...");
let mut buf = String::new();
io::stdin()
.read_to_string(&mut buf)
.expect("Error reading from STDIN");
Graph::from_as_rel_string(&buf)
} else {
eprintln!("Reading graph from file...");
Graph::from_as_rel_file(&graph_data_path)
};
eprintln!("Read graph with {} nodes.", graph.number_of_nodes());
let nr_of_nodes = &graph.number_of_nodes();
(graph, *nr_of_nodes)
}
fn parse_qsc_config(
qsc_config: QuorumSetConfiguratorConfig,
) -> (Rc<dyn QuorumSetConfigurator>, usize) {
use qsc::*;
use QuorumSetConfiguratorConfig::*;
match qsc_config {
SuperSafe => (Rc::new(SuperSafeQsc::new()), 0),
Ideal => (Rc::new(IdealQsc::new()), 0),
Random {
desired_quorum_set_size,
} => (Rc::new(RandomQsc::new_simple(desired_quorum_set_size)), 0),
FameWeightedRandom {
desired_quorum_set_size,
desired_threshold,
graph_data_path,
} => {
let (graph, nodes) = parse_graph_path(graph_data_path);
(
Rc::new(RandomQsc::new(
desired_quorum_set_size,
desired_threshold,
Some(graph.get_in_degrees()),
)),
nodes,
)
}
AllNeighbors {
relative_threshold,
graph_data_path,
} => {
let (graph, nodes) = parse_graph_path(graph_data_path);
(
Rc::new(AllNeighborsQsc::new(graph, relative_threshold)),
nodes,
)
}
HigherTierNeighbors {
graph_data_path,
relative_threshold,
} => {
let (graph, nodes) = parse_graph_path(graph_data_path);
(
Rc::new(HigherTierNeighborsQsc::new(
graph,
relative_threshold,
false,
)),
nodes,
)
}
SymmetryEnforcingHigherTierNeighbors {
graph_data_path,
relative_threshold,
} => {
let (graph, nodes) = parse_graph_path(graph_data_path);
(
Rc::new(HigherTierNeighborsQsc::new(graph, relative_threshold, true)),
nodes,
)
}
GlobalRank {
graph_data_path,
relative_threshold,
} => {
let (graph, nodes) = parse_graph_path(graph_data_path);
(
Rc::new(GlobalRankQsc::new(graph, relative_threshold)),
nodes,
)
}
}
}
fn main() -> CliResult {
let args = Cli::from_args();
args.verbosity.setup_env_logger("fbas_analyzer")?;
let (qsc, nodes_in_graph) = parse_qsc_config(args.qsc_config);
let initial_n = if args.initial_n > 0 || args.grow_by_n > 0 {
args.initial_n
} else {
nodes_in_graph
};
let grow_by_n = args.grow_by_n;
let monitor = Rc::new(monitors::DebugMonitor::new());
let mut simulator = Simulator::new(
Fbas::new_generic_unconfigured(initial_n),
qsc,
Rc::clone(&monitor) as Rc<dyn SimulationMonitor>,
);
eprintln!("Starting simulation...");
simulator.simulate_global_reevaluation(initial_n);
simulator.simulate_growth(grow_by_n);
let fbas = simulator.finalize();
eprintln!("Finished simulation, dumping FBAS...");
println!("{}", fbas.to_json_string_pretty());
Ok(())
}