use indexmap::IndexMap;
use log::debug;
use orrery_core::{identifier::Id, semantic};
use crate::RenderError;
#[derive(Debug)]
pub enum SequenceEvent<'a> {
Relation(&'a semantic::Relation),
Activate(&'a semantic::Activate),
Deactivate(Id),
FragmentStart(&'a semantic::Fragment),
FragmentSectionStart(&'a semantic::FragmentSection),
FragmentSectionEnd,
FragmentEnd,
Note(&'a semantic::Note),
}
#[derive(Debug)]
pub struct SequenceGraph<'a> {
nodes: IndexMap<Id, &'a semantic::Node>,
events: Vec<SequenceEvent<'a>>,
}
impl<'a> SequenceGraph<'a> {
pub fn events(&self) -> impl Iterator<Item = &SequenceEvent<'a>> {
self.events.iter()
}
pub fn relations(&self) -> impl Iterator<Item = &semantic::Relation> {
self.events().filter_map(|event| match event {
SequenceEvent::Relation(relation) => Some(*relation),
_ => None,
})
}
pub fn nodes(&self) -> impl Iterator<Item = &semantic::Node> {
self.nodes.values().cloned()
}
pub fn node_ids(&self) -> impl Iterator<Item = &Id> {
self.nodes.keys()
}
pub(super) fn new_from_elements<'idx>(
elements: &'a [semantic::Element],
) -> Result<(Self, Vec<super::HierarchyNode<'a, 'idx>>), RenderError> {
let mut graph = Self::new();
let child_diagrams = Self::process_elements(elements, &mut graph)?;
Ok((graph, child_diagrams))
}
fn new() -> Self {
Self {
nodes: IndexMap::new(),
events: Vec::new(),
}
}
fn add_node(&mut self, node: &'a semantic::Node) {
self.nodes.insert(node.id(), node);
}
fn add_event(&mut self, event: SequenceEvent<'a>) {
self.events.push(event);
}
fn process_elements<'idx>(
elements: &'a [semantic::Element],
graph: &mut SequenceGraph<'a>,
) -> Result<Vec<super::HierarchyNode<'a, 'idx>>, RenderError> {
let mut child_diagrams = Vec::new();
for element in elements {
match element {
semantic::Element::Node(node) => {
graph.add_node(node);
match node.block() {
semantic::Block::Diagram(inner_diagram) => {
debug!(
"Processing nested diagram of kind {:?}",
inner_diagram.kind()
);
let inner_hierarchy_child =
super::HierarchyNode::build_from_ast_diagram(
inner_diagram,
Some(node.id()),
)?;
child_diagrams.push(inner_hierarchy_child);
}
semantic::Block::None => {}
semantic::Block::Scope(..) => {
unreachable!("Unexpected scope block in sequence diagram")
}
}
}
semantic::Element::Relation(relation) => {
graph.add_event(SequenceEvent::Relation(relation));
}
semantic::Element::Activate(activate) => {
graph.add_event(SequenceEvent::Activate(activate));
}
semantic::Element::Deactivate(id) => {
graph.add_event(SequenceEvent::Deactivate(*id));
}
semantic::Element::Fragment(fragment) => {
graph.add_event(SequenceEvent::FragmentStart(fragment));
for section in fragment.sections() {
graph.add_event(SequenceEvent::FragmentSectionStart(section));
let mut section_child_diagrams =
Self::process_elements(section.elements(), graph)?;
child_diagrams.append(&mut section_child_diagrams);
graph.add_event(SequenceEvent::FragmentSectionEnd);
}
graph.add_event(SequenceEvent::FragmentEnd);
}
semantic::Element::Note(note) => {
graph.add_event(SequenceEvent::Note(note));
}
}
}
Ok(child_diagrams)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::rc::Rc;
use orrery_core::{
draw,
semantic::{Block, Node},
};
fn create_test_node(id_str: &str) -> Node {
let id = Id::new(id_str);
let shape_def =
Rc::new(Box::new(draw::RectangleDefinition::new()) as Box<dyn draw::ShapeDefinition>);
Node::new(id, None, Block::None, shape_def)
}
#[test]
fn test_participant_ordering_preserved() {
let zara = create_test_node("Zara");
let alice = create_test_node("Alice");
let mike = create_test_node("Mike");
let bob = create_test_node("Bob");
let eve = create_test_node("Eve");
let mut graph = SequenceGraph::new();
graph.add_node(&zara);
graph.add_node(&alice);
graph.add_node(&mike);
graph.add_node(&bob);
graph.add_node(&eve);
let node_ids: Vec<Id> = graph.node_ids().copied().collect();
assert_eq!(node_ids.len(), 5, "Should have 5 participants");
assert_eq!(
node_ids[0],
Id::new("Zara"),
"First participant should be Zara"
);
assert_eq!(
node_ids[1],
Id::new("Alice"),
"Second participant should be Alice"
);
assert_eq!(
node_ids[2],
Id::new("Mike"),
"Third participant should be Mike"
);
assert_eq!(
node_ids[3],
Id::new("Bob"),
"Fourth participant should be Bob"
);
assert_eq!(
node_ids[4],
Id::new("Eve"),
"Fifth participant should be Eve"
);
let node_labels: Vec<String> = graph.nodes().map(|n| n.id().to_string()).collect();
assert_eq!(
node_labels[0], "Zara",
"nodes() iterator: first should be Zara"
);
assert_eq!(
node_labels[1], "Alice",
"nodes() iterator: second should be Alice"
);
assert_eq!(
node_labels[2], "Mike",
"nodes() iterator: third should be Mike"
);
assert_eq!(
node_labels[3], "Bob",
"nodes() iterator: fourth should be Bob"
);
assert_eq!(
node_labels[4], "Eve",
"nodes() iterator: fifth should be Eve"
);
}
#[test]
fn test_participant_ordering_with_duplicates() {
let zara = create_test_node("Zara");
let bob = create_test_node("Bob");
let alice = create_test_node("Alice");
let mut graph = SequenceGraph::new();
graph.add_node(&zara);
graph.add_node(&bob);
graph.add_node(&alice);
graph.add_node(&bob);
let node_ids: Vec<Id> = graph.node_ids().copied().collect();
assert_eq!(node_ids.len(), 3, "Should still have 3 participants");
assert_eq!(node_ids[0], "Zara", "Zara should remain first");
assert_eq!(node_ids[1], "Bob", "Bob should remain second");
assert_eq!(node_ids[2], "Alice", "Alice should remain third");
}
}