use alloc::{rc::Rc, string::ToString, vec::Vec};
use crate::{
diagrams::flowchart::{
curve_styles::CurveStyle, flowchart_edge::FlowchartEdge, flowchart_node::FlowchartNode,
},
errors::EdgeError,
shared::{StyleClass, StyleClassError, StyleProperty, generic_edge::GenericEdgeBuilder},
traits::EdgeBuilder,
};
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FlowchartEdgeBuilder {
id: Option<usize>,
edge_builder: GenericEdgeBuilder<FlowchartNode>,
style_classes: Vec<Rc<StyleClass>>,
style_properties: Vec<StyleProperty>,
curve_style: CurveStyle,
length: u8,
}
impl FlowchartEdgeBuilder {
#[must_use]
pub fn id(mut self, id: usize) -> Self {
self.id = Some(id);
self
}
pub fn style_class(mut self, class: Rc<StyleClass>) -> Result<Self, StyleClassError> {
if self.style_classes.iter().any(|c| c.name() == class.name()) {
return Err(StyleClassError::DuplicateClass(class.name().to_string()));
}
self.style_classes.push(class);
Ok(self)
}
pub fn style_property(mut self, property: StyleProperty) -> Result<Self, StyleClassError> {
if self.style_properties.iter().any(|p| p.is_same_type(property)) {
return Err(StyleClassError::DuplicateProperty(property));
}
self.style_properties.push(property);
Ok(self)
}
#[must_use]
pub fn curve_style(mut self, style: CurveStyle) -> Self {
self.curve_style = style;
self
}
#[must_use]
pub fn length(mut self, length: u8) -> Self {
self.length = length;
self
}
}
impl Default for FlowchartEdgeBuilder {
fn default() -> Self {
Self {
id: None,
edge_builder: GenericEdgeBuilder::default(),
style_classes: Vec::new(),
style_properties: Vec::new(),
curve_style: CurveStyle::default(),
length: 1,
}
}
}
impl TryFrom<FlowchartEdgeBuilder> for FlowchartEdge {
type Error = EdgeError;
fn try_from(builder: FlowchartEdgeBuilder) -> Result<Self, Self::Error> {
if builder.length == 0 {
return Err(EdgeError::InvalidLength);
}
Ok(FlowchartEdge {
id: builder.id.ok_or(EdgeError::MissingId)?,
edge: builder.edge_builder.try_into()?,
style_classes: builder.style_classes,
style_properties: builder.style_properties,
curve_style: builder.curve_style,
length: builder.length,
})
}
}
impl EdgeBuilder for FlowchartEdgeBuilder {
type Edge = FlowchartEdge;
type Node = FlowchartNode;
type Error = EdgeError;
fn build(self) -> Result<Self::Edge, Self::Error> {
self.try_into()
}
fn source(mut self, node: Rc<Self::Node>) -> Result<Self, Self::Error> {
self.edge_builder = self.edge_builder.source(node)?;
Ok(self)
}
fn destination(mut self, node: Rc<Self::Node>) -> Result<Self, Self::Error> {
self.edge_builder = self.edge_builder.destination(node)?;
Ok(self)
}
fn label<S: ToString>(mut self, label: S) -> Result<Self, Self::Error> {
self.edge_builder = self.edge_builder.label(label)?;
Ok(self)
}
fn line_style(mut self, style: crate::shared::LineStyle) -> Self {
self.edge_builder = self.edge_builder.line_style(style);
self
}
fn left_arrow_shape(mut self, shape: crate::shared::ArrowShape) -> Result<Self, Self::Error> {
self.edge_builder = self.edge_builder.left_arrow_shape(shape)?;
Ok(self)
}
fn right_arrow_shape(mut self, shape: crate::shared::ArrowShape) -> Result<Self, Self::Error> {
self.edge_builder = self.edge_builder.right_arrow_shape(shape)?;
Ok(self)
}
}
#[cfg(test)]
mod tests {
use alloc::{boxed::Box, rc::Rc};
use super::*;
use crate::{
diagrams::flowchart::flowchart_node::FlowchartNodeBuilder,
shared::{ArrowShape, LineStyle, StyleClassBuilder, style_class::Unit},
traits::{NodeBuilder, edge::Edge, node::Node},
};
#[test]
fn test_flowchart_edge_builder() -> Result<(), Box<dyn core::error::Error>> {
let node1 = Rc::new(FlowchartNodeBuilder::default().label("A")?.id(0).build()?);
let node2 = Rc::new(FlowchartNodeBuilder::default().label("B")?.id(1).build()?);
let style_class = Rc::new(
StyleClassBuilder::default()
.name("test")?
.property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?,
);
let edge = FlowchartEdgeBuilder::default()
.id(1)
.source(node1.clone())?
.destination(node2.clone())?
.label("Edge Label")?
.line_style(LineStyle::Dashed)
.left_arrow_shape(ArrowShape::Circle)?
.right_arrow_shape(ArrowShape::X)?
.curve_style(CurveStyle::StepAfter)
.length(2)
.style_class(style_class.clone())?
.style_property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?;
assert_eq!(edge.id, 1);
assert_eq!(edge.source().id(), 0);
assert_eq!(edge.destination().id(), 1);
assert_eq!(edge.label(), Some("Edge Label"));
assert_eq!(edge.line_style(), LineStyle::Dashed);
assert_eq!(edge.left_arrow_shape(), Some(ArrowShape::Circle));
assert_eq!(edge.right_arrow_shape(), Some(ArrowShape::X));
assert_eq!(edge.curve_style, CurveStyle::StepAfter);
assert_eq!(edge.length, 2);
assert_eq!(edge.style_classes.len(), 1);
assert_eq!(edge.style_properties.len(), 1);
Ok(())
}
}