1mod layout;
4mod node;
5mod reconcile;
6mod theme;
7
8pub use layout::measure_sequence_diagram;
9pub use node::SequenceDiagramNode;
10pub(crate) use node::{PositionedFragment, PositionedMessage, autonumber_rect};
11pub use reconcile::{reconcile_sequence_diagram, reconcile_sequence_diagram_with_width};
12pub use theme::{
13 ActivationTheme, AutonumberTheme, FragmentGlyphs, LifelineTheme, MessageGlyphs,
14 SequenceDiagramTheme,
15};
16
17use std::sync::Arc;
18
19use crate::callback::Callback;
20use crate::core::element::{Element, ElementKind};
21use crate::style::{BorderStyle, Length, Padding, Style};
22use crate::widgets::Overflow;
23
24#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct ActorRef(pub Arc<str>);
27
28impl ActorRef {
29 pub fn new(value: impl Into<Arc<str>>) -> Self {
31 Self(value.into())
32 }
33
34 pub fn as_str(&self) -> &str {
36 &self.0
37 }
38}
39
40impl From<&str> for ActorRef {
41 fn from(value: &str) -> Self {
42 Self::new(value)
43 }
44}
45
46impl From<String> for ActorRef {
47 fn from(value: String) -> Self {
48 Self::new(value)
49 }
50}
51
52impl From<Arc<str>> for ActorRef {
53 fn from(value: Arc<str>) -> Self {
54 Self::new(value)
55 }
56}
57
58#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
60pub enum ActorKind {
61 #[default]
63 Participant,
64 Actor,
66}
67
68#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
70pub enum SequenceDiagramVariant {
71 #[default]
73 Boxed,
74 Minimal,
76}
77
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
80pub enum MessageStyle {
81 #[default]
83 Sync,
84 Async,
86 SyncReply,
88 AsyncReply,
90 Lost,
92 Open,
94}
95
96impl MessageStyle {
97 pub const INDEX_COUNT: usize = 6;
99
100 pub const fn index(self) -> usize {
102 match self {
103 Self::Sync => 0,
104 Self::Async => 1,
105 Self::SyncReply => 2,
106 Self::AsyncReply => 3,
107 Self::Lost => 4,
108 Self::Open => 5,
109 }
110 }
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
115pub enum FragmentKind {
116 Loop,
118 Alt,
120 Opt,
122 Par,
124 Critical,
126 Break,
128 Rect,
130}
131
132impl FragmentKind {
133 pub const INDEX_COUNT: usize = 7;
135
136 pub const fn index(self) -> usize {
138 match self {
139 Self::Loop => 0,
140 Self::Alt => 1,
141 Self::Opt => 2,
142 Self::Par => 3,
143 Self::Critical => 4,
144 Self::Break => 5,
145 Self::Rect => 6,
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
152pub enum NotePlacement {
153 LeftOf,
155 RightOf,
157 Over,
159}
160
161#[derive(Clone, Debug, PartialEq, Eq, Hash)]
166pub enum SequenceItemPath {
167 Message(usize),
169 SelfMessage(usize),
171 Participant(usize),
173 Note(usize),
175 Fragment(usize),
177 Divider(usize),
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Hash)]
183pub struct SequenceItemEvent {
184 pub path: SequenceItemPath,
186 pub label: Arc<str>,
188}
189
190#[derive(Clone, Debug, PartialEq, Eq, Hash)]
192pub struct SequenceMessage {
193 pub from: ActorRef,
195 pub to: ActorRef,
197 pub label: Arc<str>,
199 pub style: MessageStyle,
201 pub activate_target: bool,
203 pub deactivate_source: bool,
205 pub line_style: Option<Style>,
207 pub label_style: Option<Style>,
209}
210
211impl SequenceMessage {
212 pub fn new(
214 from: impl Into<ActorRef>,
215 to: impl Into<ActorRef>,
216 label: impl Into<Arc<str>>,
217 ) -> Self {
218 Self {
219 from: from.into(),
220 to: to.into(),
221 label: label.into(),
222 style: MessageStyle::Sync,
223 activate_target: false,
224 deactivate_source: false,
225 line_style: None,
226 label_style: None,
227 }
228 }
229
230 pub fn sync(
232 from: impl Into<ActorRef>,
233 to: impl Into<ActorRef>,
234 label: impl Into<Arc<str>>,
235 ) -> Self {
236 Self::new(from, to, label).message_style(MessageStyle::Sync)
237 }
238
239 pub fn async_(
241 from: impl Into<ActorRef>,
242 to: impl Into<ActorRef>,
243 label: impl Into<Arc<str>>,
244 ) -> Self {
245 Self::new(from, to, label).message_style(MessageStyle::Async)
246 }
247
248 pub fn reply(
250 from: impl Into<ActorRef>,
251 to: impl Into<ActorRef>,
252 label: impl Into<Arc<str>>,
253 ) -> Self {
254 Self::new(from, to, label).message_style(MessageStyle::SyncReply)
255 }
256
257 pub fn async_reply(
259 from: impl Into<ActorRef>,
260 to: impl Into<ActorRef>,
261 label: impl Into<Arc<str>>,
262 ) -> Self {
263 Self::new(from, to, label).message_style(MessageStyle::AsyncReply)
264 }
265
266 pub fn lost(
268 from: impl Into<ActorRef>,
269 to: impl Into<ActorRef>,
270 label: impl Into<Arc<str>>,
271 ) -> Self {
272 Self::new(from, to, label).message_style(MessageStyle::Lost)
273 }
274
275 pub fn open(
277 from: impl Into<ActorRef>,
278 to: impl Into<ActorRef>,
279 label: impl Into<Arc<str>>,
280 ) -> Self {
281 Self::new(from, to, label).message_style(MessageStyle::Open)
282 }
283
284 pub fn message_style(mut self, style: MessageStyle) -> Self {
286 self.style = style;
287 self
288 }
289
290 pub fn activate_target(mut self, activate: bool) -> Self {
292 self.activate_target = activate;
293 self
294 }
295
296 pub fn deactivate_source(mut self, deactivate: bool) -> Self {
298 self.deactivate_source = deactivate;
299 self
300 }
301
302 pub fn line_style(mut self, style: Style) -> Self {
304 self.line_style = Some(style);
305 self
306 }
307
308 pub fn label_style(mut self, style: Style) -> Self {
310 self.label_style = Some(style);
311 self
312 }
313}
314
315pub type Msg = SequenceMessage;
317
318#[derive(Clone, Debug, PartialEq, Eq, Hash)]
324pub enum SequenceStep {
325 Message(SequenceMessage),
327 SelfMessage {
329 actor: ActorRef,
331 label: Arc<str>,
333 style: Option<Style>,
335 },
336 Note {
338 placement: NotePlacement,
340 actors: Arc<[ActorRef]>,
342 text: Arc<str>,
344 style: Option<Style>,
346 },
347 Activate(ActorRef),
349 Deactivate(ActorRef),
351 FragmentBegin {
353 kind: FragmentKind,
355 label: Arc<str>,
357 branch_label: Option<Arc<str>>,
359 style: Option<Style>,
361 },
362 FragmentBranch {
364 kind: FragmentKind,
366 label: Arc<str>,
368 },
369 FragmentEnd,
371 Rect {
373 color: Style,
375 },
376 Divider(Arc<str>),
378}
379
380impl SequenceStep {
381 pub fn message(message: SequenceMessage) -> Self {
383 Self::Message(message)
384 }
385 pub fn self_msg(actor: impl Into<ActorRef>, label: impl Into<Arc<str>>) -> Self {
387 Self::SelfMessage {
388 actor: actor.into(),
389 label: label.into(),
390 style: None,
391 }
392 }
393 pub fn note_over(
395 actors: impl IntoIterator<Item = impl Into<ActorRef>>,
396 text: impl Into<Arc<str>>,
397 ) -> Self {
398 Self::Note {
399 placement: NotePlacement::Over,
400 actors: Arc::<[ActorRef]>::from(actors.into_iter().map(Into::into).collect::<Vec<_>>()),
401 text: text.into(),
402 style: None,
403 }
404 }
405 pub fn note(
407 placement: NotePlacement,
408 actors: impl IntoIterator<Item = impl Into<ActorRef>>,
409 text: impl Into<Arc<str>>,
410 ) -> Self {
411 Self::Note {
412 placement,
413 actors: Arc::<[ActorRef]>::from(actors.into_iter().map(Into::into).collect::<Vec<_>>()),
414 text: text.into(),
415 style: None,
416 }
417 }
418 pub fn activate(actor: impl Into<ActorRef>) -> Self {
420 Self::Activate(actor.into())
421 }
422 pub fn deactivate(actor: impl Into<ActorRef>) -> Self {
424 Self::Deactivate(actor.into())
425 }
426 pub fn fragment_begin(kind: FragmentKind, label: impl Into<Arc<str>>) -> Self {
428 Self::FragmentBegin {
429 kind,
430 label: label.into(),
431 branch_label: None,
432 style: None,
433 }
434 }
435 pub fn fragment_branch(kind: FragmentKind, label: impl Into<Arc<str>>) -> Self {
437 Self::FragmentBranch {
438 kind,
439 label: label.into(),
440 }
441 }
442 pub fn fragment_end() -> Self {
444 Self::FragmentEnd
445 }
446}
447
448pub type Step = SequenceStep;
450
451#[derive(Clone, Debug, PartialEq, Eq, Hash)]
452pub(crate) struct ParticipantSpec {
453 pub(crate) actor: ActorRef,
454 pub(crate) label: Arc<str>,
455 pub(crate) kind: ActorKind,
456}
457
458#[derive(Clone)]
460pub struct SequenceDiagram {
461 pub(crate) participants: Vec<ParticipantSpec>,
462 pub(crate) steps: Vec<SequenceStep>,
463 pub(crate) variant: SequenceDiagramVariant,
464 pub(crate) actor_glyph: Arc<str>,
465 pub(crate) style: Style,
466 pub(crate) theme: SequenceDiagramTheme,
467 pub(crate) border: bool,
468 pub(crate) border_style: BorderStyle,
469 pub(crate) padding: Padding,
470 pub(crate) width: Length,
471 pub(crate) height: Length,
472 pub(crate) max_label_cells: Option<u16>,
473 pub(crate) message_label_overflow: Overflow,
474 pub(crate) autonumber: bool,
475 pub(crate) repeat_participants_at_bottom: bool,
476 pub(crate) on_item_click: Option<Callback<SequenceItemEvent>>,
477 pub(crate) on_item_hover: Option<Callback<SequenceItemEvent>>,
478}
479
480impl Default for SequenceDiagram {
481 fn default() -> Self {
482 Self {
483 participants: Vec::new(),
484 steps: Vec::new(),
485 variant: SequenceDiagramVariant::Boxed,
486 actor_glyph: Arc::from("○ "),
487 style: Style::default(),
488 theme: SequenceDiagramTheme::classic(),
489 border: false,
490 border_style: BorderStyle::Plain,
491 padding: Padding::default(),
492 width: Length::Auto,
493 height: Length::Auto,
494 max_label_cells: Some(32),
495 message_label_overflow: Overflow::Ellipsis,
496 autonumber: false,
497 repeat_participants_at_bottom: false,
498 on_item_click: None,
499 on_item_hover: None,
500 }
501 }
502}
503
504impl SequenceDiagram {
505 pub fn new() -> Self {
507 Self::default()
508 }
509
510 pub fn participant(mut self, actor: impl Into<ActorRef>) -> Self {
512 let actor = actor.into();
513 let label = actor.0.clone();
514 self.upsert_participant(actor, label, ActorKind::Participant);
515 self
516 }
517
518 pub fn participant_aliased(
520 mut self,
521 alias: impl Into<ActorRef>,
522 label: impl Into<Arc<str>>,
523 ) -> Self {
524 self.upsert_participant(alias.into(), label.into(), ActorKind::Participant);
525 self
526 }
527
528 pub fn actor_kind(mut self, actor: impl Into<ActorRef>, kind: ActorKind) -> Self {
530 let actor = actor.into();
531 if let Some(participant) = self.participants.iter_mut().find(|p| p.actor == actor) {
532 participant.kind = kind;
533 } else {
534 let label = actor.0.clone();
535 self.upsert_participant(actor, label, kind);
536 }
537 self
538 }
539
540 pub fn step(mut self, step: SequenceStep) -> Self {
542 self.steps.push(step);
543 self
544 }
545 pub fn message(self, message: SequenceMessage) -> Self {
547 self.step(SequenceStep::Message(message))
548 }
549 pub fn self_msg(self, actor: impl Into<ActorRef>, label: impl Into<Arc<str>>) -> Self {
551 self.step(SequenceStep::self_msg(actor, label))
552 }
553 pub fn note_over(
555 self,
556 actors: impl IntoIterator<Item = impl Into<ActorRef>>,
557 text: impl Into<Arc<str>>,
558 ) -> Self {
559 self.step(SequenceStep::note_over(actors, text))
560 }
561 pub fn note_left_of(self, actor: impl Into<ActorRef>, text: impl Into<Arc<str>>) -> Self {
563 self.note_one(NotePlacement::LeftOf, actor, text)
564 }
565 pub fn note_right_of(self, actor: impl Into<ActorRef>, text: impl Into<Arc<str>>) -> Self {
567 self.note_one(NotePlacement::RightOf, actor, text)
568 }
569 pub fn activate(self, actor: impl Into<ActorRef>) -> Self {
571 self.step(SequenceStep::Activate(actor.into()))
572 }
573 pub fn deactivate(self, actor: impl Into<ActorRef>) -> Self {
575 self.step(SequenceStep::Deactivate(actor.into()))
576 }
577 pub fn fragment_begin(self, kind: FragmentKind, label: impl Into<Arc<str>>) -> Self {
580 self.step(SequenceStep::fragment_begin(kind, label))
581 }
582 pub fn fragment_branch(self, kind: FragmentKind, label: impl Into<Arc<str>>) -> Self {
584 self.step(SequenceStep::FragmentBranch {
585 kind,
586 label: label.into(),
587 })
588 }
589 pub fn fragment_end(self) -> Self {
591 self.step(SequenceStep::FragmentEnd)
592 }
593 pub fn rect(self, color: Style) -> Self {
595 self.step(SequenceStep::Rect { color })
596 }
597 pub fn divider(self, label: impl Into<Arc<str>>) -> Self {
599 self.step(SequenceStep::Divider(label.into()))
600 }
601
602 pub fn loop_(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
604 self.fragment(FragmentKind::Loop, label, f)
605 }
606 pub fn alt(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
609 self.fragment(FragmentKind::Alt, label, f)
610 }
611 pub fn else_(self, label: impl Into<Arc<str>>) -> Self {
613 self.fragment_branch(FragmentKind::Alt, label)
614 }
615 pub fn par(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
618 self.fragment(FragmentKind::Par, label, f)
619 }
620 pub fn and(self, label: impl Into<Arc<str>>) -> Self {
622 self.fragment_branch(FragmentKind::Par, label)
623 }
624 pub fn opt(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
626 self.fragment(FragmentKind::Opt, label, f)
627 }
628 pub fn critical(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
630 self.fragment(FragmentKind::Critical, label, f)
631 }
632 pub fn break_(self, label: impl Into<Arc<str>>, f: impl FnOnce(Self) -> Self) -> Self {
634 self.fragment(FragmentKind::Break, label, f)
635 }
636
637 pub fn style(mut self, style: Style) -> Self {
639 self.style = style;
640 self
641 }
642 pub fn participant_style(mut self, style: Style) -> Self {
644 self.theme.participant_style = style;
645 self
646 }
647 pub fn lifeline_style(mut self, style: Style) -> Self {
649 self.theme.lifeline.style = style;
650 self
651 }
652 pub fn message_label_style(mut self, style: Style) -> Self {
654 self.theme.message_label_style = style;
655 self
656 }
657 pub fn note_style(mut self, style: Style) -> Self {
659 self.theme.note_style = style;
660 self
661 }
662 pub fn fragment_style(mut self, style: Style) -> Self {
664 self.theme.fragment_styles.fill(style);
665 self
666 }
667 pub fn activation_style(mut self, style: Style) -> Self {
669 self.theme.activation.style = style;
670 self
671 }
672 pub fn item_hover_style(mut self, style: Style) -> Self {
674 self.theme.hover_style = style;
675 self
676 }
677 pub fn autonumber_style(mut self, style: Style) -> Self {
679 self.theme.autonumber.style = style;
680 self
681 }
682 pub fn theme(mut self, theme: SequenceDiagramTheme) -> Self {
684 self.theme = theme;
685 self
686 }
687 pub fn message_kind_style(mut self, kind: MessageStyle, style: Style) -> Self {
689 *self.theme.message_style_mut(kind) = style;
690 self
691 }
692 pub fn fragment_kind_style(mut self, kind: FragmentKind, style: Style) -> Self {
694 *self.theme.fragment_style_mut(kind) = style;
695 self
696 }
697 pub fn lifeline_glyph(mut self, glyph: char) -> Self {
699 self.theme.lifeline.glyph = glyph;
700 self
701 }
702 pub fn activation_glyph(mut self, glyph: char) -> Self {
704 self.theme.activation.fill_glyph = glyph;
705 self
706 }
707 pub fn autonumber_format(mut self, format: impl Into<Arc<str>>) -> Self {
709 self.theme.autonumber.format = format.into();
710 self
711 }
712 pub fn border(mut self, border: bool) -> Self {
714 self.border = border;
715 self
716 }
717 pub fn border_style(mut self, border_style: BorderStyle) -> Self {
719 self.border_style = border_style;
720 self
721 }
722 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
724 self.padding = padding.into();
725 self
726 }
727 pub fn width(mut self, width: Length) -> Self {
729 self.width = width;
730 self
731 }
732 pub fn height(mut self, height: Length) -> Self {
734 self.height = height;
735 self
736 }
737 pub fn max_label_cells(mut self, max_label_cells: Option<u16>) -> Self {
739 self.max_label_cells = max_label_cells.map(|cells| cells.max(1));
740 self
741 }
742 pub fn message_label_overflow(mut self, overflow: Overflow) -> Self {
744 self.message_label_overflow = overflow;
745 self
746 }
747 pub fn autonumber(mut self, autonumber: bool) -> Self {
749 self.autonumber = autonumber;
750 self
751 }
752 pub fn variant(mut self, variant: SequenceDiagramVariant) -> Self {
754 self.variant = variant;
755 self
756 }
757 pub fn minimal(self) -> Self {
759 self.variant(SequenceDiagramVariant::Minimal)
760 .theme(SequenceDiagramTheme::minimal())
761 }
762 pub fn boxed(self) -> Self {
764 self.variant(SequenceDiagramVariant::Boxed)
765 .theme(SequenceDiagramTheme::classic())
766 }
767 pub fn actor_glyph(mut self, glyph: impl Into<Arc<str>>) -> Self {
769 self.actor_glyph = glyph.into();
770 self
771 }
772 pub fn repeat_participants_at_bottom(mut self, repeat: bool) -> Self {
774 self.repeat_participants_at_bottom = repeat;
775 self
776 }
777 pub fn on_item_click(mut self, cb: Callback<SequenceItemEvent>) -> Self {
779 self.on_item_click = Some(cb);
780 self
781 }
782 pub fn on_item_hover(mut self, cb: Callback<SequenceItemEvent>) -> Self {
784 self.on_item_hover = Some(cb);
785 self
786 }
787
788 fn fragment(
789 self,
790 kind: FragmentKind,
791 label: impl Into<Arc<str>>,
792 f: impl FnOnce(Self) -> Self,
793 ) -> Self {
794 f(self.fragment_begin(kind, label)).fragment_end()
795 }
796
797 fn note_one(
798 self,
799 placement: NotePlacement,
800 actor: impl Into<ActorRef>,
801 text: impl Into<Arc<str>>,
802 ) -> Self {
803 self.step(SequenceStep::Note {
804 placement,
805 actors: Arc::<[ActorRef]>::from(vec![actor.into()]),
806 text: text.into(),
807 style: None,
808 })
809 }
810
811 fn upsert_participant(&mut self, actor: ActorRef, label: Arc<str>, kind: ActorKind) {
812 if let Some(participant) = self.participants.iter_mut().find(|p| p.actor == actor) {
813 participant.label = label;
814 participant.kind = kind;
815 } else {
816 self.participants
817 .push(ParticipantSpec { actor, label, kind });
818 }
819 }
820}
821
822impl From<SequenceDiagram> for Element {
823 fn from(value: SequenceDiagram) -> Self {
824 Element::new(ElementKind::SequenceDiagram(Box::new(value)))
825 }
826}