rush_sync_server/commands/cleanup/
command.rs1use crate::commands::command::Command;
3use crate::core::prelude::*;
4use crate::server::types::{ServerContext, ServerStatus};
5use std::future::Future;
6use std::pin::Pin;
7use std::time::Duration;
8use tokio::time::timeout;
9
10#[derive(Debug, Default)]
11pub struct CleanupCommand;
12
13impl CleanupCommand {
14 pub fn new() -> Self {
15 Self
16 }
17}
18
19impl Command for CleanupCommand {
20 fn name(&self) -> &'static str {
21 "cleanup"
22 }
23 fn description(&self) -> &'static str {
24 "Clean up stopped or failed servers"
25 }
26 fn matches(&self, command: &str) -> bool {
27 command.trim().to_lowercase().starts_with("cleanup")
28 }
29
30 fn execute_sync(&self, args: &[&str]) -> Result<String> {
31 let ctx = crate::server::shared::get_shared_context();
32
33 match args.first() {
34 Some(&"failed") => Ok(self.cleanup_failed_servers(ctx)),
35 Some(&"stopped") | None => Ok(self.cleanup_stopped_servers(ctx)),
36 Some(&"all") => {
37 let stopped = self.cleanup_stopped_servers(ctx);
38 let failed = self.cleanup_failed_servers(ctx);
39 Ok(format!("{}\n{}", stopped, failed))
40 }
41 _ => Err(AppError::Validation(
42 "Usage: cleanup [stopped|failed|all]".to_string(),
43 )),
44 }
45 }
46
47 fn execute_async<'a>(
48 &'a self,
49 args: &'a [&'a str],
50 ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
51 Box::pin(async move { self.execute_sync(args) })
52 }
53
54 fn supports_async(&self) -> bool {
55 true
56 }
57 fn priority(&self) -> u8 {
58 50
59 }
60}
61
62impl CleanupCommand {
63 fn cleanup_stopped_servers(&self, ctx: &ServerContext) -> String {
64 let mut servers = ctx.servers.write().unwrap();
65 let initial_count = servers.len();
66 servers.retain(|_, server| server.status != ServerStatus::Stopped);
67 let removed_count = initial_count - servers.len();
68
69 if removed_count > 0 {
70 format!("{} gestoppte Server entfernt", removed_count)
71 } else {
72 "Keine gestoppten Server zum Entfernen gefunden".to_string()
73 }
74 }
75
76 fn cleanup_failed_servers(&self, ctx: &ServerContext) -> String {
77 let mut servers = ctx.servers.write().unwrap();
78 let initial_count = servers.len();
79 servers.retain(|_, server| server.status != ServerStatus::Failed);
80 let removed_count = initial_count - servers.len();
81
82 if removed_count > 0 {
83 format!("{} fehlgeschlagene Server entfernt", removed_count)
84 } else {
85 "Keine fehlgeschlagenen Server zum Entfernen gefunden".to_string()
86 }
87 }
88
89 pub async fn shutdown_all_servers(&self, ctx: &ServerContext) -> Result<()> {
90 let handles: Vec<_> = {
91 let mut handles_guard = ctx.handles.write().unwrap();
92 handles_guard.drain().collect()
93 };
94
95 let shutdown_futures: Vec<_> = handles
96 .into_iter()
97 .map(|(id, handle)| async move {
98 match timeout(Duration::from_secs(5), handle.stop(true)).await {
99 Ok(_) => log::info!("Server {} stopped", id),
100 Err(_) => {
101 log::warn!("Force stopping server {}", id);
102 handle.stop(false).await;
103 }
104 }
105 })
106 .collect();
107
108 futures::future::join_all(shutdown_futures).await;
109
110 {
111 let mut servers = ctx.servers.write().unwrap();
112 for server in servers.values_mut() {
113 server.status = ServerStatus::Stopped;
114 }
115 }
116 Ok(())
117 }
118}