theater_cli/commands/
state.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::ActorState;
8use crate::CommandContext;
9use theater::id::TheaterId;
10
11#[derive(Debug, Parser)]
12pub struct StateArgs {
13    /// ID of the actor to get state from
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    /// Output format (raw, json, pretty)
22    #[arg(short, long, default_value = "pretty")]
23    pub format: String,
24}
25
26/// Execute the state command asynchronously with modern patterns
27pub async fn execute_async(args: &StateArgs, ctx: &CommandContext) -> CliResult<()> {
28    debug!("Getting state for actor: {}", args.actor_id);
29    debug!("Connecting to server at: {}", args.address);
30
31    // Parse the actor ID
32    let actor_id = TheaterId::from_str(&args.actor_id).map_err(|_| CliError::InvalidInput {
33        field: "actor_id".to_string(),
34        value: args.actor_id.clone(),
35        suggestion: "Provide a valid actor ID in the correct format".to_string(),
36    })?;
37
38    // Create client and connect
39    let client = ctx.create_client();
40    client
41        .connect()
42        .await
43        .map_err(|e| CliError::connection_failed(args.address, e))?;
44
45    // Get the actor state
46    let state = client
47        .get_actor_state(&actor_id.to_string())
48        .await
49        .map_err(|e| CliError::ServerError {
50            message: format!("Failed to get actor state: {}", e),
51        })?;
52
53    // Create formatted output
54    let actor_state = ActorState {
55        actor_id: actor_id.to_string(),
56        state,
57    };
58
59    // Output using the configured format
60    let format = if ctx.json {
61        Some("json")
62    } else {
63        Some(args.format.as_str())
64    };
65    ctx.output.output(&actor_state, format)?;
66
67    Ok(())
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::config::Config;
74    use crate::output::OutputManager;
75
76    #[tokio::test]
77    async fn test_state_command_invalid_actor_id() {
78        let args = StateArgs {
79            actor_id: "invalid-id".to_string(),
80            address: "127.0.0.1:9000".parse().unwrap(),
81            format: "pretty".to_string(),
82        };
83        let config = Config::default();
84        let output = OutputManager::new(config.output.clone());
85
86        let ctx = CommandContext {
87            config,
88            output,
89            verbose: false,
90            json: false,
91        };
92
93        let result = execute_async(&args, &ctx).await;
94        assert!(result.is_err());
95        if let Err(CliError::InvalidInput { field, .. }) = result {
96            assert_eq!(field, "actor_id");
97        } else {
98            panic!("Expected InvalidInput error");
99        }
100    }
101}