use alloc::{
borrow::ToOwned,
rc::Rc,
string::{String, ToString},
vec::Vec,
};
use crate::{
diagrams::flowchart::flowchart_node::{ClickEvent, FlowchartNode, shape::FlowchartNodeShape},
errors::NodeError,
shared::{
StyleClass, StyleClassError, generic_configuration::Direction,
generic_node::GenericNodeBuilder,
},
traits::{Node, NodeBuilder},
};
#[derive(Default, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FlowchartNodeBuilder {
builder: GenericNodeBuilder,
click_event: Option<ClickEvent>,
shape: FlowchartNodeShape,
subnodes: Vec<Rc<FlowchartNode>>,
direction: Option<Direction>,
}
impl FlowchartNodeBuilder {
#[must_use]
pub fn click_event(mut self, click_event: ClickEvent) -> Self {
self.click_event = Some(click_event);
self
}
#[must_use]
pub fn shape(mut self, shape: FlowchartNodeShape) -> Self {
self.shape = shape;
self
}
pub fn subnode(mut self, subnode: Rc<FlowchartNode>) -> Result<Self, NodeError> {
if self.subnodes.contains(&subnode) {
return Err(NodeError::DuplicateNode(subnode.label().to_owned()));
}
self.subnodes.push(subnode);
Ok(self)
}
#[must_use]
pub fn is_subgraph(&self) -> bool {
!self.subnodes.is_empty()
}
#[must_use]
pub fn direction(mut self, direction: Direction) -> Self {
self.direction = Some(direction);
self
}
#[must_use]
pub fn get_direction(&self) -> Option<Direction> {
self.direction
}
#[must_use]
pub fn reset_direction(mut self) -> Self {
self.direction = None;
self
}
}
impl TryFrom<FlowchartNodeBuilder> for FlowchartNode {
type Error = NodeError;
fn try_from(mut builder: FlowchartNodeBuilder) -> Result<Self, Self::Error> {
if builder.direction.is_some() && builder.subnodes.is_empty() {
return Err(NodeError::MissingSubnodes);
}
builder.subnodes.sort_unstable();
Ok(FlowchartNode {
node: builder.builder.try_into()?,
click_event: builder.click_event,
shape: builder.shape,
subnodes: builder.subnodes,
direction: builder.direction,
})
}
}
impl NodeBuilder for FlowchartNodeBuilder {
type Node = FlowchartNode;
type Error = NodeError;
fn build(self) -> Result<Self::Node, Self::Error> {
self.try_into()
}
fn id(mut self, id: u64) -> Self {
self.builder = self.builder.id(id);
self
}
fn get_id(&self) -> Option<u64> {
self.builder.get_id()
}
fn label<S: ToString>(mut self, label: S) -> Result<Self, Self::Error> {
self.builder = self.builder.label(label)?;
Ok(self)
}
fn get_label(&self) -> Option<&String> {
self.builder.get_label()
}
fn style_class(mut self, style_class: Rc<StyleClass>) -> Result<Self, StyleClassError> {
self.builder = self.builder.style_class(style_class)?;
Ok(self)
}
fn style_property(
mut self,
property: crate::shared::StyleProperty,
) -> Result<Self, StyleClassError> {
self.builder = self.builder.style_property(property)?;
Ok(self)
}
fn style_properties(&self) -> impl Iterator<Item = &crate::prelude::StyleProperty> {
self.builder.style_properties()
}
}
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use super::*;
use crate::{
shared::{
ClickEvent, StyleClassBuilder, StyleProperty, click_event::Navigation,
style_class::Unit,
},
traits::node::Node,
};
#[test]
fn test_flowchart_node_builder() -> Result<(), Box<dyn core::error::Error>> {
let style_class = Rc::new(
StyleClassBuilder::default()
.name("test")?
.property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?,
);
let subnode = Rc::new(FlowchartNodeBuilder::default().label("Sub")?.id(2).build()?);
let node = FlowchartNodeBuilder::default()
.id(1)
.label("My Node")?
.shape(FlowchartNodeShape::Circle)
.click_event(ClickEvent::Navigation(Navigation::new("https://example.com")))
.subnode(subnode.clone())?
.direction(Direction::TopToBottom)
.style_class(style_class.clone())?
.style_property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?;
assert_eq!(node.id(), 1);
assert_eq!(node.label(), "My Node");
assert_eq!(node.shape, FlowchartNodeShape::Circle);
assert!(matches!(node.click_event, Some(ClickEvent::Navigation { .. })));
assert_eq!(node.subnodes.len(), 1);
assert_eq!(node.direction, Some(Direction::TopToBottom));
assert_eq!(node.classes().count(), 1);
assert_eq!(node.styles().count(), 1);
Ok(())
}
#[test]
fn test_flowchart_node_builder_subgraph_methods() -> Result<(), Box<dyn core::error::Error>> {
let mut builder = FlowchartNodeBuilder::default();
assert!(!builder.is_subgraph());
let subnode = Rc::new(FlowchartNodeBuilder::default().label("Sub")?.id(2).build()?);
builder = builder.subnode(subnode)?;
assert!(builder.is_subgraph());
builder = builder.direction(Direction::LeftToRight);
assert_eq!(builder.get_direction(), Some(Direction::LeftToRight));
builder = builder.reset_direction();
assert_eq!(builder.get_direction(), None);
Ok(())
}
}