2025-07-10 15:02:14 +02:00
|
|
|
//! Definitions for generating graphs from a simulation.
|
|
|
|
|
|
2025-07-07 22:45:02 +02:00
|
|
|
use petgraph::{Graph, Directed};
|
2025-07-07 01:25:38 +02:00
|
|
|
use std::rc::Rc;
|
2025-07-05 14:54:43 +02:00
|
|
|
|
2025-08-23 23:40:19 +02:00
|
|
|
use super::label::Label;
|
|
|
|
|
use super::set::Set;
|
|
|
|
|
use super::system::System;
|
|
|
|
|
use super::translator::{self, IdType};
|
2025-07-16 00:05:14 +02:00
|
|
|
|
2025-08-23 23:40:19 +02:00
|
|
|
pub type SystemGraph = Graph<System, Label, Directed, u32>;
|
2025-07-16 16:20:29 +02:00
|
|
|
|
2025-08-23 23:40:19 +02:00
|
|
|
fn common_system_entities(graph: &SystemGraph) -> Set {
|
2025-07-16 16:20:29 +02:00
|
|
|
graph.node_references().fold(
|
|
|
|
|
None,
|
|
|
|
|
|acc, node|
|
|
|
|
|
match acc {
|
|
|
|
|
None => Some(node.1.available_entities.clone()),
|
|
|
|
|
Some(acc) => Some(node.1.available_entities.intersection(&acc))
|
|
|
|
|
}
|
2025-08-23 23:40:19 +02:00
|
|
|
).unwrap_or(Set::new())
|
2025-07-16 16:20:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-27 19:08:27 +02:00
|
|
|
macro_rules! common_label {
|
|
|
|
|
(
|
|
|
|
|
$name:ident,
|
|
|
|
|
[$edge_name:ident, $acc_name:ident],
|
|
|
|
|
$empty_expr:expr,
|
|
|
|
|
$some_expr:expr
|
|
|
|
|
) => {
|
2025-08-23 23:40:19 +02:00
|
|
|
fn $name(graph: &SystemGraph) -> Set {
|
2025-07-27 19:08:27 +02:00
|
|
|
graph.edge_references().fold(
|
|
|
|
|
None,
|
|
|
|
|
|$acc_name, $edge_name| {
|
|
|
|
|
let $edge_name = $edge_name.weight();
|
|
|
|
|
match $acc_name {
|
|
|
|
|
None => Some($empty_expr),
|
|
|
|
|
Some($acc_name) => Some($some_expr)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-23 23:40:19 +02:00
|
|
|
).unwrap_or(Set::new())
|
2025-07-27 19:08:27 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_products,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.products.clone(),
|
|
|
|
|
edge.products.intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_entities,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.available_entities.clone(),
|
|
|
|
|
edge.available_entities.intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_context,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.context.clone(),
|
|
|
|
|
edge.context.intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_union,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.t.clone(),
|
|
|
|
|
edge.t.intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_difference,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.context.subtraction(&edge.available_entities),
|
|
|
|
|
edge.context.subtraction(&edge.available_entities).intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_entities_deleted,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.available_entities.subtraction(&edge.products),
|
|
|
|
|
edge.available_entities.subtraction(&edge.products).intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
common_label!(
|
|
|
|
|
common_label_entities_added,
|
|
|
|
|
[edge, acc],
|
|
|
|
|
edge.products.subtraction(&edge.available_entities),
|
|
|
|
|
edge.products.subtraction(&edge.available_entities).intersection(&acc)
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-07 01:25:38 +02:00
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// helper functions
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
2025-08-15 00:32:58 +02:00
|
|
|
/// Very inelegant way to provide our graph with a map method where the edges
|
|
|
|
|
/// are mapped until the first error.
|
|
|
|
|
pub trait MapEdges<'a, N: 'a, E, Ty, Ix>
|
|
|
|
|
where
|
|
|
|
|
Ty: petgraph::EdgeType,
|
|
|
|
|
Ix: petgraph::graph::IndexType
|
|
|
|
|
{
|
|
|
|
|
fn map_edges(
|
|
|
|
|
&self,
|
2025-08-24 03:35:32 +02:00
|
|
|
edge_map: &super::assert::types::Assert,
|
2025-08-15 00:32:58 +02:00
|
|
|
translator: &mut super::translator::Translator
|
2025-08-23 23:40:19 +02:00
|
|
|
) -> Result<
|
|
|
|
|
Graph<System,
|
2025-08-24 03:35:32 +02:00
|
|
|
super::assert::types::AssertReturnValue, Ty, Ix>
|
2025-08-23 23:40:19 +02:00
|
|
|
, String>;
|
2025-08-15 00:32:58 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-23 23:40:19 +02:00
|
|
|
impl<'a> MapEdges<'a, System, Label, Directed, u32>
|
|
|
|
|
for SystemGraph
|
2025-08-15 00:32:58 +02:00
|
|
|
{
|
|
|
|
|
fn map_edges(
|
|
|
|
|
&self,
|
2025-08-24 03:35:32 +02:00
|
|
|
edge_map: &super::assert::types::Assert,
|
2025-08-15 00:32:58 +02:00
|
|
|
translator: &mut super::translator::Translator
|
2025-08-24 03:35:32 +02:00
|
|
|
)-> Result<Graph<System, super::assert::types::AssertReturnValue,
|
|
|
|
|
Directed, u32>
|
|
|
|
|
, String> {
|
2025-08-15 00:32:58 +02:00
|
|
|
use petgraph::graph::EdgeIndex;
|
|
|
|
|
|
|
|
|
|
let mut g = Graph::with_capacity(self.node_count(), self.edge_count());
|
|
|
|
|
let nodes = self.raw_nodes();
|
|
|
|
|
let edges = self.raw_edges();
|
|
|
|
|
|
|
|
|
|
let edges = edges.iter().enumerate().map(
|
|
|
|
|
|(i, edge)|
|
|
|
|
|
match edge_map.execute(self, &EdgeIndex::new(i), translator) {
|
|
|
|
|
Err(e) => Err(e),
|
|
|
|
|
Ok(val) => Ok((edge.source(), edge.target(), val))
|
|
|
|
|
}
|
|
|
|
|
).collect::<Result<Vec<_>, _>>()?;
|
|
|
|
|
nodes.iter().for_each(|node| { g.add_node(node.weight.clone()); });
|
|
|
|
|
|
|
|
|
|
edges.into_iter().for_each(|(source, target, v)| { g.add_edge(source, target, v); });
|
|
|
|
|
|
|
|
|
|
Ok(g)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-07 01:25:38 +02:00
|
|
|
|
|
|
|
|
// Nodes -----------------------------------------------------------------------
|
|
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
/// Helper structure that specifies what information to display for nodes.
|
2025-07-12 15:32:21 +02:00
|
|
|
#[derive(Clone)]
|
2025-07-26 22:46:10 +02:00
|
|
|
pub enum NodeDisplayBase {
|
2025-07-26 19:43:20 +02:00
|
|
|
String { string: String },
|
2025-07-07 01:25:38 +02:00
|
|
|
Hide,
|
|
|
|
|
Entities,
|
2025-08-23 23:40:19 +02:00
|
|
|
MaskEntities { mask: Set },
|
|
|
|
|
ExcludeEntities { mask: Set },
|
2025-07-07 01:25:38 +02:00
|
|
|
Context,
|
2025-07-26 22:46:10 +02:00
|
|
|
UncommonEntities,
|
2025-08-23 23:40:19 +02:00
|
|
|
MaskUncommonEntities { mask: Set }
|
2025-07-26 22:46:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct NodeDisplay {
|
|
|
|
|
pub base: Vec<NodeDisplayBase>
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:22:30 +02:00
|
|
|
type GraphMapNodesFnTy<'a> =
|
2025-08-23 23:40:19 +02:00
|
|
|
dyn Fn(petgraph::prelude::NodeIndex, &'a System) -> String + 'a;
|
2025-07-07 01:25:38 +02:00
|
|
|
|
2025-07-26 19:43:20 +02:00
|
|
|
|
2025-07-26 22:46:10 +02:00
|
|
|
fn match_node_display<'a>(
|
|
|
|
|
base: &NodeDisplayBase,
|
2025-08-23 23:40:19 +02:00
|
|
|
common_entities: Rc<Set>,
|
2025-07-26 22:46:10 +02:00
|
|
|
translator: Rc<translator::Translator>
|
|
|
|
|
) -> Box<GraphMapNodesFnTy<'a>> {
|
|
|
|
|
use NodeDisplayBase::*;
|
|
|
|
|
use super::format_helpers::graph_map_nodes_ty_from::*;
|
|
|
|
|
|
|
|
|
|
match base {
|
|
|
|
|
String { string } => {
|
|
|
|
|
format_string(string.clone())
|
|
|
|
|
},
|
|
|
|
|
Hide => {
|
|
|
|
|
format_hide(translator)
|
|
|
|
|
},
|
|
|
|
|
Entities => {
|
|
|
|
|
format_entities(translator)
|
|
|
|
|
},
|
|
|
|
|
MaskEntities { mask } => {
|
|
|
|
|
format_mask_entities(translator, mask.clone())
|
|
|
|
|
},
|
|
|
|
|
ExcludeEntities { mask } => {
|
|
|
|
|
format_exclude_entities(translator, mask.clone())
|
|
|
|
|
},
|
|
|
|
|
Context => {
|
|
|
|
|
format_context(translator)
|
|
|
|
|
},
|
|
|
|
|
UncommonEntities => {
|
|
|
|
|
format_exclude_entities(translator, (*common_entities).clone())
|
|
|
|
|
},
|
|
|
|
|
MaskUncommonEntities { mask } => {
|
|
|
|
|
format_exclude_entities(translator,
|
|
|
|
|
mask.intersection(&common_entities))
|
|
|
|
|
}
|
2025-07-26 19:43:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 16:46:48 +02:00
|
|
|
|
2025-07-26 22:46:10 +02:00
|
|
|
impl NodeDisplay {
|
|
|
|
|
fn contains_uncommon(&self) -> bool {
|
|
|
|
|
self.base.iter().any(
|
|
|
|
|
|b|
|
|
|
|
|
matches!(b, NodeDisplayBase::UncommonEntities |
|
|
|
|
|
NodeDisplayBase::MaskUncommonEntities { mask: _ }))
|
|
|
|
|
}
|
2025-07-26 19:43:20 +02:00
|
|
|
|
2025-07-26 22:46:10 +02:00
|
|
|
pub fn generate<'a>(
|
|
|
|
|
self,
|
|
|
|
|
translator: Rc<translator::Translator>,
|
2025-08-23 23:40:19 +02:00
|
|
|
current_graph: &SystemGraph
|
2025-07-26 22:46:10 +02:00
|
|
|
) -> Box<GraphMapNodesFnTy<'a>> {
|
|
|
|
|
let common_entities =
|
|
|
|
|
if self.contains_uncommon() {
|
2025-07-27 19:08:27 +02:00
|
|
|
Rc::new(common_system_entities(current_graph))
|
2025-07-26 22:46:10 +02:00
|
|
|
} else {
|
2025-08-23 23:40:19 +02:00
|
|
|
Rc::new(Set::new())
|
2025-07-07 01:25:38 +02:00
|
|
|
};
|
|
|
|
|
|
2025-07-26 22:46:10 +02:00
|
|
|
Box::new(
|
|
|
|
|
move |i, n| {
|
|
|
|
|
let mut accumulator = String::new();
|
|
|
|
|
for b in &self.base {
|
|
|
|
|
let f = match_node_display(b,
|
|
|
|
|
Rc::clone(&common_entities),
|
|
|
|
|
Rc::clone(&translator));
|
2025-07-07 01:25:38 +02:00
|
|
|
|
2025-07-26 22:46:10 +02:00
|
|
|
accumulator.push_str(&(f)(i, n));
|
|
|
|
|
}
|
|
|
|
|
accumulator
|
|
|
|
|
}
|
|
|
|
|
)
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 19:43:20 +02:00
|
|
|
|
2025-07-07 01:25:38 +02:00
|
|
|
// Edges -----------------------------------------------------------------------
|
|
|
|
|
|
2025-07-12 15:32:21 +02:00
|
|
|
#[derive(Clone)]
|
2025-07-27 14:59:35 +02:00
|
|
|
pub enum EdgeDisplayBase {
|
2025-07-26 20:01:46 +02:00
|
|
|
String { string: String },
|
2025-07-07 01:25:38 +02:00
|
|
|
Hide,
|
2025-08-23 23:40:19 +02:00
|
|
|
Products { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
Entities { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
Context { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
Union { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
Difference { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
EntitiesDeleted { mask: Option<Set>, filter_common: bool },
|
|
|
|
|
EntitiesAdded { mask: Option<Set>, filter_common: bool },
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
pub struct EdgeDisplay {
|
2025-07-27 19:08:27 +02:00
|
|
|
pub base: Vec<EdgeDisplayBase>,
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
type GraphMapEdgesFnTy<'a> =
|
2025-08-23 23:40:19 +02:00
|
|
|
dyn Fn(petgraph::prelude::EdgeIndex, &'a Label) -> String + 'a;
|
2025-07-26 20:16:32 +02:00
|
|
|
|
2025-07-27 19:08:27 +02:00
|
|
|
#[derive(Default, Clone)]
|
|
|
|
|
struct CommonEntities {
|
2025-08-23 23:40:19 +02:00
|
|
|
common_products: Set,
|
|
|
|
|
common_entities: Set,
|
|
|
|
|
common_context: Set,
|
|
|
|
|
common_union: Set,
|
|
|
|
|
common_difference: Set,
|
|
|
|
|
common_entities_deleted: Set,
|
|
|
|
|
common_entities_added: Set,
|
2025-07-27 19:08:27 +02:00
|
|
|
}
|
2025-07-26 16:46:48 +02:00
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
fn match_edge_display<'a>(
|
|
|
|
|
base: &'a EdgeDisplayBase,
|
2025-07-27 19:08:27 +02:00
|
|
|
translator: Rc<translator::Translator>,
|
|
|
|
|
common: CommonEntities
|
2025-07-27 14:59:35 +02:00
|
|
|
) -> Box<GraphMapEdgesFnTy<'a>> {
|
|
|
|
|
use EdgeDisplayBase::*;
|
|
|
|
|
use super::format_helpers::graph_map_edges_ty_from::*;
|
2025-07-26 20:16:32 +02:00
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
match base {
|
|
|
|
|
String { string } => {
|
|
|
|
|
format_string(translator, string.clone())
|
2025-07-26 20:16:32 +02:00
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
Hide => {
|
|
|
|
|
format_hide(translator)
|
|
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
Products { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_products(translator, mask.clone(),
|
|
|
|
|
Some(common.common_products))
|
|
|
|
|
} else {
|
|
|
|
|
format_products(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
Entities { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_entities(translator, mask.clone(),
|
|
|
|
|
Some(common.common_entities))
|
|
|
|
|
} else {
|
|
|
|
|
format_entities(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
Context { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_context(translator, mask.clone(),
|
|
|
|
|
Some(common.common_context))
|
|
|
|
|
} else {
|
|
|
|
|
format_context(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
Union { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_union(translator, mask.clone(),
|
|
|
|
|
Some(common.common_union))
|
|
|
|
|
} else {
|
|
|
|
|
format_union(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
Difference { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_difference(translator, mask.clone(),
|
|
|
|
|
Some(common.common_difference))
|
|
|
|
|
} else {
|
|
|
|
|
format_difference(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
EntitiesDeleted { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_entities_deleted(translator, mask.clone(),
|
|
|
|
|
Some(common.common_entities_deleted))
|
|
|
|
|
} else {
|
|
|
|
|
format_entities_deleted(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-27 19:08:27 +02:00
|
|
|
EntitiesAdded { mask, filter_common } => {
|
|
|
|
|
if *filter_common {
|
|
|
|
|
format_entities_added(translator, mask.clone(),
|
|
|
|
|
Some(common.common_entities_added))
|
|
|
|
|
} else {
|
|
|
|
|
format_entities_added(translator, mask.clone(), None)
|
|
|
|
|
}
|
2025-07-27 14:59:35 +02:00
|
|
|
},
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
2025-07-26 20:16:32 +02:00
|
|
|
}
|
2025-07-07 01:25:38 +02:00
|
|
|
|
2025-07-27 19:08:27 +02:00
|
|
|
macro_rules! common_entity {
|
|
|
|
|
($name:ident, $match:pat, $filter_common:ident) => {
|
|
|
|
|
fn $name(&self) -> bool {
|
|
|
|
|
self.base.iter().any(
|
|
|
|
|
|b|
|
|
|
|
|
if let $match = b {
|
|
|
|
|
*$filter_common
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 20:16:32 +02:00
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
impl EdgeDisplay {
|
2025-07-27 19:08:27 +02:00
|
|
|
common_entity!(common_products,
|
|
|
|
|
EdgeDisplayBase::Products {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_entities,
|
|
|
|
|
EdgeDisplayBase::Entities {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_context,
|
|
|
|
|
EdgeDisplayBase::Context {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_union,
|
|
|
|
|
EdgeDisplayBase::Union {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_difference,
|
|
|
|
|
EdgeDisplayBase::Difference {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_entities_deleted,
|
|
|
|
|
EdgeDisplayBase::EntitiesDeleted {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
common_entity!(common_entities_added,
|
|
|
|
|
EdgeDisplayBase::EntitiesAdded {mask: _, filter_common},
|
|
|
|
|
filter_common);
|
|
|
|
|
|
|
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
pub fn generate<'a>(
|
|
|
|
|
self,
|
|
|
|
|
translator: Rc<translator::Translator>,
|
2025-08-23 23:40:19 +02:00
|
|
|
current_graph: &SystemGraph
|
2025-07-27 14:59:35 +02:00
|
|
|
) -> Box<GraphMapEdgesFnTy<'a>> {
|
2025-07-27 19:08:27 +02:00
|
|
|
// create the structure for common entities if required
|
|
|
|
|
let common = {
|
|
|
|
|
let mut tmp = CommonEntities::default();
|
|
|
|
|
if self.common_products() {
|
|
|
|
|
tmp.common_products = common_label_products(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_entities() {
|
|
|
|
|
tmp.common_entities = common_label_entities(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_context() {
|
|
|
|
|
tmp.common_context = common_label_context(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_union() {
|
|
|
|
|
tmp.common_union = common_label_union(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_difference() {
|
|
|
|
|
tmp.common_difference = common_label_difference(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_entities_deleted() {
|
|
|
|
|
tmp.common_entities_deleted = common_label_entities_deleted(current_graph);
|
|
|
|
|
}
|
|
|
|
|
if self.common_entities_added() {
|
|
|
|
|
tmp.common_entities_added = common_label_entities_added(current_graph);
|
|
|
|
|
}
|
|
|
|
|
tmp
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-27 14:59:35 +02:00
|
|
|
Box::new(
|
|
|
|
|
move |i, n| {
|
|
|
|
|
let mut accumulator = String::new();
|
|
|
|
|
for b in &self.base {
|
|
|
|
|
let f = match_edge_display(b,
|
2025-07-27 19:08:27 +02:00
|
|
|
Rc::clone(&translator),
|
|
|
|
|
common.clone());
|
2025-07-27 14:59:35 +02:00
|
|
|
accumulator.push_str(&(f)(i, n));
|
|
|
|
|
}
|
|
|
|
|
accumulator
|
|
|
|
|
}
|
|
|
|
|
)
|
2025-07-07 01:25:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-07 22:45:02 +02:00
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
2025-07-26 20:01:46 +02:00
|
|
|
// Color Nodes & Edges
|
2025-07-07 22:45:02 +02:00
|
|
|
// -----------------------------------------------------------------------------
|
2025-07-26 21:36:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// Node ------------------------------------------------------------------------
|
2025-07-26 16:46:48 +02:00
|
|
|
use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences};
|
2025-07-07 22:45:02 +02:00
|
|
|
|
|
|
|
|
type RSdotGraph = Graph<String, String, Directed, u32>;
|
2025-07-26 21:22:30 +02:00
|
|
|
type RSformatNodeTy<'a> =
|
2025-07-13 17:28:13 +02:00
|
|
|
dyn Fn(
|
2025-07-26 21:22:30 +02:00
|
|
|
&'a RSdotGraph,
|
|
|
|
|
<&'a RSdotGraph as IntoNodeReferences>::NodeRef
|
|
|
|
|
) -> String + 'a;
|
|
|
|
|
type RSformatNodeTyOpt<'a> =
|
|
|
|
|
dyn Fn(
|
|
|
|
|
&'a RSdotGraph,
|
|
|
|
|
<&'a RSdotGraph as IntoNodeReferences>::NodeRef
|
|
|
|
|
) -> Option<String> + 'a;
|
2025-07-07 22:45:02 +02:00
|
|
|
|
2025-07-13 17:28:13 +02:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
|
pub enum OperationType {
|
|
|
|
|
Equals,
|
|
|
|
|
Subset,
|
|
|
|
|
SubsetEqual,
|
|
|
|
|
Superset,
|
|
|
|
|
SupersetEqual
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl OperationType {
|
2025-08-23 23:40:19 +02:00
|
|
|
pub fn evaluate(&self, a: &Set, b: &Set) -> bool {
|
2025-07-13 17:28:13 +02:00
|
|
|
match self {
|
|
|
|
|
Self::Equals => {
|
|
|
|
|
a.is_subset(b) && b.is_subset(a)
|
|
|
|
|
},
|
|
|
|
|
Self::Subset => {
|
|
|
|
|
a.is_subset(b) && !b.is_subset(a)
|
|
|
|
|
},
|
|
|
|
|
Self::SubsetEqual => {
|
|
|
|
|
a.is_subset(b)
|
|
|
|
|
},
|
|
|
|
|
Self::Superset => {
|
|
|
|
|
b.is_subset(a) && !a.is_subset(b)
|
|
|
|
|
},
|
|
|
|
|
Self::SupersetEqual => {
|
|
|
|
|
b.is_subset(a)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub enum ContextColorConditional {
|
|
|
|
|
Nill,
|
|
|
|
|
RecursiveIdentifier(IdType),
|
2025-08-23 23:40:19 +02:00
|
|
|
EntitySet(OperationType, Set),
|
2025-07-13 17:28:13 +02:00
|
|
|
NonDeterministicChoice,
|
|
|
|
|
Summation,
|
|
|
|
|
WaitEntity
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub enum NodeColorConditional {
|
|
|
|
|
ContextConditional(ContextColorConditional),
|
2025-08-23 23:40:19 +02:00
|
|
|
EntitiesConditional(OperationType, Set)
|
2025-07-13 17:28:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct NodeColor {
|
|
|
|
|
pub conditionals: Vec<(NodeColorConditional, String)>,
|
2025-07-26 21:22:30 +02:00
|
|
|
pub base_color: String,
|
2025-07-13 17:28:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:22:30 +02:00
|
|
|
#[inline(always)]
|
2025-07-26 21:36:56 +02:00
|
|
|
fn node_formatter_base_color(
|
2025-07-13 18:14:35 +02:00
|
|
|
base_color: String
|
|
|
|
|
) -> String
|
|
|
|
|
{
|
|
|
|
|
", fillcolor=".to_string() + &base_color
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:22:30 +02:00
|
|
|
#[inline(always)]
|
|
|
|
|
fn match_node_color_conditional<'a>(
|
2025-07-26 21:36:56 +02:00
|
|
|
rule: &'a NodeColorConditional,
|
|
|
|
|
color: &'a String,
|
2025-08-23 23:40:19 +02:00
|
|
|
original_graph: Rc<SystemGraph>,
|
2025-07-26 21:22:30 +02:00
|
|
|
star: Option<IdType>
|
|
|
|
|
) -> Box<RSformatNodeTyOpt<'a>> {
|
2025-07-26 16:46:48 +02:00
|
|
|
use super::format_helpers::node_formatter::*;
|
2025-07-13 17:28:13 +02:00
|
|
|
match rule {
|
|
|
|
|
NodeColorConditional::ContextConditional(ccc) => {
|
|
|
|
|
match ccc {
|
|
|
|
|
ContextColorConditional::Nill => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_nill(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star)
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
ContextColorConditional::RecursiveIdentifier(s) => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_recursive_identifier(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star,
|
|
|
|
|
*s)
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
ContextColorConditional::EntitySet(ot, set) => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_entity_set(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star,
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
ContextColorConditional::NonDeterministicChoice => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_non_deterministic_choice(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star)
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
ContextColorConditional::Summation => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_summation(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star)
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
ContextColorConditional::WaitEntity => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_wait_entity(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star)
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
2025-07-07 22:45:02 +02:00
|
|
|
}
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
NodeColorConditional::EntitiesConditional(ot, set) => {
|
2025-07-26 21:22:30 +02:00
|
|
|
format_entities_conditional(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
star,
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 17:28:13 +02:00
|
|
|
},
|
|
|
|
|
}
|
2025-07-07 22:45:02 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:22:30 +02:00
|
|
|
impl NodeColor {
|
|
|
|
|
pub fn generate<'a>(
|
|
|
|
|
self,
|
2025-08-23 23:40:19 +02:00
|
|
|
original_graph: Rc<SystemGraph>,
|
2025-07-26 21:22:30 +02:00
|
|
|
star: Option<IdType>
|
|
|
|
|
) -> Box<RSformatNodeTy<'a>> {
|
|
|
|
|
Box::new(
|
|
|
|
|
move |i, n| {
|
|
|
|
|
for (rule, color) in &self.conditionals {
|
|
|
|
|
let f = match_node_color_conditional(
|
|
|
|
|
rule,
|
|
|
|
|
color,
|
|
|
|
|
Rc::clone(&original_graph),
|
|
|
|
|
star
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Some(s) = (f)(i, n) {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
node_formatter_base_color(self.base_color.clone())
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-07-26 21:36:56 +02:00
|
|
|
// Edge ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
type RSformatEdgeTy<'a> =
|
2025-07-13 19:08:39 +02:00
|
|
|
dyn Fn(
|
2025-07-26 21:36:56 +02:00
|
|
|
&'a RSdotGraph,
|
|
|
|
|
<&'a RSdotGraph as IntoEdgeReferences>::EdgeRef
|
|
|
|
|
) -> String + 'a;
|
|
|
|
|
type RSformatEdgeTyOpt<'a> =
|
|
|
|
|
dyn Fn(
|
|
|
|
|
&'a RSdotGraph,
|
|
|
|
|
<&'a RSdotGraph as IntoEdgeReferences>::EdgeRef
|
|
|
|
|
) -> Option<String> + 'a;
|
|
|
|
|
|
2025-07-13 19:08:39 +02:00
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub enum EdgeColorConditional {
|
2025-08-23 23:40:19 +02:00
|
|
|
Entities(OperationType, Set),
|
|
|
|
|
Context(OperationType, Set),
|
|
|
|
|
T(OperationType, Set),
|
|
|
|
|
Reactants(OperationType, Set),
|
|
|
|
|
ReactantsAbsent(OperationType, Set),
|
|
|
|
|
Inhibitors(OperationType, Set),
|
|
|
|
|
InhibitorsPresent(OperationType, Set),
|
|
|
|
|
Products(OperationType, Set),
|
2025-07-13 19:08:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct EdgeColor {
|
|
|
|
|
pub conditionals: Vec<(EdgeColorConditional, String)>,
|
|
|
|
|
pub base_color: String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-07-26 21:36:56 +02:00
|
|
|
fn edge_formatter_base_color(
|
2025-07-13 19:08:39 +02:00
|
|
|
base_color: String
|
|
|
|
|
) -> String
|
|
|
|
|
{
|
2025-07-13 19:32:11 +02:00
|
|
|
", color=".to_string() + &base_color
|
2025-07-13 19:08:39 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:36:56 +02:00
|
|
|
fn match_edge_color_conditional<'a>(
|
|
|
|
|
rule: &'a EdgeColorConditional,
|
|
|
|
|
color: &'a String,
|
2025-08-23 23:40:19 +02:00
|
|
|
original_graph: Rc<SystemGraph>
|
2025-07-26 21:36:56 +02:00
|
|
|
) -> Box<RSformatEdgeTyOpt<'a>> {
|
2025-07-26 16:46:48 +02:00
|
|
|
use super::format_helpers::edge_formatter::*;
|
2025-07-13 19:08:39 +02:00
|
|
|
match rule {
|
|
|
|
|
EdgeColorConditional::Entities(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_entities(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::Context(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_context(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::T(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_t(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::Reactants(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_reactants(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::ReactantsAbsent(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_reactants_absent(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::Inhibitors(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_inhibitors(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::InhibitorsPresent(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_inhibitors_present(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
EdgeColorConditional::Products(ot, set) => {
|
2025-07-26 21:36:56 +02:00
|
|
|
format_products(Rc::clone(&original_graph),
|
|
|
|
|
color.to_string(),
|
|
|
|
|
*ot,
|
|
|
|
|
set.clone())
|
2025-07-13 19:08:39 +02:00
|
|
|
},
|
|
|
|
|
}
|
2025-07-07 22:45:02 +02:00
|
|
|
}
|
2025-07-26 21:36:56 +02:00
|
|
|
|
|
|
|
|
impl EdgeColor {
|
|
|
|
|
pub fn generate<'a>(
|
|
|
|
|
self,
|
2025-08-23 23:40:19 +02:00
|
|
|
original_graph: Rc<SystemGraph>,
|
2025-07-26 21:36:56 +02:00
|
|
|
) -> Box<RSformatEdgeTy<'a>> {
|
|
|
|
|
Box::new(
|
|
|
|
|
move |i, n| {
|
|
|
|
|
for (rule, color) in &self.conditionals {
|
|
|
|
|
let f = match_edge_color_conditional(
|
|
|
|
|
rule,
|
|
|
|
|
color,
|
|
|
|
|
Rc::clone(&original_graph),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Some(s) = (f)(i, n) {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
edge_formatter_base_color(self.base_color.clone())
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|