theater_cli/commands/
stop.rs

1use clap::Parser;
2use std::net::SocketAddr;
3use std::str::FromStr;
4use tracing::debug;
5
6use crate::error::{CliError, CliResult};
7use crate::output::formatters::ActorAction;
8use crate::CommandContext;
9use theater::id::TheaterId;
10
11#[derive(Debug, Parser)]
12pub struct StopArgs {
13    /// ID of the actor to stop
14    #[arg(required = true)]
15    pub actor_id: String,
16
17    /// Address of the theater server
18    #[arg(short, long, default_value = "127.0.0.1:9000")]
19    pub address: SocketAddr,
20}
21
22/// Execute the stop command asynchronously with modern patterns
23pub async fn execute_async(args: &StopArgs, ctx: &CommandContext) -> CliResult<()> {
24    debug!("Stopping actor: {}", args.actor_id);
25    debug!("Connecting to server at: {}", args.address);
26
27    // Parse the actor ID
28    let actor_id = TheaterId::from_str(&args.actor_id).map_err(|_| CliError::InvalidInput {
29        field: "actor_id".to_string(),
30        value: args.actor_id.clone(),
31        suggestion: "Provide a valid actor ID in the correct format".to_string(),
32    })?;
33
34    // Create client and connect
35    let client = ctx.create_client();
36    client
37        .connect()
38        .await
39        .map_err(|e| CliError::connection_failed(args.address, e))?;
40
41    // Stop the actor
42    client
43        .stop_actor(&actor_id.to_string())
44        .await
45        .map_err(|e| CliError::ServerError {
46            message: format!("Failed to stop actor: {}", e),
47        })?;
48
49    // Create formatted output
50    let action_result = ActorAction {
51        action: "stopped".to_string(),
52        actor_id: actor_id.to_string(),
53        success: true,
54        message: None,
55    };
56
57    // Output using the configured format
58    let format = if ctx.json { Some("json") } else { None };
59    ctx.output.output(&action_result, format)?;
60
61    Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use crate::config::Config;
68    use crate::output::OutputManager;
69
70    #[tokio::test]
71    async fn test_stop_command_invalid_actor_id() {
72        let args = StopArgs {
73            actor_id: "invalid-id".to_string(),
74            address: "127.0.0.1:9000".parse().unwrap(),
75        };
76        let config = Config::default();
77        let output = OutputManager::new(config.output.clone());
78
79        let ctx = CommandContext {
80            config,
81            output,
82            verbose: false,
83            json: false,
84        };
85
86        let result = execute_async(&args, &ctx).await;
87        assert!(result.is_err());
88        if let Err(CliError::InvalidInput { field, .. }) = result {
89            assert_eq!(field, "actor_id");
90        } else {
91            panic!("Expected InvalidInput error");
92        }
93    }
94}