use super::core::{BoundaryCrossing, GgenMembrane};
use crate::utils::error::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvDocument {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub entity: HashMap<String, HashMap<String, String>>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub activity: HashMap<String, HashMap<String, String>>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub agent: HashMap<String, HashMap<String, String>>,
#[serde(
default,
rename = "wasGeneratedBy",
skip_serializing_if = "HashMap::is_empty"
)]
pub was_generated_by: HashMap<String, ProvRelation>,
#[serde(default, rename = "used", skip_serializing_if = "HashMap::is_empty")]
pub used: HashMap<String, ProvRelation>,
#[serde(
default,
rename = "wasAssociatedWith",
skip_serializing_if = "HashMap::is_empty"
)]
pub was_associated_with: HashMap<String, ProvRelation>,
#[serde(
default,
rename = "wasAttributedTo",
skip_serializing_if = "HashMap::is_empty"
)]
pub was_attributed_to: HashMap<String, ProvRelation>,
#[serde(
default,
rename = "wasDerivedFrom",
skip_serializing_if = "HashMap::is_empty"
)]
pub was_derived_from: HashMap<String, ProvRelation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvRelation {
#[serde(rename = "prov:entity", skip_serializing_if = "Option::is_none")]
pub entity: Option<String>,
#[serde(rename = "prov:activity", skip_serializing_if = "Option::is_none")]
pub activity: Option<String>,
#[serde(rename = "prov:agent", skip_serializing_if = "Option::is_none")]
pub agent: Option<String>,
#[serde(
rename = "prov:generatedEntity",
skip_serializing_if = "Option::is_none"
)]
pub generated_entity: Option<String>,
#[serde(rename = "prov:usedEntity", skip_serializing_if = "Option::is_none")]
pub used_entity: Option<String>,
}
impl ProvDocument {
pub fn from_membrane(membrane: &GgenMembrane) -> Self {
let mut entity = HashMap::new();
let mut activity = HashMap::new();
let mut agent = HashMap::new();
let mut was_generated_by = HashMap::new();
let mut used = HashMap::new();
let mut was_associated_with = HashMap::new();
let mut was_attributed_to = HashMap::new();
let mut was_derived_from = HashMap::new();
let mut ggen_attrs = HashMap::new();
ggen_attrs.insert("prov:type".to_string(), "SoftwareAgent".to_string());
ggen_attrs.insert(
"prov:label".to_string(),
"ggen Membrane Adapter Layer".to_string(),
);
agent.insert("agent:ggen".to_string(), ggen_attrs);
let mut genesis_attrs = HashMap::new();
genesis_attrs.insert("prov:type".to_string(), "SoftwareAgent".to_string());
genesis_attrs.insert(
"prov:label".to_string(),
"Genesis Embedded Core".to_string(),
);
agent.insert("agent:genesis-core".to_string(), genesis_attrs);
for (part_id, part) in &membrane.core.parts {
let mut part_attrs = HashMap::new();
part_attrs.insert("prov:type".to_string(), "interchangeable-part".to_string());
part_attrs.insert("ggen:part_type".to_string(), part.part_type.clone());
part_attrs.insert("ggen:version".to_string(), part.version.clone());
part_attrs.insert("ggen:payload_hash".to_string(), part.payload_hash.clone());
entity.insert(format!("entity:part:{}", part_id), part_attrs);
let attr_id = format!("attr:part:{}", part_id);
was_attributed_to.insert(
attr_id,
ProvRelation {
entity: Some(format!("entity:part:{}", part_id)),
activity: None,
agent: Some("agent:genesis-core".to_string()),
generated_entity: None,
used_entity: None,
},
);
}
for (idx, crossing) in membrane.event_log.iter().enumerate() {
let act_id = format!("activity:crossing:{}", crossing.id);
let input_ent_id = format!("entity:input:{}", crossing.id);
let output_ent_id = format!("entity:output:{}", crossing.id);
let mut act_attrs = HashMap::new();
act_attrs.insert("prov:type".to_string(), "boundary-crossing".to_string());
act_attrs.insert(
"ggen:interface_fn".to_string(),
crossing.interface_fn.clone(),
);
act_attrs.insert(
"ggen:timestamp".to_string(),
crossing.timestamp.to_rfc3339(),
);
act_attrs.insert(
"ggen:duration_us".to_string(),
crossing.duration_us.to_string(),
);
activity.insert(act_id.clone(), act_attrs);
let mut in_attrs = HashMap::new();
in_attrs.insert("prov:type".to_string(), "input-payload".to_string());
in_attrs.insert("ggen:hash".to_string(), crossing.input_hash.clone());
entity.insert(input_ent_id.clone(), in_attrs);
if let Some(ref oh) = crossing.output_hash {
let mut out_attrs = HashMap::new();
out_attrs.insert("prov:type".to_string(), "output-payload".to_string());
out_attrs.insert("ggen:hash".to_string(), oh.clone());
out_attrs.insert(
"ggen:status_code".to_string(),
crossing.status_code.to_string(),
);
entity.insert(output_ent_id.clone(), out_attrs);
let gen_id = format!("gen:output:{}", crossing.id);
was_generated_by.insert(
gen_id,
ProvRelation {
entity: Some(output_ent_id.clone()),
activity: Some(act_id.clone()),
agent: None,
generated_entity: None,
used_entity: None,
},
);
let deriv_id = format!("deriv:out-from-in:{}", crossing.id);
was_derived_from.insert(
deriv_id,
ProvRelation {
entity: None,
activity: None,
agent: None,
generated_entity: Some(output_ent_id.clone()),
used_entity: Some(input_ent_id.clone()),
},
);
}
let use_id = format!("use:input:{}", crossing.id);
used.insert(
use_id,
ProvRelation {
entity: Some(input_ent_id.clone()),
activity: Some(act_id.clone()),
agent: None,
generated_entity: None,
used_entity: None,
},
);
let assoc_id = format!("assoc:ggen:{}", crossing.id);
was_associated_with.insert(
assoc_id,
ProvRelation {
entity: None,
activity: Some(act_id.clone()),
agent: Some("agent:ggen".to_string()),
generated_entity: None,
used_entity: None,
},
);
let part_use_id = format!("use:part:{}:{}", crossing.callee_id, idx);
used.insert(
part_use_id,
ProvRelation {
entity: Some(format!("entity:part:{}", crossing.callee_id)),
activity: Some(act_id.clone()),
agent: None,
generated_entity: None,
used_entity: None,
},
);
}
Self {
entity,
activity,
agent,
was_generated_by,
used,
was_associated_with,
was_attributed_to,
was_derived_from,
}
}
pub fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(self)?)
}
}