1use crate::cli::args::{Args, Command, ServerAction};
4use crate::client::ZinitClient;
5use anyhow::{Context, Result};
6use colored::Colorize;
7use std::process::Command as ProcessCommand;
8
9pub async fn run_command(args: &Args) -> Result<()> {
11 if args.stdin {
13 return run_stdin().await;
14 }
15
16 if let Some(script) = &args.inline_script {
18 return run_inline(script).await;
19 }
20
21 match &args.command {
22 Some(Command::Server { action }) => run_server_command(action).await,
23 Some(Command::List) => run_list().await,
24 Some(Command::Status { name }) => run_status(name).await,
25 Some(Command::Start { name }) => run_start(name).await,
26 Some(Command::Stop { name }) => run_stop(name).await,
27 Some(Command::Restart { name }) => run_restart(name).await,
28 Some(Command::Delete { name }) => run_delete(name).await,
29 Some(Command::Kill { name, signal }) => run_kill(name, signal).await,
30 Some(Command::Stats { name }) => run_stats(name).await,
31 Some(Command::Logs {
32 service,
33 follow: _,
34 lines,
35 }) => run_logs(service.as_deref(), *lines).await,
36 Some(Command::Repl) => run_repl().await,
37 Some(Command::Tui) => run_tui().await,
38 Some(Command::Ping) => run_ping().await,
39 None => {
40 if let Some(script) = &args.script {
41 run_script(script).await
42 } else {
43 println!("Use --help for usage information");
44 Ok(())
45 }
46 }
47 }
48}
49
50fn get_client() -> Result<ZinitClient> {
51 ZinitClient::try_default()
52}
53
54async fn run_server_command(action: &ServerAction) -> Result<()> {
55 match action {
56 ServerAction::Start { background, port } => {
57 let mut cmd = ProcessCommand::new("zinit-server");
58 if *background {
59 cmd.arg("-bg");
60 }
61 if let Some(p) = port {
62 cmd.arg("--port").arg(p.to_string());
63 }
64
65 let status = cmd.status().context("Failed to start zinit-server")?;
66 if !status.success() {
67 anyhow::bail!("zinit-server exited with error");
68 }
69 Ok(())
70 }
71 ServerAction::Stop => {
72 let _ = ProcessCommand::new("pkill")
73 .args(["-f", "zinit-server"])
74 .status();
75 println!("Server stopped");
76 Ok(())
77 }
78 ServerAction::Restart => {
79 let _ = ProcessCommand::new("pkill")
80 .args(["-f", "zinit-server"])
81 .status();
82 std::thread::sleep(std::time::Duration::from_millis(500));
83
84 let status = ProcessCommand::new("zinit-server")
85 .arg("-bg")
86 .status()
87 .context("Failed to start zinit-server")?;
88 if !status.success() {
89 anyhow::bail!("zinit-server exited with error");
90 }
91 println!("Server restarted");
92 Ok(())
93 }
94 }
95}
96
97async fn run_list() -> Result<()> {
98 let client = get_client()?;
99 let services = client.list().await?;
100
101 if services.is_empty() {
102 println!("No services registered");
103 return Ok(());
104 }
105
106 println!("{}", "Services:".bold());
107 for name in &services {
108 match client.status(name).await {
109 Ok(status) => {
110 let is_sleeping = status.state.contains("143")
116 || status.state.contains("137")
117 || status.state.contains("SIGTERM")
118 || status.state.contains("SIGKILL");
119
120 let state_colored = if is_sleeping {
121 "Sleeping".blue()
122 } else {
123 match status.state.as_str() {
124 s if s.contains("Running") => status.state.green(),
125 s if s.contains("Success") => status.state.green(),
126 s if s.contains("Blocked") => status.state.yellow(),
127 s if s.contains("Spawned") => status.state.cyan(),
128 _ => status.state.red(),
129 }
130 };
131 println!(" {} {} (pid: {})", name.bold(), state_colored, status.pid);
132 }
133 Err(_) => {
134 println!(" {} {}", name.bold(), "unknown".dimmed());
135 }
136 }
137 }
138
139 Ok(())
140}
141
142async fn run_status(name: &str) -> Result<()> {
143 let client = get_client()?;
144 let status = client.status(name).await?;
145
146 println!("{}: {}", "Service".bold(), name);
147 println!(" {}: {}", "State".bold(), status.state);
148 println!(" {}: {}", "Target".bold(), status.target);
149 println!(" {}: {}", "PID".bold(), status.pid);
150
151 Ok(())
152}
153
154async fn run_start(name: &str) -> Result<()> {
155 let client = get_client()?;
156 client.start(name).await?;
157 println!("Started service: {}", name.green());
158 Ok(())
159}
160
161async fn run_stop(name: &str) -> Result<()> {
162 let client = get_client()?;
163 client.stop(name).await?;
164 println!("Stopped service: {}", name.yellow());
165 Ok(())
166}
167
168async fn run_restart(name: &str) -> Result<()> {
169 let client = get_client()?;
170 client.restart(name).await?;
171 println!("Restarted service: {}", name.green());
172 Ok(())
173}
174
175async fn run_delete(name: &str) -> Result<()> {
176 let client = get_client()?;
177 client.delete(name).await?;
178 println!("Deleted service: {}", name.red());
179 Ok(())
180}
181
182async fn run_kill(name: &str, signal: &str) -> Result<()> {
183 let client = get_client()?;
184 client.kill(name, signal).await?;
185 println!("Sent {} to service: {}", signal, name);
186 Ok(())
187}
188
189async fn run_stats(name: &str) -> Result<()> {
190 let client = get_client()?;
191 let stats = client.stats(name).await?;
192
193 println!("{}: {}", "Service".bold(), name);
194 println!(" {}: {}", "PID".bold(), stats.pid);
195 println!(
196 " {}: {} MB",
197 "Memory".bold(),
198 stats.memory_usage / 1024 / 1024
199 );
200 println!(" {}: {:.1}%", "CPU".bold(), stats.cpu_usage);
201
202 if !stats.children.is_empty() {
203 println!(" {}:", "Children".bold());
204 for child in &stats.children {
205 println!(
206 " PID {}: {} MB, {:.1}% CPU",
207 child.pid,
208 child.memory_usage / 1024 / 1024,
209 child.cpu_usage
210 );
211 }
212 }
213
214 Ok(())
215}
216
217async fn run_logs(service: Option<&str>, lines: u32) -> Result<()> {
218 let client = get_client()?;
219
220 let logs = if let Some(name) = service {
221 client.logs_filter(name).await?
222 } else {
223 client.logs_tail(lines).await?
224 };
225
226 for line in logs {
227 println!("{}", line);
228 }
229
230 Ok(())
231}
232
233async fn run_script(path: &str) -> Result<()> {
234 use std::path::Path;
235
236 let script_path = Path::new(path);
237
238 if script_path.is_dir() {
240 let mut scripts: Vec<_> = std::fs::read_dir(script_path)?
241 .filter_map(|e| e.ok())
242 .filter(|e| {
243 e.path()
244 .extension()
245 .map(|ext| ext == "rhai")
246 .unwrap_or(false)
247 })
248 .collect();
249
250 scripts.sort_by_key(|e| e.path());
251
252 if scripts.is_empty() {
253 println!("No .rhai scripts found in {}", path);
254 return Ok(());
255 }
256
257 for entry in scripts {
258 let file_path = entry.path();
259 println!("{} {}", "Running:".green(), file_path.display());
260 match crate::rhai::run_script_file(&file_path) {
261 Ok(result) => {
262 if !result.is_unit() {
263 println!("{} {:?}", "=>".green(), result);
264 }
265 }
266 Err(e) => {
267 println!("{}: {}", "Error".red(), e);
268 }
269 }
270 }
271 } else {
272 match crate::rhai::run_script_file(script_path) {
274 Ok(result) => {
275 if !result.is_unit() {
276 println!("{} {:?}", "=>".green(), result);
277 }
278 }
279 Err(e) => {
280 println!("{}: {}", "Error".red(), e);
281 anyhow::bail!("Script execution failed");
282 }
283 }
284 }
285
286 Ok(())
287}
288
289async fn run_stdin() -> Result<()> {
290 use std::io::Read;
291 let mut script = String::new();
292 std::io::stdin()
293 .read_to_string(&mut script)
294 .context("Failed to read from stdin")?;
295
296 match crate::rhai::run_script(&script) {
297 Ok(result) => {
298 if !result.is_unit() {
299 println!("{} {:?}", "=>".green(), result);
300 }
301 Ok(())
302 }
303 Err(e) => {
304 println!("{}: {}", "Error".red(), e);
305 anyhow::bail!("Script execution failed");
306 }
307 }
308}
309
310async fn run_inline(script: &str) -> Result<()> {
311 match crate::rhai::run_script(script) {
312 Ok(result) => {
313 if !result.is_unit() {
314 println!("{} {:?}", "=>".green(), result);
315 }
316 Ok(())
317 }
318 Err(e) => {
319 println!("{}: {}", "Error".red(), e);
320 anyhow::bail!("Script execution failed");
321 }
322 }
323}
324
325async fn run_repl() -> Result<()> {
326 let handle = crate::client::ZinitHandle::new()?;
327 crate::repl::run_repl(handle)?;
328 Ok(())
329}
330
331async fn run_tui() -> Result<()> {
332 let handle = crate::client::ZinitHandle::new()?;
333 crate::tui::run_tui(handle)?;
334 Ok(())
335}
336
337async fn run_ping() -> Result<()> {
338 let client = get_client()?;
339 let response = client.ping().await?;
340 println!(
341 "{}: {} (version {})",
342 "Server".bold(),
343 response.message.green(),
344 response.version
345 );
346 Ok(())
347}