1use anyhow::{anyhow, Result};
7use bytes::Bytes;
8use futures::sink::SinkExt;
9use futures::stream::StreamExt;
10use std::net::SocketAddr;
11use tokio::net::TcpStream;
12use tokio_util::codec::Framed;
13use tracing::{debug, error, info};
14
15use theater_server::{FragmentingCodec, ManagementCommand, ManagementResponse};
16
17#[derive(Debug)]
22pub struct TheaterConnection {
23 pub address: SocketAddr,
25 connection: Option<Framed<TcpStream, FragmentingCodec>>,
27}
28
29impl TheaterConnection {
30 pub fn new(address: SocketAddr) -> Self {
40 Self {
41 address,
42 connection: None,
43 }
44 }
45
46 pub async fn connect(&mut self) -> Result<()> {
53 if self.connection.is_some() {
54 return Ok(());
55 }
56
57 info!("Connecting to Theater server at {}", self.address);
58 let socket = TcpStream::connect(self.address).await?;
59
60 let codec = FragmentingCodec::new();
61 let framed = Framed::new(socket, codec);
62
63 self.connection = Some(framed);
64 info!("Connected to Theater server");
65
66 Ok(())
67 }
68
69 pub async fn send(&mut self, command: ManagementCommand) -> Result<()> {
80 if self.connection.is_none() {
82 self.connect().await?;
83 }
84
85 debug!("Sending command: {:?}", command);
87 let command_bytes = serde_json::to_vec(&command)?;
88
89 let connection = self
90 .connection
91 .as_mut()
92 .ok_or_else(|| anyhow!("Connection lost"))?;
93
94 connection.send(Bytes::from(command_bytes)).await?;
95 debug!("Command sent");
96
97 Ok(())
98 }
99
100 pub async fn receive(&mut self) -> Result<ManagementResponse> {
110 if self.connection.is_none() {
112 return Err(anyhow!("Not connected"));
113 }
114
115 let connection = self
116 .connection
117 .as_mut()
118 .ok_or_else(|| anyhow!("Connection lost"))?;
119
120 match connection.next().await {
122 Some(Ok(bytes)) => {
123 let response: ManagementResponse = serde_json::from_slice(&bytes)?;
125 debug!("Received response: {:?}", response);
126 Ok(response)
127 }
128 Some(Err(e)) => {
129 error!("Error receiving response: {}", e);
130 self.connection = None;
131 Err(anyhow!("Connection error: {}", e))
132 }
133 None => {
134 debug!("Connection closed by server");
136 self.connection = None;
137 Err(anyhow!("Connection closed by server"))
138 }
139 }
140 }
141
142 pub async fn send_and_receive(
143 &mut self,
144 command: ManagementCommand,
145 ) -> Result<ManagementResponse> {
146 self.send(command).await?;
148
149 self.receive().await
151 }
152
153 pub fn is_connected(&self) -> bool {
160 self.connection.is_some()
161 }
162
163 pub async fn close(&mut self) -> Result<()> {
170 if let Some(mut connection) = self.connection.take() {
171 connection.close().await?;
172 }
173 Ok(())
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use std::time::Duration;
181 use tokio::net::TcpListener;
182 use tokio::sync::oneshot;
183
184 async fn run_mock_server(addr: SocketAddr, shutdown_rx: oneshot::Receiver<()>) -> Result<()> {
186 let listener = TcpListener::bind(addr).await?;
187 let shutdown_future = shutdown_rx;
188
189 tokio::select! {
190 _ = async {
191 while let Ok((socket, _)) = listener.accept().await {
192 let mut framed = Framed::new(socket, FragmentingCodec::new());
193
194 while let Some(Ok(bytes)) = framed.next().await {
196 framed.send(bytes.into()).await?;
197 }
198 }
199 Ok::<(), anyhow::Error>(())
200 } => {},
201 _ = shutdown_future => {},
202 }
203
204 Ok(())
205 }
206
207 #[tokio::test]
208 async fn test_connection() -> Result<()> {
209 let listener = TcpListener::bind("127.0.0.1:0").await?;
211 let addr = listener.local_addr()?;
212 drop(listener);
213
214 let (shutdown_tx, shutdown_rx) = oneshot::channel();
216 let server_handle = tokio::spawn(run_mock_server(addr, shutdown_rx));
217
218 tokio::time::sleep(Duration::from_millis(100)).await;
220
221 let mut client = TheaterConnection::new(addr);
223 client.connect().await?;
224 assert!(client.is_connected());
225
226 client.close().await?;
228 let _ = shutdown_tx.send(());
229 let _ = server_handle.await;
230
231 Ok(())
232 }
233}