use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use derive_more::Display;
use num_enum::TryFromPrimitive;
use prost::Message;
use std::convert::TryFrom;
pub trait Send {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()>;
}
pub trait Receive {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized;
}
#[derive(Copy, Clone, Display, PartialEq, Eq, TryFromPrimitive)]
#[repr(i16)]
pub enum RpcReplyCode {
Result = -1,
Fail = -2,
Text = -3,
Quit = -4,
}
#[derive(Copy, Clone, Display, PartialEq, Eq, TryFromPrimitive, Debug)]
#[repr(i32)]
pub enum CommandResult {
LinkFailure = -3,
NeedsConsole = -2,
NotImplemented = -1,
Ok = 0,
Failure = 1,
WrongUsage = 2,
NotFound = 3,
}
#[derive(Display)]
#[display("Handshake {} version {}", magic, version)]
pub struct Handshake {
pub magic: String,
pub version: i32,
}
#[derive(Display)]
#[display("Header id {}, size {}", id, size)]
pub struct Header {
pub id: i16,
padding: i16,
pub size: i32,
}
#[derive(Display)]
#[display("protobuf message id {}", id)]
pub struct Request<TMessage: crate::Message> {
pub id: i16,
pub message: TMessage,
}
pub enum Reply<TMessage: crate::Message> {
Text(crate::CoreTextNotification),
Result(TMessage),
Fail(CommandResult),
}
#[derive(Display)]
pub struct Quit {}
impl Handshake {
pub fn new(magic: String, version: i32) -> Self {
Handshake { magic, version }
}
}
impl Send for Handshake {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
log::trace!("Sending {}", self);
stream.write_all(self.magic.as_bytes())?;
self.version.send(stream)?;
log::trace!("Sent handshake");
Ok(())
}
}
impl Receive for Handshake {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized,
{
log::trace!("Receiving handshake");
let mut magic = [0_u8; 8];
stream.read_exact(&mut magic)?;
let version = i32::receive(stream)?;
let handshake = Self::new(String::from_utf8(magic.to_vec())?, version);
log::trace!("Received {}", handshake);
Ok(handshake)
}
}
impl Header {
pub fn new(id: i16, size: i32) -> Self {
Header {
id,
size,
padding: 0,
}
}
}
impl Send for Header {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
log::trace!("Sending {}", self);
self.id.send(stream)?;
self.padding.send(stream)?;
self.size.send(stream)?;
log::trace!("Sent {}", self);
Ok(())
}
}
impl Receive for Header {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized,
{
log::trace!("Receiving header");
let header = Header {
id: i16::receive(stream)?,
padding: i16::receive(stream)?,
size: i32::receive(stream)?,
};
log::trace!("Received {}", header);
Ok(header)
}
}
impl<TMessage: crate::Message> Request<TMessage> {
pub fn new(id: i16, message: TMessage) -> Self {
Self { id, message }
}
}
impl<TMessage: crate::Message> Send for Request<TMessage> {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
let payload: Vec<u8> = self.message.encode_to_vec();
let header = Header::new(self.id, payload.len() as i32);
log::trace!("Sending protobuf message {}", TMessage::NAME);
header.send(stream)?;
stream.write_all(&payload)?;
log::trace!("Sent protobuf message");
Ok(())
}
}
impl<TMessage: crate::Message> Receive for Reply<TMessage> {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized,
{
let header = Header::receive(stream)?;
log::trace!("Expecting protobuf message {}", TMessage::NAME);
let reply_code = RpcReplyCode::try_from(header.id)?;
match reply_code {
RpcReplyCode::Result => {
log::trace!("Receiving RPC_REPLY_RESULT of size {}", header.size);
let mut buf = vec![0u8; header.size as usize];
stream.read_exact(&mut buf)?;
log::trace!("Read stream");
let reply = TMessage::decode(buf.as_slice())?;
log::trace!("Received {}", TMessage::NAME);
Ok(Reply::Result(reply))
}
RpcReplyCode::Fail => {
log::trace!("Received RPC_REPLY_FAIL {}", header.size);
let res = match CommandResult::try_from(header.size) {
Ok(res) => res,
Err(err) => {
return Err(crate::Error::ProtocolError(format!(
"Unknown CommandResult {}",
err.number
)))
}
};
Ok(Reply::Fail(res))
}
RpcReplyCode::Text => {
log::trace!("Receiving RPC_REPLY_TEXT of size {}", header.size);
let mut buf = vec![0u8; header.size as usize];
stream.read_exact(&mut buf)?;
let reply = crate::CoreTextNotification::decode(buf.as_slice())?;
Ok(Reply::Text(reply))
}
RpcReplyCode::Quit => Err(crate::Error::ProtocolError(
"Unexpected \"Quit\" reply code".to_string(),
)),
}
}
}
impl Quit {
pub fn new() -> Self {
Self {}
}
}
impl Send for Quit {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
log::trace!("Sending {}", self);
let header = Header::new(RpcReplyCode::Quit as i16, 0);
header.send(stream)?;
log::trace!("Sent {}", self);
Ok(())
}
}
impl Send for i16 {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
stream.write_i16::<LittleEndian>(*self)?;
Ok(())
}
}
impl Send for i32 {
fn send<T: std::io::Write>(&self, stream: &mut T) -> crate::Result<()> {
stream.write_i32::<LittleEndian>(*self)?;
Ok(())
}
}
impl Receive for i16 {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized,
{
Ok(stream.read_i16::<LittleEndian>()?)
}
}
impl Receive for i32 {
fn receive<T: std::io::Read>(stream: &mut T) -> crate::Result<Self>
where
Self: Sized,
{
Ok(stream.read_i32::<LittleEndian>()?)
}
}