use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info, warn};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ServerState {
Creating,
Initializing,
Ready,
Running,
ShuttingDown,
Stopped,
Error,
}
impl ServerState {
pub fn can_accept_requests(&self) -> bool {
matches!(self, ServerState::Ready | ServerState::Running)
}
pub fn is_terminal(&self) -> bool {
matches!(self, ServerState::Stopped | ServerState::Error)
}
}
pub struct LifecycleManager {
state: Arc<RwLock<ServerState>>,
shutdown_requested: Arc<AtomicBool>,
graceful_shutdown: Arc<AtomicBool>,
}
impl LifecycleManager {
pub fn new() -> Self {
Self {
state: Arc::new(RwLock::new(ServerState::Creating)),
shutdown_requested: Arc::new(AtomicBool::new(false)),
graceful_shutdown: Arc::new(AtomicBool::new(true)),
}
}
pub async fn get_state(&self) -> ServerState {
let state_guard = self.state.read().await;
*state_guard
}
pub async fn can_accept_requests(&self) -> bool {
let state = self.get_state().await;
state.can_accept_requests()
}
pub fn is_shutdown_requested(&self) -> bool {
self.shutdown_requested.load(Ordering::SeqCst)
}
pub fn is_graceful_shutdown(&self) -> bool {
self.graceful_shutdown.load(Ordering::SeqCst)
}
pub async fn transition_to_initializing(&self) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
match *state_guard {
ServerState::Creating => {
*state_guard = ServerState::Initializing;
info!("Server state transitioned to Initializing");
Ok(())
}
current_state => {
warn!(
"Invalid state transition from {:?} to Initializing",
current_state
);
Err(LifecycleError::InvalidStateTransition {
from: current_state,
to: ServerState::Initializing,
})
}
}
}
pub async fn transition_to_ready(&self) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
match *state_guard {
ServerState::Initializing => {
*state_guard = ServerState::Ready;
info!("Server state transitioned to Ready");
Ok(())
}
current_state => {
warn!("Invalid state transition from {:?} to Ready", current_state);
Err(LifecycleError::InvalidStateTransition {
from: current_state,
to: ServerState::Ready,
})
}
}
}
pub async fn transition_to_running(&self) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
match *state_guard {
ServerState::Ready => {
*state_guard = ServerState::Running;
info!("Server state transitioned to Running");
Ok(())
}
current_state => {
warn!("Invalid state transition from {:?} to Running", current_state);
Err(LifecycleError::InvalidStateTransition {
from: current_state,
to: ServerState::Running,
})
}
}
}
pub async fn transition_to_shutting_down(&self) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
match *state_guard {
ServerState::Ready | ServerState::Running => {
*state_guard = ServerState::ShuttingDown;
info!("Server state transitioned to ShuttingDown");
Ok(())
}
current_state => {
debug!(
"State transition from {:?} to ShuttingDown allowed in emergency",
current_state
);
*state_guard = ServerState::ShuttingDown;
Ok(())
}
}
}
pub async fn transition_to_stopped(&self) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
match *state_guard {
ServerState::ShuttingDown => {
*state_guard = ServerState::Stopped;
info!("Server state transitioned to Stopped");
Ok(())
}
current_state => {
warn!("Invalid state transition from {:?} to Stopped", current_state);
Err(LifecycleError::InvalidStateTransition {
from: current_state,
to: ServerState::Stopped,
})
}
}
}
pub async fn transition_to_error(&self, error_reason: String) -> Result<(), LifecycleError> {
let mut state_guard = self.state.write().await;
let current_state = *state_guard;
*state_guard = ServerState::Error;
warn!(
"Server state transitioned to Error from {:?}: {}",
current_state, error_reason
);
Ok(())
}
pub fn request_shutdown(&self, graceful: bool) {
self.shutdown_requested.store(true, Ordering::SeqCst);
self.graceful_shutdown.store(graceful, Ordering::SeqCst);
if graceful {
info!("Graceful shutdown requested");
} else {
warn!("Immediate shutdown requested");
}
}
pub async fn reset(&self) {
let mut state_guard = self.state.write().await;
*state_guard = ServerState::Creating;
self.shutdown_requested.store(false, Ordering::SeqCst);
self.graceful_shutdown.store(true, Ordering::SeqCst);
debug!("Lifecycle manager reset");
}
pub async fn get_summary(&self) -> LifecycleSummary {
let state = self.get_state().await;
LifecycleSummary {
state,
shutdown_requested: self.is_shutdown_requested(),
graceful_shutdown: self.is_graceful_shutdown(),
can_accept_requests: state.can_accept_requests(),
is_terminal: state.is_terminal(),
}
}
pub async fn handle_stdin_closed(&self) {
info!("STDIN closed, initiating graceful shutdown");
self.request_shutdown(true);
let _ = self.transition_to_shutting_down().await;
}
pub async fn handle_fatal_error(&self, error: String) {
warn!("Fatal error occurred: {}", error);
let _ = self.transition_to_error(error).await;
}
}
impl Default for LifecycleManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct LifecycleSummary {
pub state: ServerState,
pub shutdown_requested: bool,
pub graceful_shutdown: bool,
pub can_accept_requests: bool,
pub is_terminal: bool,
}
#[derive(Debug, thiserror::Error)]
pub enum LifecycleError {
#[error("Invalid state transition from {from:?} to {to:?}")]
InvalidStateTransition { from: ServerState, to: ServerState },
#[error("Server is in terminal state: {state:?}")]
TerminalState { state: ServerState },
#[error("Operation not allowed in current state: {state:?}")]
OperationNotAllowed { state: ServerState },
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_lifecycle_manager_creation() {
let manager = LifecycleManager::new();
let state = manager.get_state().await;
assert_eq!(state, ServerState::Creating);
assert!(!manager.is_shutdown_requested());
assert!(manager.is_graceful_shutdown());
}
#[tokio::test]
async fn test_state_transitions() {
let manager = LifecycleManager::new();
assert!(manager.transition_to_initializing().await.is_ok());
assert_eq!(manager.get_state().await, ServerState::Initializing);
assert!(manager.transition_to_ready().await.is_ok());
assert_eq!(manager.get_state().await, ServerState::Ready);
assert!(manager.transition_to_running().await.is_ok());
assert_eq!(manager.get_state().await, ServerState::Running);
assert!(manager.transition_to_shutting_down().await.is_ok());
assert_eq!(manager.get_state().await, ServerState::ShuttingDown);
assert!(manager.transition_to_stopped().await.is_ok());
assert_eq!(manager.get_state().await, ServerState::Stopped);
}
#[tokio::test]
async fn test_invalid_state_transition() {
let manager = LifecycleManager::new();
let result = manager.transition_to_running().await;
assert!(result.is_err());
matches!(result.unwrap_err(), LifecycleError::InvalidStateTransition { .. });
}
#[tokio::test]
async fn test_can_accept_requests() {
let manager = LifecycleManager::new();
assert!(!manager.can_accept_requests().await);
assert!(manager.transition_to_initializing().await.is_ok());
assert!(manager.transition_to_ready().await.is_ok());
assert!(manager.can_accept_requests().await);
assert!(manager.transition_to_running().await.is_ok());
assert!(manager.can_accept_requests().await);
assert!(manager.transition_to_shutting_down().await.is_ok());
assert!(!manager.can_accept_requests().await);
}
#[tokio::test]
async fn test_shutdown_request() {
let manager = LifecycleManager::new();
assert!(!manager.is_shutdown_requested());
manager.request_shutdown(true);
assert!(manager.is_shutdown_requested());
assert!(manager.is_graceful_shutdown());
manager.request_shutdown(false);
assert!(manager.is_shutdown_requested());
assert!(!manager.is_graceful_shutdown());
}
#[tokio::test]
async fn test_error_state_transition() {
let manager = LifecycleManager::new();
let result = manager.transition_to_error("Test error".to_string()).await;
assert!(result.is_ok());
assert_eq!(manager.get_state().await, ServerState::Error);
}
#[tokio::test]
async fn test_lifecycle_summary() {
let manager = LifecycleManager::new();
let summary = manager.get_summary().await;
assert_eq!(summary.state, ServerState::Creating);
assert!(!summary.shutdown_requested);
assert!(summary.graceful_shutdown);
assert!(!summary.can_accept_requests);
assert!(!summary.is_terminal);
}
#[tokio::test]
async fn test_stdin_closed_handling() {
let manager = LifecycleManager::new();
assert!(manager.transition_to_initializing().await.is_ok());
assert!(manager.transition_to_ready().await.is_ok());
assert!(manager.transition_to_running().await.is_ok());
manager.handle_stdin_closed().await;
assert!(manager.is_shutdown_requested());
assert_eq!(manager.get_state().await, ServerState::ShuttingDown);
}
#[tokio::test]
async fn test_reset() {
let manager = LifecycleManager::new();
assert!(manager.transition_to_initializing().await.is_ok());
manager.request_shutdown(false);
manager.reset().await;
assert_eq!(manager.get_state().await, ServerState::Creating);
assert!(!manager.is_shutdown_requested());
assert!(manager.is_graceful_shutdown());
}
}