use alloc::{
rc::Rc,
string::{String, ToString},
vec::Vec,
};
use crate::{
diagrams::class_diagram::class_node::{ClassAttribute, ClassMethod, ClassNode},
errors::NodeError,
shared::{ClickEvent, StyleClass, StyleClassError, generic_node::GenericNodeBuilder},
traits::NodeBuilder,
};
#[derive(Default, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ClassNodeBuilder {
builder: GenericNodeBuilder,
click_event: Option<ClickEvent>,
annotation: Option<String>,
attributes: Vec<ClassAttribute>,
methods: Vec<ClassMethod>,
}
impl ClassNodeBuilder {
#[must_use]
pub fn click_event(mut self, click_event: ClickEvent) -> Self {
self.click_event = Some(click_event);
self
}
#[must_use]
pub fn annotation<S: ToString + ?Sized>(mut self, annotation: &S) -> Self {
self.annotation = Some(annotation.to_string());
self
}
#[must_use]
pub fn attribute(mut self, attribute: ClassAttribute) -> Self {
self.attributes.push(attribute);
self
}
#[must_use]
pub fn method(mut self, method: ClassMethod) -> Self {
self.methods.push(method);
self
}
}
impl TryFrom<ClassNodeBuilder> for ClassNode {
type Error = NodeError;
fn try_from(builder: ClassNodeBuilder) -> Result<Self, Self::Error> {
Ok(ClassNode {
node: builder.builder.try_into()?,
click_event: builder.click_event,
annotation: builder.annotation,
attributes: builder.attributes,
methods: builder.methods,
})
}
}
impl NodeBuilder for ClassNodeBuilder {
type Node = ClassNode;
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::vec;
use super::*;
use crate::{
diagrams::class_diagram::class_node::{ClassAttribute, ClassMethod},
shared::{
ClickEvent, StyleClassBuilder, StyleProperty, click_event::Navigation,
style_class::Unit,
},
traits::node::Node,
};
#[test]
fn test_class_node_builder() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let style_class = Rc::new(
StyleClassBuilder::default()
.name("test")?
.property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?,
);
let node = ClassNodeBuilder::default()
.id(1)
.label("MyClass")?
.annotation("interface")
.attribute(ClassAttribute::new("int", "id"))
.method(ClassMethod::new("void", "method", vec![]))
.click_event(ClickEvent::Navigation(Navigation::new("https://example.com")))
.style_class(style_class.clone())?
.style_property(StyleProperty::StrokeWidth(Unit::Pixel(2)))?
.build()?;
assert_eq!(node.id(), 1);
assert_eq!(node.label(), "MyClass");
assert_eq!(node.annotation, Some("interface".to_string()));
assert_eq!(node.attributes.len(), 1);
assert_eq!(node.methods.len(), 1);
assert!(matches!(node.click_event, Some(ClickEvent::Navigation(_))));
assert_eq!(node.classes().count(), 1);
assert_eq!(node.styles().count(), 1);
Ok(())
}
}