mod layout;
mod node;
mod reconcile;
mod theme;
pub use layout::measure_class_diagram;
pub use node::ClassDiagramNode;
pub use reconcile::reconcile_class_diagram;
pub use theme::ClassDiagramTheme;
use std::sync::Arc;
use crate::core::element::{Element, ElementKind};
use crate::style::{BorderStyle, Length, Padding, Style};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum ClassVisibility {
#[default]
Public,
Private,
Protected,
Package,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum ClassRelationKind {
#[default]
Association,
Dependency,
Inheritance,
Realization,
Composition,
Aggregation,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClassMember {
pub visibility: ClassVisibility,
pub name: Arc<str>,
pub ty: Option<Arc<str>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClassSpec {
pub name: Arc<str>,
pub attributes: Vec<ClassMember>,
pub methods: Vec<ClassMember>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClassRelation {
pub from: Arc<str>,
pub to: Arc<str>,
pub kind: ClassRelationKind,
pub multiplicity_from: Option<Arc<str>>,
pub multiplicity_to: Option<Arc<str>>,
pub label: Option<Arc<str>>,
}
#[derive(Clone)]
pub struct ClassDiagram {
pub(crate) classes: Arc<[ClassSpec]>,
pub(crate) relations: Arc<[ClassRelation]>,
pub(crate) style: Style,
pub(crate) class_style: Style,
pub(crate) edge_style: Style,
pub(crate) label_style: Style,
pub(crate) border_style: BorderStyle,
pub(crate) padding: Padding,
pub(crate) node_padding: Padding,
pub(crate) layer_gap: u16,
pub(crate) node_gap: u16,
pub(crate) max_node_width: u16,
pub(crate) theme: ClassDiagramTheme,
pub(crate) width: Length,
pub(crate) height: Length,
}
impl Default for ClassDiagram {
fn default() -> Self {
Self {
classes: Arc::new([]),
relations: Arc::new([]),
style: Style::default(),
class_style: Style::default(),
edge_style: Style::default(),
label_style: Style::default(),
border_style: BorderStyle::Plain,
padding: Padding::default(),
node_padding: (0, 1).into(),
layer_gap: 4,
node_gap: 4,
max_node_width: 32,
theme: ClassDiagramTheme::default(),
width: Length::Auto,
height: Length::Auto,
}
}
}
impl ClassDiagram {
pub fn new() -> Self {
Self::default()
}
pub fn classes(mut self, classes: impl IntoIterator<Item = ClassSpec>) -> Self {
self.classes = classes.into_iter().collect::<Vec<_>>().into();
self
}
pub fn relations(mut self, relations: impl IntoIterator<Item = ClassRelation>) -> Self {
self.relations = relations.into_iter().collect::<Vec<_>>().into();
self
}
pub fn class(mut self, name: impl Into<Arc<str>>) -> Self {
let mut v = self.classes.to_vec();
v.push(ClassSpec::new(name));
self.classes = v.into();
self
}
pub fn attribute(
mut self,
class: impl AsRef<str>,
visibility: ClassVisibility,
name: impl Into<Arc<str>>,
ty: impl Into<Arc<str>>,
) -> Self {
self.update_class(class.as_ref(), |c| {
c.attributes.push(ClassMember {
visibility,
name: name.into(),
ty: Some(ty.into()),
})
});
self
}
pub fn method(
mut self,
class: impl AsRef<str>,
visibility: ClassVisibility,
name: impl Into<Arc<str>>,
sig: impl Into<Arc<str>>,
) -> Self {
self.update_class(class.as_ref(), |c| {
c.methods.push(ClassMember {
visibility,
name: name.into(),
ty: Some(sig.into()),
})
});
self
}
pub fn relation(
mut self,
from: impl Into<Arc<str>>,
to: impl Into<Arc<str>>,
kind: ClassRelationKind,
multiplicity_from: impl Into<Option<Arc<str>>>,
multiplicity_to: impl Into<Option<Arc<str>>>,
label: impl Into<Option<Arc<str>>>,
) -> Self {
let mut v = self.relations.to_vec();
v.push(ClassRelation {
from: from.into(),
to: to.into(),
kind,
multiplicity_from: multiplicity_from.into(),
multiplicity_to: multiplicity_to.into(),
label: label.into(),
});
self.relations = v.into();
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn class_style(mut self, style: Style) -> Self {
self.class_style = style;
self
}
pub fn edge_style(mut self, style: Style) -> Self {
self.edge_style = style;
self
}
pub fn label_style(mut self, style: Style) -> Self {
self.label_style = style;
self
}
pub fn border_style(mut self, style: BorderStyle) -> Self {
self.border_style = style;
self
}
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
self
}
pub fn node_padding(mut self, padding: impl Into<Padding>) -> Self {
self.node_padding = padding.into();
self
}
pub fn max_node_width(mut self, width: u16) -> Self {
self.max_node_width = width.max(1);
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
fn update_class(&mut self, name: &str, f: impl FnOnce(&mut ClassSpec)) {
let mut classes = self.classes.to_vec();
let index = classes
.iter()
.position(|c| c.name.as_ref() == name)
.unwrap_or_else(|| {
classes.push(ClassSpec::new(name.to_owned()));
classes.len() - 1
});
f(&mut classes[index]);
self.classes = classes.into();
}
}
impl ClassSpec {
pub fn new(name: impl Into<Arc<str>>) -> Self {
Self {
name: name.into(),
attributes: Vec::new(),
methods: Vec::new(),
}
}
pub fn attribute(
mut self,
visibility: ClassVisibility,
name: impl Into<Arc<str>>,
ty: impl Into<Arc<str>>,
) -> Self {
self.attributes.push(ClassMember {
visibility,
name: name.into(),
ty: Some(ty.into()),
});
self
}
pub fn method(
mut self,
visibility: ClassVisibility,
name: impl Into<Arc<str>>,
sig: impl Into<Arc<str>>,
) -> Self {
self.methods.push(ClassMember {
visibility,
name: name.into(),
ty: Some(sig.into()),
});
self
}
}
impl From<ClassDiagram> for Element {
fn from(value: ClassDiagram) -> Self {
Element::new(ElementKind::ClassDiagram(Box::new(value)))
}
}