#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Block {
pub id: String,
pub text: String,
pub col_span: usize,
}
impl Block {
pub fn display_text(&self) -> &str {
if self.text.is_empty() {
&self.id
} else {
&self.text
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockEdge {
pub source: String,
pub target: String,
pub label: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct BlockDiagram {
pub columns: usize,
pub blocks: Vec<Block>,
pub edges: Vec<BlockEdge>,
}
impl BlockDiagram {
pub fn block_count(&self) -> usize {
self.blocks.len()
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
pub fn find_block(&self, id: &str) -> Option<&Block> {
self.blocks.iter().find(|b| b.id == id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_diagram_is_empty() {
let d = BlockDiagram::default();
assert_eq!(d.columns, 0);
assert_eq!(d.block_count(), 0);
assert_eq!(d.edge_count(), 0);
}
#[test]
fn block_display_text_falls_back_to_id() {
let b_with_text = Block {
id: "myid".to_string(),
text: "My Label".to_string(),
col_span: 1,
};
assert_eq!(b_with_text.display_text(), "My Label");
let b_bare = Block {
id: "bare".to_string(),
text: String::new(),
col_span: 1,
};
assert_eq!(b_bare.display_text(), "bare");
}
#[test]
fn find_block_returns_correct_block() {
let diag = BlockDiagram {
columns: 2,
blocks: vec![
Block {
id: "A".to_string(),
text: "Alpha".to_string(),
col_span: 1,
},
Block {
id: "B".to_string(),
text: String::new(),
col_span: 2,
},
],
edges: vec![],
};
let found = diag.find_block("A").expect("block A must exist");
assert_eq!(found.id, "A");
assert_eq!(found.text, "Alpha");
assert!(diag.find_block("Z").is_none(), "Z does not exist");
}
#[test]
fn equality_holds_for_identical_diagrams() {
let a = BlockDiagram {
columns: 3,
blocks: vec![Block {
id: "X".to_string(),
text: "Ex".to_string(),
col_span: 2,
}],
edges: vec![BlockEdge {
source: "X".to_string(),
target: "Y".to_string(),
label: None,
}],
};
let b = a.clone();
assert_eq!(a, b);
let c = BlockDiagram {
columns: 1,
..Default::default()
};
assert_ne!(a, c);
}
#[test]
fn block_edge_label_is_optional() {
let with_label = BlockEdge {
source: "A".to_string(),
target: "B".to_string(),
label: Some("calls".to_string()),
};
let without_label = BlockEdge {
source: "A".to_string(),
target: "B".to_string(),
label: None,
};
assert_ne!(with_label, without_label);
assert!(with_label.label.is_some());
assert!(without_label.label.is_none());
}
}