use std::net::SocketAddr;
use comp_cat_rs::effect::io::Io;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::error::Error;
use crate::protocol::{Envelope, RequestId};
use crate::transport::{TcpTransport, Transport};
#[derive(Debug, Clone, Copy)]
pub struct ServerAddr(SocketAddr);
impl ServerAddr {
#[must_use]
pub fn new(addr: SocketAddr) -> Self {
Self(addr)
}
#[must_use]
pub fn addr(self) -> SocketAddr {
self.0
}
}
#[must_use]
pub fn call<Req, Resp>(addr: ServerAddr, request: Req) -> Io<Error, Resp>
where
Req: Serialize + Send + 'static,
Resp: DeserializeOwned + Send + 'static,
{
TcpTransport::connect(addr.addr()).flat_map(move |transport| {
call_on(transport, request).map(|(resp, _transport)| resp)
})
}
#[must_use]
pub fn call_on<T, Req, Resp>(transport: T, request: Req) -> Io<Error, (Resp, T)>
where
T: Transport,
Req: Serialize + Send + 'static,
Resp: DeserializeOwned + Send + 'static,
{
Io::suspend(move || {
let payload = serde_json::to_string(&request).map_err(Error::from_serialize)?;
Ok((payload, transport))
})
.flat_map(|(payload, transport)| {
let envelope = Envelope::Request {
id: RequestId::new(0),
payload,
};
transport.send(envelope)
})
.flat_map(Transport::recv)
.flat_map(|(envelope, transport)| {
Io::suspend(move || {
match envelope {
Envelope::Response { payload, .. } => {
let resp = serde_json::from_str(&payload)
.map_err(Error::from_deserialize)?;
Ok((resp, transport))
}
Envelope::Error { message, .. } => Err(Error::Server { message }),
Envelope::Request { .. } => Err(Error::Server {
message: "unexpected request envelope from server".to_owned(),
}),
}
})
})
}