use crate::{error::Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::{json, to_value, Value};
#[derive(Serialize, Deserialize)]
pub struct D3JsDataControl {
id: String,
val: f32,
}
#[derive(Serialize, Deserialize)]
pub struct D3JsDataThreat {
id: String,
controls: Vec<D3JsDataControl>,
}
#[derive(Serialize, Deserialize)]
pub struct D3JsData {
threats: Vec<D3JsDataThreat>,
}
pub fn scenario_to_d3js(data: &str, t: Vec<&str>) -> Result<Value> {
let json: serde_json::Value = serde_json::from_str(data)?;
let mut ddd = D3JsData { threats: vec![] };
for s in json["simulation_result"]
.as_array()
.ok_or(Error::SimulationResultNotFound)?
{
for tt in &t {
let mut threat = D3JsDataThreat {
id: format!(
"{} {}",
s["threat"].as_str().ok_or(Error::ThreatsFieldNotFound)?,
tt
),
controls: vec![],
};
for c in s["simulation_result"]
.as_array()
.ok_or(Error::SimulationResultNotFound)?
{
let control = D3JsDataControl {
id: c["control"]
.as_str()
.ok_or_else(|| {
Error::InvalidChartControl(
c["control"].as_str().unwrap_or_default().to_string(),
)
})?
.to_string(),
val: c[tt]["mean"].as_f64().ok_or_else(|| {
Error::InvalidChartMean(c[tt]["mean"].as_f64().unwrap_or_default())
})? as f32,
};
threat.controls.push(control);
}
ddd.threats.push(threat);
}
}
Ok(to_value(ddd)?)
}
pub fn model_set_to_d3js(data: &str) -> Result<Value> {
let mut json: serde_json::Value = serde_json::from_str(data)?;
let mut res = json!({
"breaf_table": [],
"risk_distribution": [],
"exceedence_probability_curve": [],
"loss_exceedence_curve": [],
"component_and_aggregate_risk": [],
"dependency_trees": []
});
for m in json["metamodels"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
res["breaf_table"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"mean": m["mean"],
"min": m["min"],
"max": m["max"],
"deviation": m["dev"],
}));
}
for m in json["models"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
res["breaf_table"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"mean": m["mean"],
"min": m["min"],
"max": m["max"],
"deviation": m["dev"],
}));
}
for m in json["metamodels"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..41 {
val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
as usize]
.1 += 1.0;
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["risk_distribution"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["models"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..41 {
val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
as usize]
.1 += 1.0;
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["risk_distribution"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["metamodels"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..101 {
val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
as u32)..101
{
val[i as usize].0 += 1.0;
}
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["exceedence_probability_curve"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["models"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..101 {
val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
as u32)..101
{
val[i as usize].0 += 1.0;
}
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["exceedence_probability_curve"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["metamodels"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..101 {
val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
/ (vxmax / 100.0)) as u32)
{
val[i as usize].1 += 1.0;
}
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["loss_exceedence_curve"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["models"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
let mut v: Vec<Value> = vec![];
let mut val: Vec<(f32, f32)> = vec![];
let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
for i in 0..101 {
val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
}
for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
/ (vxmax / 100.0)) as u32)
{
val[i as usize].1 += 1.0;
}
}
for vvv in val {
v.push(json!({
"x": vvv.0,
"y": vvv.1
}));
}
res["loss_exceedence_curve"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"values": v
}));
}
for m in json["metamodels"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
res["component_and_aggregate_risk"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"mean": m["mean"],
"min": m["min"],
"max": m["max"],
"deviation": m["dev"],
}));
}
for m in json["models"]
.as_array()
.ok_or(Error::ThreatsFieldNotFound)?
{
res["component_and_aggregate_risk"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"mean": m["mean"],
"min": m["min"],
"max": m["max"],
"deviation": m["dev"],
}));
}
for m in json["models"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
{
clean_json_tree(&mut m["tree"]["root"])?;
res["dependency_trees"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
.push(json!({
"name": m["name"],
"tree": [m["tree"]["root"]]
}));
}
Ok(res)
}
fn clean_json_tree(v: &mut Value) -> Result<()> {
let obj = v.as_object_mut().ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("values").ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("min").ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("max").ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("mean").ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("dev").ok_or(Error::ThreatsFieldNotFound)?;
obj.remove("gen_params")
.ok_or(Error::ThreatsFieldNotFound)?;
for c in obj["children"]
.as_array_mut()
.ok_or(Error::ThreatsFieldNotFound)?
{
clean_json_tree(c)?
}
Ok(())
}