state.rs
1 /// Bot state management with typestate pattern 2 /// 3 /// Provides type-safe state transitions using phantom types to encode 4 /// bot lifecycle states at compile time. 5 6 use crate::Result; 7 use serde::{Deserialize, Serialize}; 8 use std::marker::PhantomData; 9 10 /// Bot state enumeration 11 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 12 pub enum BotState { 13 /// Initial state 14 Created, 15 /// Setup phase 16 Initializing, 17 /// Running and executing behaviors 18 Running, 19 /// Paused (can be resumed) 20 Paused, 21 /// Shutting down 22 Stopping, 23 /// Fully stopped 24 Stopped, 25 /// Error state 26 Error, 27 } 28 29 impl std::fmt::Display for BotState { 30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 31 match self { 32 BotState::Created => write!(f, "created"), 33 BotState::Initializing => write!(f, "initializing"), 34 BotState::Running => write!(f, "running"), 35 BotState::Paused => write!(f, "paused"), 36 BotState::Stopping => write!(f, "stopping"), 37 BotState::Stopped => write!(f, "stopped"), 38 BotState::Error => write!(f, "error"), 39 } 40 } 41 } 42 43 /// State transition result 44 #[derive(Debug, Clone, Serialize, Deserialize)] 45 pub struct StateTransition { 46 /// Previous state 47 pub from: BotState, 48 /// New state 49 pub to: BotState, 50 /// Timestamp of transition 51 pub timestamp_ms: i64, 52 /// Optional message 53 pub message: Option<String>, 54 } 55 56 impl StateTransition { 57 pub fn new(from: BotState, to: BotState) -> Self { 58 Self { 59 from, 60 to, 61 timestamp_ms: std::time::SystemTime::now() 62 .duration_since(std::time::UNIX_EPOCH) 63 .unwrap_or_default() 64 .as_millis() as i64, 65 message: None, 66 } 67 } 68 69 pub fn with_message(mut self, message: String) -> Self { 70 self.message = Some(message); 71 self 72 } 73 } 74 75 /// Type-safe state machine using phantom types 76 /// This allows compile-time enforcement of valid state transitions 77 pub struct StateMachine<S> { 78 current: BotState, 79 history: Vec<StateTransition>, 80 _marker: PhantomData<S>, 81 } 82 83 /// Phantom types for different states 84 pub struct Created; 85 pub struct Initializing; 86 pub struct Running; 87 pub struct Paused; 88 pub struct Stopping; 89 pub struct Stopped; 90 pub struct Error; 91 92 impl StateMachine<Created> { 93 pub fn new() -> Self { 94 Self { 95 current: BotState::Created, 96 history: Vec::new(), 97 _marker: PhantomData, 98 } 99 } 100 101 pub fn initialize(mut self) -> StateMachine<Initializing> { 102 let transition = StateTransition::new(self.current, BotState::Initializing); 103 self.history.push(transition); 104 StateMachine { 105 current: BotState::Initializing, 106 history: self.history, 107 _marker: PhantomData, 108 } 109 } 110 } 111 112 impl StateMachine<Initializing> { 113 pub fn start(mut self) -> StateMachine<Running> { 114 let transition = StateTransition::new(self.current, BotState::Running); 115 self.history.push(transition); 116 StateMachine { 117 current: BotState::Running, 118 history: self.history, 119 _marker: PhantomData, 120 } 121 } 122 123 pub fn fail(mut self, message: String) -> StateMachine<Error> { 124 let transition = StateTransition::new(self.current, BotState::Error) 125 .with_message(message); 126 self.history.push(transition); 127 StateMachine { 128 current: BotState::Error, 129 history: self.history, 130 _marker: PhantomData, 131 } 132 } 133 } 134 135 impl StateMachine<Running> { 136 pub fn pause(mut self) -> StateMachine<Paused> { 137 let transition = StateTransition::new(self.current, BotState::Paused); 138 self.history.push(transition); 139 StateMachine { 140 current: BotState::Paused, 141 history: self.history, 142 _marker: PhantomData, 143 } 144 } 145 146 pub fn stop(mut self) -> StateMachine<Stopping> { 147 let transition = StateTransition::new(self.current, BotState::Stopping); 148 self.history.push(transition); 149 StateMachine { 150 current: BotState::Stopping, 151 history: self.history, 152 _marker: PhantomData, 153 } 154 } 155 156 pub fn fail(mut self, message: String) -> StateMachine<Error> { 157 let transition = StateTransition::new(self.current, BotState::Error) 158 .with_message(message); 159 self.history.push(transition); 160 StateMachine { 161 current: BotState::Error, 162 history: self.history, 163 _marker: PhantomData, 164 } 165 } 166 } 167 168 impl StateMachine<Paused> { 169 pub fn resume(mut self) -> StateMachine<Running> { 170 let transition = StateTransition::new(self.current, BotState::Running); 171 self.history.push(transition); 172 StateMachine { 173 current: BotState::Running, 174 history: self.history, 175 _marker: PhantomData, 176 } 177 } 178 179 pub fn stop(mut self) -> StateMachine<Stopping> { 180 let transition = StateTransition::new(self.current, BotState::Stopping); 181 self.history.push(transition); 182 StateMachine { 183 current: BotState::Stopping, 184 history: self.history, 185 _marker: PhantomData, 186 } 187 } 188 } 189 190 impl StateMachine<Stopping> { 191 pub fn complete(mut self) -> StateMachine<Stopped> { 192 let transition = StateTransition::new(self.current, BotState::Stopped); 193 self.history.push(transition); 194 StateMachine { 195 current: BotState::Stopped, 196 history: self.history, 197 _marker: PhantomData, 198 } 199 } 200 } 201 202 impl<S> StateMachine<S> { 203 pub fn current_state(&self) -> BotState { 204 self.current 205 } 206 207 pub fn history(&self) -> &[StateTransition] { 208 &self.history 209 } 210 } 211 212 impl Default for StateMachine<Created> { 213 fn default() -> Self { 214 Self::new() 215 } 216 } 217 218 #[cfg(test)] 219 mod tests { 220 use super::*; 221 222 #[test] 223 fn test_state_machine_happy_path() { 224 // Created -> Initializing -> Running -> Stopping -> Stopped 225 let sm = StateMachine::new(); 226 assert_eq!(sm.current_state(), BotState::Created); 227 228 let sm = sm.initialize(); 229 assert_eq!(sm.current_state(), BotState::Initializing); 230 231 let sm = sm.start(); 232 assert_eq!(sm.current_state(), BotState::Running); 233 234 let sm = sm.stop(); 235 assert_eq!(sm.current_state(), BotState::Stopping); 236 237 let sm = sm.complete(); 238 assert_eq!(sm.current_state(), BotState::Stopped); 239 240 // Check history 241 assert_eq!(sm.history().len(), 4); 242 } 243 244 #[test] 245 fn test_state_machine_with_pause() { 246 let sm = StateMachine::new(); 247 let sm = sm.initialize(); 248 let sm = sm.start(); 249 250 let sm = sm.pause(); 251 assert_eq!(sm.current_state(), BotState::Paused); 252 253 let sm = sm.resume(); 254 assert_eq!(sm.current_state(), BotState::Running); 255 } 256 257 #[test] 258 fn test_state_machine_error() { 259 let sm = StateMachine::new(); 260 let sm = sm.initialize(); 261 let sm = sm.fail("Initialization failed".to_string()); 262 263 assert_eq!(sm.current_state(), BotState::Error); 264 265 let last_transition = sm.history().last().unwrap(); 266 assert_eq!(last_transition.message, Some("Initialization failed".to_string())); 267 } 268 }