tui_lipan/widgets/class_diagram/
mod.rs1mod layout;
4mod node;
5mod reconcile;
6mod theme;
7
8pub use layout::measure_class_diagram;
9pub use node::ClassDiagramNode;
10pub use reconcile::reconcile_class_diagram;
11pub use theme::ClassDiagramTheme;
12
13use std::sync::Arc;
14
15use crate::core::element::{Element, ElementKind};
16use crate::style::{BorderStyle, Length, Padding, Style};
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
20pub enum ClassVisibility {
21 #[default]
23 Public,
24 Private,
26 Protected,
28 Package,
30}
31
32#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
34pub enum ClassRelationKind {
35 #[default]
37 Association,
38 Dependency,
40 Inheritance,
42 Realization,
44 Composition,
46 Aggregation,
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
52pub struct ClassMember {
53 pub visibility: ClassVisibility,
55 pub name: Arc<str>,
57 pub ty: Option<Arc<str>>,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq, Hash)]
63pub struct ClassSpec {
64 pub name: Arc<str>,
66 pub attributes: Vec<ClassMember>,
68 pub methods: Vec<ClassMember>,
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, Hash)]
74pub struct ClassRelation {
75 pub from: Arc<str>,
77 pub to: Arc<str>,
79 pub kind: ClassRelationKind,
81 pub multiplicity_from: Option<Arc<str>>,
83 pub multiplicity_to: Option<Arc<str>>,
85 pub label: Option<Arc<str>>,
87}
88
89#[derive(Clone)]
92pub struct ClassDiagram {
93 pub(crate) classes: Arc<[ClassSpec]>,
94 pub(crate) relations: Arc<[ClassRelation]>,
95 pub(crate) style: Style,
96 pub(crate) class_style: Style,
97 pub(crate) edge_style: Style,
98 pub(crate) label_style: Style,
99 pub(crate) border_style: BorderStyle,
100 pub(crate) padding: Padding,
101 pub(crate) node_padding: Padding,
102 pub(crate) layer_gap: u16,
103 pub(crate) node_gap: u16,
104 pub(crate) max_node_width: u16,
105 pub(crate) theme: ClassDiagramTheme,
106 pub(crate) width: Length,
107 pub(crate) height: Length,
108}
109
110impl Default for ClassDiagram {
111 fn default() -> Self {
112 Self {
113 classes: Arc::new([]),
114 relations: Arc::new([]),
115 style: Style::default(),
116 class_style: Style::default(),
117 edge_style: Style::default(),
118 label_style: Style::default(),
119 border_style: BorderStyle::Plain,
120 padding: Padding::default(),
121 node_padding: (0, 1).into(),
122 layer_gap: 4,
123 node_gap: 4,
124 max_node_width: 32,
125 theme: ClassDiagramTheme::default(),
126 width: Length::Auto,
127 height: Length::Auto,
128 }
129 }
130}
131
132impl ClassDiagram {
133 pub fn new() -> Self {
135 Self::default()
136 }
137 pub fn classes(mut self, classes: impl IntoIterator<Item = ClassSpec>) -> Self {
139 self.classes = classes.into_iter().collect::<Vec<_>>().into();
140 self
141 }
142 pub fn relations(mut self, relations: impl IntoIterator<Item = ClassRelation>) -> Self {
144 self.relations = relations.into_iter().collect::<Vec<_>>().into();
145 self
146 }
147 pub fn class(mut self, name: impl Into<Arc<str>>) -> Self {
150 let mut v = self.classes.to_vec();
151 v.push(ClassSpec::new(name));
152 self.classes = v.into();
153 self
154 }
155 pub fn attribute(
158 mut self,
159 class: impl AsRef<str>,
160 visibility: ClassVisibility,
161 name: impl Into<Arc<str>>,
162 ty: impl Into<Arc<str>>,
163 ) -> Self {
164 self.update_class(class.as_ref(), |c| {
165 c.attributes.push(ClassMember {
166 visibility,
167 name: name.into(),
168 ty: Some(ty.into()),
169 })
170 });
171 self
172 }
173 pub fn method(
176 mut self,
177 class: impl AsRef<str>,
178 visibility: ClassVisibility,
179 name: impl Into<Arc<str>>,
180 sig: impl Into<Arc<str>>,
181 ) -> Self {
182 self.update_class(class.as_ref(), |c| {
183 c.methods.push(ClassMember {
184 visibility,
185 name: name.into(),
186 ty: Some(sig.into()),
187 })
188 });
189 self
190 }
191 pub fn relation(
194 mut self,
195 from: impl Into<Arc<str>>,
196 to: impl Into<Arc<str>>,
197 kind: ClassRelationKind,
198 multiplicity_from: impl Into<Option<Arc<str>>>,
199 multiplicity_to: impl Into<Option<Arc<str>>>,
200 label: impl Into<Option<Arc<str>>>,
201 ) -> Self {
202 let mut v = self.relations.to_vec();
203 v.push(ClassRelation {
204 from: from.into(),
205 to: to.into(),
206 kind,
207 multiplicity_from: multiplicity_from.into(),
208 multiplicity_to: multiplicity_to.into(),
209 label: label.into(),
210 });
211 self.relations = v.into();
212 self
213 }
214 pub fn style(mut self, style: Style) -> Self {
216 self.style = style;
217 self
218 }
219 pub fn class_style(mut self, style: Style) -> Self {
221 self.class_style = style;
222 self
223 }
224 pub fn edge_style(mut self, style: Style) -> Self {
226 self.edge_style = style;
227 self
228 }
229 pub fn label_style(mut self, style: Style) -> Self {
231 self.label_style = style;
232 self
233 }
234 pub fn border_style(mut self, style: BorderStyle) -> Self {
236 self.border_style = style;
237 self
238 }
239 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
241 self.padding = padding.into();
242 self
243 }
244 pub fn node_padding(mut self, padding: impl Into<Padding>) -> Self {
246 self.node_padding = padding.into();
247 self
248 }
249 pub fn max_node_width(mut self, width: u16) -> Self {
251 self.max_node_width = width.max(1);
252 self
253 }
254 pub fn width(mut self, width: Length) -> Self {
256 self.width = width;
257 self
258 }
259 pub fn height(mut self, height: Length) -> Self {
261 self.height = height;
262 self
263 }
264
265 fn update_class(&mut self, name: &str, f: impl FnOnce(&mut ClassSpec)) {
266 let mut classes = self.classes.to_vec();
267 let index = classes
268 .iter()
269 .position(|c| c.name.as_ref() == name)
270 .unwrap_or_else(|| {
271 classes.push(ClassSpec::new(name.to_owned()));
272 classes.len() - 1
273 });
274 f(&mut classes[index]);
275 self.classes = classes.into();
276 }
277}
278
279impl ClassSpec {
280 pub fn new(name: impl Into<Arc<str>>) -> Self {
282 Self {
283 name: name.into(),
284 attributes: Vec::new(),
285 methods: Vec::new(),
286 }
287 }
288 pub fn attribute(
290 mut self,
291 visibility: ClassVisibility,
292 name: impl Into<Arc<str>>,
293 ty: impl Into<Arc<str>>,
294 ) -> Self {
295 self.attributes.push(ClassMember {
296 visibility,
297 name: name.into(),
298 ty: Some(ty.into()),
299 });
300 self
301 }
302 pub fn method(
304 mut self,
305 visibility: ClassVisibility,
306 name: impl Into<Arc<str>>,
307 sig: impl Into<Arc<str>>,
308 ) -> Self {
309 self.methods.push(ClassMember {
310 visibility,
311 name: name.into(),
312 ty: Some(sig.into()),
313 });
314 self
315 }
316}
317
318impl From<ClassDiagram> for Element {
319 fn from(value: ClassDiagram) -> Self {
320 Element::new(ElementKind::ClassDiagram(Box::new(value)))
321 }
322}