theater_cli/client/
cli_wrapper.rs1use std::net::SocketAddr;
4use theater_client::TheaterConnection;
5use theater_server::{ManagementCommand, ManagementResponse};
6
7use crate::config::Config;
8use crate::error::{CliError, CliResult};
9
10pub struct CliTheaterClient {
12 connection: TheaterConnection,
13 config: Config,
14 address: SocketAddr,
15}
16
17impl CliTheaterClient {
18 pub fn new(address: SocketAddr, config: Config) -> Self {
20 Self {
21 connection: TheaterConnection::new(address),
22 config,
23 address,
24 }
25 }
26
27 pub async fn send_command(
29 &mut self,
30 command: ManagementCommand,
31 ) -> CliResult<ManagementResponse> {
32 self.ensure_connected().await?;
34
35 tokio::time::timeout(self.config.server.timeout, self.connection.send(command))
37 .await
38 .map_err(|_| CliError::ConnectionTimeout {
39 timeout: self.config.server.timeout.as_secs(),
40 })?
41 .map_err(|e| CliError::connection_failed(self.address, e))?;
42
43 let response = tokio::time::timeout(self.config.server.timeout, self.connection.receive())
45 .await
46 .map_err(|_| CliError::ConnectionTimeout {
47 timeout: self.config.server.timeout.as_secs(),
48 })?
49 .map_err(|e| CliError::connection_failed(self.address, e))?;
50
51 Ok(response)
52 }
53
54 pub async fn send_command_no_response(&mut self, command: ManagementCommand) -> CliResult<()> {
56 self.ensure_connected().await?;
58
59 tokio::time::timeout(self.config.server.timeout, self.connection.send(command))
61 .await
62 .map_err(|_| CliError::ConnectionTimeout {
63 timeout: self.config.server.timeout.as_secs(),
64 })?
65 .map_err(|e| CliError::connection_failed(self.address, e))?;
66
67 Ok(())
68 }
69
70 pub async fn receive(&mut self) -> CliResult<ManagementResponse> {
72 let response = tokio::time::timeout(self.config.server.timeout, self.connection.receive())
73 .await
74 .map_err(|_| CliError::ConnectionTimeout {
75 timeout: self.config.server.timeout.as_secs(),
76 })?
77 .map_err(|e| CliError::connection_failed(self.address, e))?;
78
79 Ok(response)
80 }
81
82 async fn ensure_connected(&mut self) -> CliResult<()> {
84 if self.connection.is_connected() {
85 return Ok(());
86 }
87
88 let mut attempts = 0;
89 let max_attempts = self.config.server.retry_attempts;
90
91 while attempts < max_attempts {
92 match tokio::time::timeout(self.config.server.timeout, self.connection.connect()).await
93 {
94 Ok(Ok(())) => return Ok(()),
95 Ok(Err(e)) => {
96 attempts += 1;
97 if attempts >= max_attempts {
98 return Err(CliError::connection_failed(self.address, e));
99 }
100 tokio::time::sleep(self.config.server.retry_delay).await;
102 }
103 Err(_) => {
104 attempts += 1;
105 if attempts >= max_attempts {
106 return Err(CliError::ConnectionTimeout {
107 timeout: self.config.server.timeout.as_secs(),
108 });
109 }
110 tokio::time::sleep(self.config.server.retry_delay).await;
111 }
112 }
113 }
114
115 Err(CliError::ConnectionTimeout {
116 timeout: self.config.server.timeout.as_secs(),
117 })
118 }
119
120 pub fn is_connected(&self) -> bool {
122 self.connection.is_connected()
123 }
124
125 pub fn address(&self) -> SocketAddr {
127 self.address
128 }
129}
130
131pub async fn list_actors(
135 address: SocketAddr,
136 config: &Config,
137) -> CliResult<Vec<(theater::id::TheaterId, String)>> {
138 let mut client = CliTheaterClient::new(address, config.clone());
139 let response = client.send_command(ManagementCommand::ListActors).await?;
140
141 match response {
142 ManagementResponse::ActorList { actors } => Ok(actors),
143 ManagementResponse::Error { error } => Err(CliError::ServerError {
144 message: format!("{:?}", error),
145 }),
146 _ => Err(CliError::UnexpectedResponse {
147 response: format!("{:?}", response),
148 }),
149 }
150}
151
152pub async fn stop_actor(address: SocketAddr, config: &Config, actor_id: &str) -> CliResult<()> {
154 let mut client = CliTheaterClient::new(address, config.clone());
155 let theater_id = actor_id
156 .parse()
157 .map_err(|_| CliError::invalid_actor_id(actor_id))?;
158
159 let response = client
160 .send_command(ManagementCommand::StopActor { id: theater_id })
161 .await?;
162
163 match response {
164 ManagementResponse::ActorStopped { .. } => Ok(()),
165 ManagementResponse::Error { error } => {
166 let error_str = format!("{:?}", error);
167 if error_str.contains("not found") {
168 Err(CliError::actor_not_found(actor_id))
169 } else {
170 Err(CliError::ServerError { message: error_str })
171 }
172 }
173 _ => Err(CliError::UnexpectedResponse {
174 response: format!("{:?}", response),
175 }),
176 }
177}
178
179pub async fn get_actor_state(
181 address: SocketAddr,
182 config: &Config,
183 actor_id: &str,
184) -> CliResult<Option<Vec<u8>>> {
185 let mut client = CliTheaterClient::new(address, config.clone());
186 let theater_id = actor_id
187 .parse()
188 .map_err(|_| CliError::invalid_actor_id(actor_id))?;
189
190 let response = client
191 .send_command(ManagementCommand::GetActorState { id: theater_id })
192 .await?;
193
194 match response {
195 ManagementResponse::ActorState { state, .. } => Ok(state),
196 ManagementResponse::Error { error } => {
197 let error_str = format!("{:?}", error);
198 if error_str.contains("not found") {
199 Err(CliError::actor_not_found(actor_id))
200 } else {
201 Err(CliError::ServerError { message: error_str })
202 }
203 }
204 _ => Err(CliError::UnexpectedResponse {
205 response: format!("{:?}", response),
206 }),
207 }
208}