1#[derive(Debug, Clone, Default, PartialEq)]
5pub struct DiagramOptions {
6 pub footer: FooterStyle,
8}
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct Diagram {
13 pub title: Option<String>,
15 pub items: Vec<Item>,
17 pub options: DiagramOptions,
19}
20
21impl Diagram {
22 pub fn participants(&self) -> Vec<Participant> {
24 let mut participants = Vec::new();
25 let mut seen = std::collections::HashSet::new();
26
27 fn add_participant(
28 name: &str,
29 alias: Option<&str>,
30 kind: ParticipantKind,
31 participants: &mut Vec<Participant>,
32 seen: &mut std::collections::HashSet<String>,
33 ) {
34 let key = alias.unwrap_or(name).to_string();
35 if !seen.contains(&key) {
36 seen.insert(key.clone());
37 participants.push(Participant {
38 name: name.to_string(),
39 alias: alias.map(|s| s.to_string()),
40 kind,
41 });
42 }
43 }
44
45 fn collect_from_items(
46 items: &[Item],
47 participants: &mut Vec<Participant>,
48 seen: &mut std::collections::HashSet<String>,
49 ) {
50 for item in items {
51 match item {
52 Item::ParticipantDecl { name, alias, kind } => {
53 add_participant(name, alias.as_deref(), *kind, participants, seen);
54 }
55 Item::Message { from, to, .. } => {
56 if from != "[" && from != "]" {
58 add_participant(
59 from,
60 None,
61 ParticipantKind::Participant,
62 participants,
63 seen,
64 );
65 }
66 if to != "[" && to != "]" {
67 add_participant(to, None, ParticipantKind::Participant, participants, seen);
68 }
69 }
70 Item::Note { participants: note_participants, .. } => {
71 for p in note_participants {
72 add_participant(p, None, ParticipantKind::Participant, participants, seen);
73 }
74 }
75 Item::State { participants: state_participants, .. } => {
76 for p in state_participants {
77 add_participant(p, None, ParticipantKind::Participant, participants, seen);
78 }
79 }
80 Item::Ref { participants: ref_participants, input_from, output_to, .. } => {
81 if let Some(from) = input_from {
83 add_participant(from, None, ParticipantKind::Participant, participants, seen);
84 }
85 for p in ref_participants {
87 add_participant(p, None, ParticipantKind::Participant, participants, seen);
88 }
89 if let Some(to) = output_to {
91 add_participant(to, None, ParticipantKind::Participant, participants, seen);
92 }
93 }
94 Item::Activate { participant } | Item::Deactivate { participant } | Item::Destroy { participant } => {
95 add_participant(participant, None, ParticipantKind::Participant, participants, seen);
96 }
97 Item::Block {
98 items, else_sections, ..
99 } => {
100 collect_from_items(items, participants, seen);
101 for else_section in else_sections {
102 collect_from_items(&else_section.items, participants, seen);
103 }
104 }
105 _ => {}
106 }
107 }
108 }
109
110 collect_from_items(&self.items, &mut participants, &mut seen);
111 participants
112 }
113}
114
115#[derive(Debug, Clone, PartialEq)]
117pub struct Participant {
118 pub name: String,
120 pub alias: Option<String>,
122 pub kind: ParticipantKind,
124}
125
126impl Participant {
127 pub fn id(&self) -> &str {
129 self.alias.as_deref().unwrap_or(&self.name)
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum ParticipantKind {
136 Participant,
138 Actor,
140}
141
142#[derive(Debug, Clone, PartialEq)]
144pub enum Item {
145 ParticipantDecl {
147 name: String,
148 alias: Option<String>,
149 kind: ParticipantKind,
150 },
151 Message {
153 from: String,
154 to: String,
155 text: String,
156 arrow: Arrow,
157 activate: bool,
159 deactivate: bool,
161 create: bool,
163 },
164 Note {
166 position: NotePosition,
167 participants: Vec<String>,
168 text: String,
169 },
170 Activate { participant: String },
172 Deactivate { participant: String },
174 Destroy { participant: String },
176 Block {
178 kind: BlockKind,
179 label: String,
180 items: Vec<Item>,
181 else_sections: Vec<ElseSection>,
183 },
184 Autonumber { enabled: bool, start: Option<u32> },
186 State {
188 participants: Vec<String>,
189 text: String,
190 },
191 Ref {
193 participants: Vec<String>,
194 text: String,
195 input_from: Option<String>,
197 input_label: Option<String>,
199 output_to: Option<String>,
201 output_label: Option<String>,
203 },
204 DiagramOption { key: String, value: String },
206 Description { text: String },
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct Arrow {
213 pub line: LineStyle,
215 pub head: ArrowHead,
217 pub delay: Option<u32>,
219}
220
221impl Arrow {
222 pub const SYNC: Arrow = Arrow {
223 line: LineStyle::Solid,
224 head: ArrowHead::Filled,
225 delay: None,
226 };
227
228 pub const SYNC_OPEN: Arrow = Arrow {
229 line: LineStyle::Solid,
230 head: ArrowHead::Open,
231 delay: None,
232 };
233
234 pub const RESPONSE: Arrow = Arrow {
235 line: LineStyle::Dashed,
236 head: ArrowHead::Filled,
237 delay: None,
238 };
239
240 pub const RESPONSE_OPEN: Arrow = Arrow {
241 line: LineStyle::Dashed,
242 head: ArrowHead::Open,
243 delay: None,
244 };
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
249pub enum LineStyle {
250 Solid,
252 Dashed,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258pub enum ArrowHead {
259 Filled,
261 Open,
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum NotePosition {
268 Left,
270 Right,
272 Over,
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
278pub enum FooterStyle {
279 None,
281 Bar,
283 #[default]
285 Box,
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum BlockKind {
291 Alt,
293 Opt,
295 Loop,
297 Par,
299 Seq,
301 Parallel,
303 Serial,
305}
306
307impl BlockKind {
308 pub fn as_str(&self) -> &'static str {
309 match self {
310 BlockKind::Alt => "alt",
311 BlockKind::Opt => "opt",
312 BlockKind::Loop => "loop",
313 BlockKind::Par => "par",
314 BlockKind::Seq => "seq",
315 BlockKind::Parallel => "parallel",
316 BlockKind::Serial => "serial",
317 }
318 }
319}
320
321#[derive(Debug, Clone, PartialEq)]
323pub struct ElseSection {
324 pub label: Option<String>,
326 pub items: Vec<Item>,
328}