use std::fmt::Display;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConversationState {
PendingJoin,
Working,
Freezing,
Selection,
Reelection,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OperatingMode {
#[default]
Normal,
Recovery,
}
impl Display for ConversationState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConversationState::PendingJoin => write!(f, "PendingJoin"),
ConversationState::Working => write!(f, "Working"),
ConversationState::Freezing => write!(f, "Freezing"),
ConversationState::Selection => write!(f, "Selection"),
ConversationState::Reelection => write!(f, "Reelection"),
}
}
}
#[derive(Debug, Clone)]
pub struct ConversationStateMachine {
state: ConversationState,
}
impl Default for ConversationStateMachine {
fn default() -> Self {
Self::new_as_member()
}
}
impl ConversationStateMachine {
pub fn new_as_member() -> Self {
Self {
state: ConversationState::Working,
}
}
pub fn new_as_pending_join() -> Self {
Self {
state: ConversationState::PendingJoin,
}
}
pub fn current_state(&self) -> ConversationState {
self.state
}
pub fn start_working(&mut self) {
self.state = ConversationState::Working;
}
pub fn start_freezing(&mut self) {
self.state = ConversationState::Freezing;
}
pub fn force_freezing(&mut self) -> bool {
match self.state {
ConversationState::Working | ConversationState::Reelection => {
self.state = ConversationState::Freezing;
true
}
_ => false,
}
}
pub fn start_selection(&mut self) {
self.state = ConversationState::Selection;
}
pub fn start_reelection(&mut self) {
self.state = ConversationState::Reelection;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_as_member_starts_working() {
let sm = ConversationStateMachine::new_as_member();
assert_eq!(sm.current_state(), ConversationState::Working);
}
#[test]
fn new_as_pending_join_starts_pending() {
let sm = ConversationStateMachine::new_as_pending_join();
assert_eq!(sm.current_state(), ConversationState::PendingJoin);
}
#[test]
fn named_transitions_set_state() {
let mut sm = ConversationStateMachine::new_as_member();
sm.start_freezing();
assert_eq!(sm.current_state(), ConversationState::Freezing);
sm.start_selection();
assert_eq!(sm.current_state(), ConversationState::Selection);
sm.start_reelection();
assert_eq!(sm.current_state(), ConversationState::Reelection);
sm.start_working();
assert_eq!(sm.current_state(), ConversationState::Working);
}
#[test]
fn force_freezing_from_working_transitions() {
let mut sm = ConversationStateMachine::new_as_member();
assert!(sm.force_freezing());
assert_eq!(sm.current_state(), ConversationState::Freezing);
}
#[test]
fn force_freezing_from_reelection_transitions() {
let mut sm = ConversationStateMachine::new_as_member();
sm.start_reelection();
assert!(sm.force_freezing());
assert_eq!(sm.current_state(), ConversationState::Freezing);
}
#[test]
fn force_freezing_noop_outside_working_reelection() {
for setup in [
|sm: &mut ConversationStateMachine| sm.start_freezing(),
|sm: &mut ConversationStateMachine| sm.start_selection(),
] {
let mut sm = ConversationStateMachine::new_as_member();
setup(&mut sm);
let before = sm.current_state();
assert!(!sm.force_freezing());
assert_eq!(sm.current_state(), before);
}
}
}