de_mls/core/conversation/
state_machine.rs1use std::fmt::Display;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ConversationState {
14 PendingJoin,
16 Working,
18 Freezing,
21 Selection,
24 Reelection,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
37pub enum OperatingMode {
38 #[default]
39 Normal,
40 Recovery,
41}
42
43impl Display for ConversationState {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match self {
46 ConversationState::PendingJoin => write!(f, "PendingJoin"),
47 ConversationState::Working => write!(f, "Working"),
48 ConversationState::Freezing => write!(f, "Freezing"),
49 ConversationState::Selection => write!(f, "Selection"),
50 ConversationState::Reelection => write!(f, "Reelection"),
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
56pub struct ConversationStateMachine {
57 state: ConversationState,
58}
59
60impl Default for ConversationStateMachine {
61 fn default() -> Self {
62 Self::new_as_member()
63 }
64}
65
66impl ConversationStateMachine {
67 pub fn new_as_member() -> Self {
69 Self {
70 state: ConversationState::Working,
71 }
72 }
73
74 pub fn new_as_pending_join() -> Self {
76 Self {
77 state: ConversationState::PendingJoin,
78 }
79 }
80
81 pub fn current_state(&self) -> ConversationState {
82 self.state
83 }
84
85 pub fn start_working(&mut self) {
86 self.state = ConversationState::Working;
87 }
88
89 pub fn start_freezing(&mut self) {
90 self.state = ConversationState::Freezing;
91 }
92
93 pub fn force_freezing(&mut self) -> bool {
97 match self.state {
98 ConversationState::Working | ConversationState::Reelection => {
99 self.state = ConversationState::Freezing;
100 true
101 }
102 _ => false,
103 }
104 }
105
106 pub fn start_selection(&mut self) {
107 self.state = ConversationState::Selection;
108 }
109
110 pub fn start_reelection(&mut self) {
111 self.state = ConversationState::Reelection;
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn new_as_member_starts_working() {
121 let sm = ConversationStateMachine::new_as_member();
122 assert_eq!(sm.current_state(), ConversationState::Working);
123 }
124
125 #[test]
126 fn new_as_pending_join_starts_pending() {
127 let sm = ConversationStateMachine::new_as_pending_join();
128 assert_eq!(sm.current_state(), ConversationState::PendingJoin);
129 }
130
131 #[test]
132 fn named_transitions_set_state() {
133 let mut sm = ConversationStateMachine::new_as_member();
134 sm.start_freezing();
135 assert_eq!(sm.current_state(), ConversationState::Freezing);
136 sm.start_selection();
137 assert_eq!(sm.current_state(), ConversationState::Selection);
138 sm.start_reelection();
139 assert_eq!(sm.current_state(), ConversationState::Reelection);
140 sm.start_working();
141 assert_eq!(sm.current_state(), ConversationState::Working);
142 }
143
144 #[test]
145 fn force_freezing_from_working_transitions() {
146 let mut sm = ConversationStateMachine::new_as_member();
147 assert!(sm.force_freezing());
148 assert_eq!(sm.current_state(), ConversationState::Freezing);
149 }
150
151 #[test]
152 fn force_freezing_from_reelection_transitions() {
153 let mut sm = ConversationStateMachine::new_as_member();
154 sm.start_reelection();
155 assert!(sm.force_freezing());
156 assert_eq!(sm.current_state(), ConversationState::Freezing);
157 }
158
159 #[test]
161 fn force_freezing_noop_outside_working_reelection() {
162 for setup in [
163 |sm: &mut ConversationStateMachine| sm.start_freezing(),
164 |sm: &mut ConversationStateMachine| sm.start_selection(),
165 ] {
166 let mut sm = ConversationStateMachine::new_as_member();
167 setup(&mut sm);
168 let before = sm.current_state();
169 assert!(!sm.force_freezing());
170 assert_eq!(sm.current_state(), before);
171 }
172 }
173}