1use async_trait::async_trait;
7use std::io::{Error, ErrorKind, Result};
8use tokio::{
9 io::{AsyncBufReadExt, AsyncWriteExt},
10 net::TcpStream,
11};
12
13use crate::{
14 request::{Request, RequestWriter},
15 response::{Response, ResponseReader},
16 tcp::TcpHandler,
17 timer::Timer,
18};
19
20use super::{Client, ClientStream};
21
22pub struct TcpClient {
27 pub host: String,
29
30 pub port: u16,
32}
33
34impl TcpClient {
35 pub fn new(host: impl ToString, port: u16) -> Box<dyn Client> {
37 Box::new(Self {
38 host: host.to_string(),
39 port,
40 })
41 }
42}
43
44#[async_trait]
45impl Client for TcpClient {
46 async fn send(&self, req: Request) -> Result<Response> {
48 let stream = TcpStream::connect((self.host.as_str(), self.port)).await?;
49 let mut handler = TcpHandler::from(stream);
50 handler.handle(req).await
51 }
52}
53
54#[async_trait]
55impl RequestWriter for TcpHandler {
56 async fn write(&mut self, req: Request) -> Result<()> {
57 let req = match req {
58 Request::Start => format!("start\n"),
59 Request::Get => format!("get\n"),
60 Request::Set(duration) => format!("set {duration}\n"),
61 Request::Pause => format!("pause\n"),
62 Request::Resume => format!("resume\n"),
63 Request::Stop => format!("stop\n"),
64 };
65
66 self.writer.write_all(req.as_bytes()).await?;
67
68 Ok(())
69 }
70}
71
72#[async_trait]
73impl ResponseReader for TcpHandler {
74 async fn read(&mut self) -> Result<Response> {
75 let mut res = String::new();
76 self.reader.read_line(&mut res).await?;
77
78 let mut tokens = res.split_whitespace();
79 match tokens.next() {
80 Some("ok") => Ok(Response::Ok),
81 Some("timer") => match tokens.next().map(serde_json::from_str::<Timer>) {
82 Some(Ok(timer)) => Ok(Response::Timer(timer)),
83 Some(Err(err)) => Err(Error::new(
84 ErrorKind::InvalidInput,
85 format!("invalid timer: {err}"),
86 )),
87 None => Err(Error::new(
88 ErrorKind::InvalidInput,
89 "missing timer".to_owned(),
90 )),
91 },
92 Some(res) => Err(Error::new(
93 ErrorKind::InvalidInput,
94 format!("invalid response: {res}"),
95 )),
96 None => Err(Error::new(
97 ErrorKind::InvalidInput,
98 "missing response".to_owned(),
99 )),
100 }
101 }
102}