use async_trait::async_trait;
use std::io::{Error, ErrorKind, Result};
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt},
net::TcpStream,
};
use crate::{
request::{Request, RequestWriter},
response::{Response, ResponseReader},
tcp::TcpHandler,
timer::Timer,
};
use super::{Client, ClientStream};
pub struct TcpClient {
pub host: String,
pub port: u16,
}
impl TcpClient {
pub fn new(host: impl ToString, port: u16) -> Box<dyn Client> {
Box::new(Self {
host: host.to_string(),
port,
})
}
}
#[async_trait]
impl Client for TcpClient {
async fn send(&self, req: Request) -> Result<Response> {
let stream = TcpStream::connect((self.host.as_str(), self.port)).await?;
let mut handler = TcpHandler::from(stream);
handler.handle(req).await
}
}
#[async_trait]
impl RequestWriter for TcpHandler {
async fn write(&mut self, req: Request) -> Result<()> {
let req = match req {
Request::Start => format!("start\n"),
Request::Get => format!("get\n"),
Request::Set(duration) => format!("set {duration}\n"),
Request::Pause => format!("pause\n"),
Request::Resume => format!("resume\n"),
Request::Stop => format!("stop\n"),
};
self.writer.write_all(req.as_bytes()).await?;
Ok(())
}
}
#[async_trait]
impl ResponseReader for TcpHandler {
async fn read(&mut self) -> Result<Response> {
let mut res = String::new();
self.reader.read_line(&mut res).await?;
let mut tokens = res.split_whitespace();
match tokens.next() {
Some("ok") => Ok(Response::Ok),
Some("timer") => match tokens.next().map(serde_json::from_str::<Timer>) {
Some(Ok(timer)) => Ok(Response::Timer(timer)),
Some(Err(err)) => Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid timer: {err}"),
)),
None => Err(Error::new(
ErrorKind::InvalidInput,
"missing timer".to_owned(),
)),
},
Some(res) => Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid response: {res}"),
)),
None => Err(Error::new(
ErrorKind::InvalidInput,
"missing response".to_owned(),
)),
}
}
}