use crate::{
error::Error,
simulations::{
model::{nodes::NodeNames, params::GenParams, Model, ModelTrait},
results::{
ModelSimulationNodeResult, ModelSimulationResult, SimulationResult, SimulationResults,
},
},
Result,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize)]
pub struct Threat {
id: String,
tef: (f32, f32, f32),
tc: (f32, f32, f32),
lm: (f32, f32, f32),
}
impl Threat {
pub fn from_json(name: &str, v: &Value) -> Result<Threat> {
let threats = match &v["threats"] {
Value::Null => return Err(Error::ThreatsFieldNotFound),
_ => &v["threats"],
};
let params = match &v["params"] {
Value::Null => return Err(Error::ParamsFieldNotFound),
_ => &v["params"],
};
let params_tef = match params["expected_frequency"] {
Value::Null => return Err(Error::ExpectedFrequencyFieldNotFound),
_ => ¶ms["expected_frequency"],
};
let params_tc = match params["capability"] {
Value::Null => return Err(Error::CapabilityFieldNotFound),
_ => ¶ms["capability"],
};
let params_lm = match params["loss_magnitude"] {
Value::Null => return Err(Error::LossMagnitudeFieldNotFound),
_ => ¶ms["loss_magnitude"],
};
let threat = match &threats[name] {
Value::Null => return Err(Error::ThreatNotFound(name.to_string())),
_ => &threats[name],
};
let tef_str = threat["expected_frequency"]
.as_str()
.ok_or_else(|| Error::InvalidExpectedFrequency(name.to_string()))?;
let c_str = threat["capability"]
.as_str()
.ok_or_else(|| Error::InvalidCapability(name.to_string()))?;
let lm_str = threat["loss_magnitude"]
.as_str()
.ok_or_else(|| Error::InvalidLossMagnitude(name.to_string()))?;
let s = Threat {
id: name.to_string(),
tef: (
params_tef[tef_str][0]
.as_f64()
.ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
params_tef[tef_str][1]
.as_f64()
.ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
params_tef[tef_str][2]
.as_f64()
.ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
),
tc: (
params_tc[c_str][0]
.as_f64()
.ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
params_tc[c_str][1]
.as_f64()
.ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
params_tc[c_str][2]
.as_f64()
.ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
),
lm: (
params_lm[lm_str][0]
.as_f64()
.ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
params_lm[lm_str][1]
.as_f64()
.ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
params_lm[lm_str][2]
.as_f64()
.ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
),
};
Ok(s)
}
}
#[derive(Serialize, Deserialize)]
pub struct Control {
id: String,
diff: (f32, f32, f32),
}
impl Control {
pub fn from_json(name: &str, v: &Value) -> Result<Control> {
let controls = match &v["controls"] {
Value::Null => return Err(Error::ControlsFieldNotFound),
_ => &v["controls"],
};
let params = match &v["params"] {
Value::Null => return Err(Error::ParamsFieldNotFound),
_ => &v["params"],
};
let params_diff = match params["difficulty"] {
Value::Null => return Err(Error::DifficultyFieldNotFound),
_ => ¶ms["difficulty"],
};
let control = match &controls[name] {
Value::Null => return Err(Error::InvalidControl(name.to_string())),
_ => &controls[name],
};
let diff_str = control["difficulty"]
.as_str()
.ok_or_else(|| Error::InvalidDifficulty(name.to_string()))?;
let c = Control {
id: name.to_string(),
diff: (
params_diff[diff_str][0]
.as_f64()
.ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
params_diff[diff_str][1]
.as_f64()
.ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
params_diff[diff_str][2]
.as_f64()
.ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
),
};
Ok(c)
}
}
#[derive(Serialize, Deserialize)]
pub struct Scenario {
models: Vec<Model>,
threat: Threat,
controls: Vec<Control>,
}
impl Scenario {
pub fn new(threat: Threat, controls: Vec<Control>) -> Scenario {
let mut s = Scenario {
models: vec![], threat,
controls,
};
let mut ss = Model::new(&format!("{} + {}", &s.threat.id, "NoControl"));
ss.input(
NodeNames::ThreatEventFrequency,
GenParams::PertParams {
min: s.threat.tef.0,
mean: s.threat.tef.1,
max: s.threat.tef.2,
},
)
.unwrap();
ss.input(
NodeNames::ThreatCapability,
GenParams::PertParams {
min: s.threat.tc.0,
mean: s.threat.tc.1,
max: s.threat.tc.2,
},
)
.unwrap();
ss.input(
NodeNames::LossMagnitude,
GenParams::PertParams {
min: s.threat.lm.0,
mean: s.threat.lm.1,
max: s.threat.lm.2,
},
)
.unwrap();
ss.input(
NodeNames::ControlStrength,
GenParams::ConstParams { val: 0.0 },
)
.unwrap();
s.models.push(ss);
for c in &s.controls {
let mut ss = Model::new(&format!("{} + {}", &s.threat.id, &c.id));
ss.input(
NodeNames::ThreatEventFrequency,
GenParams::PertParams {
min: s.threat.tef.0,
mean: s.threat.tef.1,
max: s.threat.tef.2,
},
)
.unwrap();
ss.input(
NodeNames::ThreatCapability,
GenParams::PertParams {
min: s.threat.tc.0,
mean: s.threat.tc.1,
max: s.threat.tc.2,
},
)
.unwrap();
ss.input(
NodeNames::LossMagnitude,
GenParams::PertParams {
min: s.threat.lm.0,
mean: s.threat.lm.1,
max: s.threat.lm.2,
},
)
.unwrap();
ss.input(
NodeNames::ControlStrength,
GenParams::PertParams {
min: c.diff.0,
mean: c.diff.1,
max: c.diff.2,
},
)
.unwrap();
s.models.push(ss);
}
s
}
pub fn from_json(threat_name: &str, control_names: Vec<&str>, v: &Value) -> Result<Scenario> {
let mut cs: Vec<Control> = vec![];
for cc in &control_names {
cs.push(Control::from_json(cc, v)?)
}
Ok(Scenario::new(Threat::from_json(threat_name, v)?, cs))
}
pub fn simulate(&mut self, n: u32) -> Result<Vec<(f32, f32, f32, f32)>> {
let mut res: Vec<(f32, f32, f32, f32)> = vec![];
for m in &mut self.models {
res.push(m.simulate(n)?);
}
Ok(res)
}
}
#[derive(Serialize, Deserialize)]
pub struct Scenarios {
simulation_count: u32,
scenarios: Vec<Scenario>,
}
impl Scenarios {
pub fn from_json(v: &Value) -> Result<Scenarios> {
let mut sc = Scenarios {
scenarios: vec![],
simulation_count: v["simulation_count"]
.as_u64()
.ok_or(Error::ThreatsFieldNotFound)? as u32,
};
for s in v["scenarios"]
.as_array()
.ok_or(Error::ScenariosFieldNotFound)?
{
let mut cc: Vec<&str> = vec![];
for c in s["controlIds"]
.as_array()
.ok_or(Error::ControlIdsNotFound)?
{
cc.push(c.as_str().ok_or_else(|| {
Error::InvalidControlId(c.as_str().unwrap_or_default().to_string())
})?);
}
sc.scenarios.push(Scenario::from_json(
s["threatId"].as_str().ok_or_else(|| {
Error::InvalidThreatId(s["threatId"].as_str().unwrap_or_default().to_string())
})?,
cc,
v,
)?);
}
Ok(sc)
}
pub fn simulate(&mut self, n: u32) -> Result<SimulationResults> {
let mut res = SimulationResults {
simulation_result: vec![],
};
for s in &mut self.scenarios {
let sim_res = s.simulate(n)?;
let mut sr = SimulationResult {
threat: s.threat.id.clone(),
simulation_result: vec![],
};
for i in 0..sim_res.len() {
let msr = ModelSimulationResult {
control: match &i {
0 => "None".to_string(),
_ => s.controls[i - 1].id.clone(),
},
risk: ModelSimulationNodeResult::new(s.models[i].get(&NodeNames::Risk)?),
lef: ModelSimulationNodeResult::new(
s.models[i].get(&NodeNames::LossEventFrequency)?,
),
v: ModelSimulationNodeResult::new(s.models[i].get(&NodeNames::Vulnerability)?),
};
sr.simulation_result.push(msr);
}
res.simulation_result.push(sr);
}
Ok(res)
}
pub fn simulate_all(&mut self) -> Result<SimulationResults> {
self.simulate(self.simulation_count)
}
}
pub fn simulate_scenario(input_json: &Value) -> Result<Value> {
let scenario = input_json
.as_object()
.ok_or(Error::InvalidInput)?
.get("scenario")
.ok_or(Error::ScenarioNotFound)?;
match Scenarios::from_json(&scenario) {
Ok(mut s) => Ok(serde_json::to_value(&s.simulate_all()?)?),
Err(e) => Err(e),
}
}