de_mls/conversation/
state_machine.rs1use std::fmt::Display;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ConversationState {
8 Working,
10 Freezing,
13 Selection,
16 Reelection,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub enum OperatingMode {
29 #[default]
30 Normal,
31 Recovery,
32}
33
34impl Display for ConversationState {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 ConversationState::Working => write!(f, "Working"),
38 ConversationState::Freezing => write!(f, "Freezing"),
39 ConversationState::Selection => write!(f, "Selection"),
40 ConversationState::Reelection => write!(f, "Reelection"),
41 }
42 }
43}
44
45#[derive(Debug, Clone)]
46pub struct ConversationStateMachine {
47 state: ConversationState,
48}
49
50impl Default for ConversationStateMachine {
51 fn default() -> Self {
52 Self::new_as_member()
53 }
54}
55
56impl ConversationStateMachine {
57 pub fn new_as_member() -> Self {
59 Self {
60 state: ConversationState::Working,
61 }
62 }
63
64 pub fn current_state(&self) -> ConversationState {
65 self.state
66 }
67
68 pub fn start_working(&mut self) {
69 self.state = ConversationState::Working;
70 }
71
72 pub fn start_freezing(&mut self) -> bool {
75 match self.state {
76 ConversationState::Working | ConversationState::Reelection => {
77 self.state = ConversationState::Freezing;
78 true
79 }
80 _ => false,
81 }
82 }
83
84 pub fn start_selection(&mut self) {
85 self.state = ConversationState::Selection;
86 }
87
88 pub fn start_reelection(&mut self) {
89 self.state = ConversationState::Reelection;
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn new_as_member_starts_working() {
99 let sm = ConversationStateMachine::new_as_member();
100 assert_eq!(sm.current_state(), ConversationState::Working);
101 }
102
103 #[test]
104 fn named_transitions_set_state() {
105 let mut sm = ConversationStateMachine::new_as_member();
106 assert!(sm.start_freezing());
107 assert_eq!(sm.current_state(), ConversationState::Freezing);
108 sm.start_selection();
109 assert_eq!(sm.current_state(), ConversationState::Selection);
110 sm.start_reelection();
111 assert_eq!(sm.current_state(), ConversationState::Reelection);
112 sm.start_working();
113 assert_eq!(sm.current_state(), ConversationState::Working);
114 }
115
116 #[test]
117 fn start_freezing_from_working_transitions() {
118 let mut sm = ConversationStateMachine::new_as_member();
119 assert!(sm.start_freezing());
120 assert_eq!(sm.current_state(), ConversationState::Freezing);
121 }
122
123 #[test]
124 fn start_freezing_from_reelection_transitions() {
125 let mut sm = ConversationStateMachine::new_as_member();
126 sm.start_reelection();
127 assert!(sm.start_freezing());
128 assert_eq!(sm.current_state(), ConversationState::Freezing);
129 }
130
131 #[test]
133 fn start_freezing_noop_outside_working_reelection() {
134 for setup in [
135 |sm: &mut ConversationStateMachine| {
136 sm.start_freezing();
137 },
138 |sm: &mut ConversationStateMachine| sm.start_selection(),
139 ] {
140 let mut sm = ConversationStateMachine::new_as_member();
141 setup(&mut sm);
142 let before = sm.current_state();
143 assert!(!sm.start_freezing());
144 assert_eq!(sm.current_state(), before);
145 }
146 }
147}