tui_lipan/widgets/er_diagram/
mod.rs1mod layout;
4mod node;
5mod reconcile;
6mod theme;
7
8pub use layout::measure_er_diagram;
9pub use node::ErDiagramNode;
10pub use reconcile::reconcile_er_diagram;
11pub use theme::ErDiagramTheme;
12
13use crate::core::element::{Element, ElementKind};
14use crate::style::{BorderStyle, Length, Padding, Style};
15use std::sync::Arc;
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
19pub enum ErCardinality {
20 ZeroOrOne,
22 ExactlyOne,
24 ZeroOrMore,
26 #[default]
28 OneOrMore,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct ErAttribute {
34 pub ty: Arc<str>,
36 pub name: Arc<str>,
38 pub pk: bool,
40 pub fk: bool,
42 pub uk: bool,
44}
45impl ErAttribute {
46 pub fn new(ty: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
48 Self {
49 ty: ty.into(),
50 name: name.into(),
51 pk: false,
52 fk: false,
53 uk: false,
54 }
55 }
56 pub fn pk(mut self) -> Self {
58 self.pk = true;
59 self
60 }
61 pub fn fk(mut self) -> Self {
63 self.fk = true;
64 self
65 }
66 pub fn uk(mut self) -> Self {
68 self.uk = true;
69 self
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75pub struct ErEntity {
76 pub name: Arc<str>,
78 pub attributes: Vec<ErAttribute>,
80}
81impl ErEntity {
82 pub fn new(name: impl Into<Arc<str>>) -> Self {
84 Self {
85 name: name.into(),
86 attributes: Vec::new(),
87 }
88 }
89 pub fn attribute(mut self, attribute: ErAttribute) -> Self {
91 self.attributes.push(attribute);
92 self
93 }
94}
95
96#[derive(Clone, Debug, PartialEq, Eq, Hash)]
98pub struct ErRelation {
99 pub left: Arc<str>,
101 pub right: Arc<str>,
103 pub left_cardinality: ErCardinality,
105 pub right_cardinality: ErCardinality,
107 pub label: Option<Arc<str>>,
109}
110
111#[derive(Clone)]
114pub struct ErDiagram {
115 pub(crate) entities: Arc<[ErEntity]>,
116 pub(crate) relations: Arc<[ErRelation]>,
117 pub(crate) style: Style,
118 pub(crate) entity_style: Style,
119 pub(crate) edge_style: Style,
120 pub(crate) label_style: Style,
121 pub(crate) border_style: BorderStyle,
122 pub(crate) padding: Padding,
123 pub(crate) node_padding: Padding,
124 pub(crate) layer_gap: u16,
125 pub(crate) node_gap: u16,
126 pub(crate) max_node_width: u16,
127 pub(crate) theme: ErDiagramTheme,
128 pub(crate) width: Length,
129 pub(crate) height: Length,
130}
131
132impl Default for ErDiagram {
133 fn default() -> Self {
134 Self {
135 entities: Arc::new([]),
136 relations: Arc::new([]),
137 style: Style::default(),
138 entity_style: Style::default(),
139 edge_style: Style::default(),
140 label_style: Style::default(),
141 border_style: BorderStyle::Plain,
142 padding: Padding::default(),
143 node_padding: (0, 1).into(),
144 layer_gap: 4,
145 node_gap: 4,
146 max_node_width: 32,
147 theme: ErDiagramTheme::default(),
148 width: Length::Auto,
149 height: Length::Auto,
150 }
151 }
152}
153
154impl ErDiagram {
155 pub fn new() -> Self {
157 Self::default()
158 }
159 pub fn entities(mut self, entities: impl IntoIterator<Item = ErEntity>) -> Self {
161 self.entities = entities.into_iter().collect::<Vec<_>>().into();
162 self
163 }
164 pub fn relations(mut self, relations: impl IntoIterator<Item = ErRelation>) -> Self {
166 self.relations = relations.into_iter().collect::<Vec<_>>().into();
167 self
168 }
169 pub fn entity(mut self, name: impl Into<Arc<str>>) -> Self {
172 let mut v = self.entities.to_vec();
173 v.push(ErEntity::new(name));
174 self.entities = v.into();
175 self
176 }
177 pub fn attribute(
180 mut self,
181 entity: impl AsRef<str>,
182 ty: impl Into<Arc<str>>,
183 name: impl Into<Arc<str>>,
184 ) -> Self {
185 self.update_entity(entity.as_ref(), |e| {
186 e.attributes.push(ErAttribute::new(ty, name))
187 });
188 self
189 }
190 pub fn relation(
193 mut self,
194 left: impl Into<Arc<str>>,
195 right: impl Into<Arc<str>>,
196 left_cardinality: ErCardinality,
197 right_cardinality: ErCardinality,
198 label: impl Into<Option<Arc<str>>>,
199 ) -> Self {
200 let mut v = self.relations.to_vec();
201 v.push(ErRelation {
202 left: left.into(),
203 right: right.into(),
204 left_cardinality,
205 right_cardinality,
206 label: label.into(),
207 });
208 self.relations = v.into();
209 self
210 }
211 pub fn style(mut self, style: Style) -> Self {
213 self.style = style;
214 self
215 }
216 pub fn entity_style(mut self, style: Style) -> Self {
218 self.entity_style = style;
219 self
220 }
221 pub fn edge_style(mut self, style: Style) -> Self {
223 self.edge_style = style;
224 self
225 }
226 pub fn label_style(mut self, style: Style) -> Self {
228 self.label_style = style;
229 self
230 }
231 pub fn border_style(mut self, style: BorderStyle) -> Self {
233 self.border_style = style;
234 self
235 }
236 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
238 self.padding = padding.into();
239 self
240 }
241 pub fn node_padding(mut self, padding: impl Into<Padding>) -> Self {
243 self.node_padding = padding.into();
244 self
245 }
246 pub fn max_node_width(mut self, width: u16) -> Self {
249 self.max_node_width = width.max(1);
250 self
251 }
252 pub fn width(mut self, width: Length) -> Self {
254 self.width = width;
255 self
256 }
257 pub fn height(mut self, height: Length) -> Self {
259 self.height = height;
260 self
261 }
262 fn update_entity(&mut self, name: &str, f: impl FnOnce(&mut ErEntity)) {
263 let mut entities = self.entities.to_vec();
264 let index = entities
265 .iter()
266 .position(|e| e.name.as_ref() == name)
267 .unwrap_or_else(|| {
268 entities.push(ErEntity::new(name.to_owned()));
269 entities.len() - 1
270 });
271 f(&mut entities[index]);
272 self.entities = entities.into();
273 }
274}
275impl From<ErDiagram> for Element {
276 fn from(value: ErDiagram) -> Self {
277 Element::new(ElementKind::ErDiagram(Box::new(value)))
278 }
279}