use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum Request {
CreateSession { username: String },
PostAuthMessageResponse { response: Option<String> },
StartSession { cmd: Vec<String> },
CancelSession,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorType {
Error,
AuthError,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthMessageType {
Visible,
Secret,
Info,
Error,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum Response {
Success,
Error {
error_type: ErrorType,
description: String,
},
AuthMessage {
auth_message_type: AuthMessageType,
auth_message: String,
},
}
#[cfg(feature = "codec")]
#[cfg_attr(docsrs, doc(cfg(feature = "codec")))]
pub mod codec {
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum Error {
#[error("serialization error: {0}")]
Serialization(String),
#[error("i/o error: {0}")]
Io(String),
#[error("EOF")]
Eof,
}
impl From<serde_json::error::Error> for Error {
fn from(error: serde_json::error::Error) -> Self {
Error::Serialization(error.to_string())
}
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Error::Io(format!("{}", error))
}
}
#[cfg(feature = "sync-codec")]
mod sync_codec {
use crate::{codec::Error, Request, Response};
use std::io::{Read, Write};
pub trait SyncCodec {
fn read_from<T: Read>(stream: &mut T) -> Result<Self, Error>
where
Self: std::marker::Sized;
fn write_to<T: Write>(&self, stream: &mut T) -> Result<(), Error>;
}
impl SyncCodec for Request {
fn read_from<T: Read>(stream: &mut T) -> Result<Self, Error> {
let mut len_bytes = [0; 4];
stream
.read_exact(&mut len_bytes)
.map_err(|e| match e.kind() {
std::io::ErrorKind::UnexpectedEof => Error::Eof,
_ => e.into(),
})?;
let len = u32::from_ne_bytes(len_bytes);
let mut resp_buf = vec![0; len as usize];
stream.read_exact(&mut resp_buf)?;
serde_json::from_slice(&resp_buf).map_err(|e| e.into())
}
fn write_to<T: Write>(&self, stream: &mut T) -> Result<(), Error> {
let body_bytes = serde_json::to_vec(self)?;
let len_bytes = (body_bytes.len() as u32).to_ne_bytes();
stream.write_all(&len_bytes)?;
stream.write_all(&body_bytes)?;
Ok(())
}
}
impl SyncCodec for Response {
fn read_from<T: Read>(stream: &mut T) -> Result<Self, Error> {
let mut len_bytes = [0; 4];
stream
.read_exact(&mut len_bytes)
.map_err(|e| match e.kind() {
std::io::ErrorKind::UnexpectedEof => Error::Eof,
_ => e.into(),
})?;
let len = u32::from_ne_bytes(len_bytes);
let mut resp_buf = vec![0; len as usize];
stream.read_exact(&mut resp_buf)?;
serde_json::from_slice(&resp_buf).map_err(|e| e.into())
}
fn write_to<T: Write>(&self, stream: &mut T) -> Result<(), Error> {
let body_bytes = serde_json::to_vec(self)?;
let len_bytes = (body_bytes.len() as u32).to_ne_bytes();
stream.write_all(&len_bytes)?;
stream.write_all(&body_bytes)?;
Ok(())
}
}
}
#[cfg(feature = "sync-codec")]
pub use sync_codec::SyncCodec;
#[cfg(feature = "tokio-codec")]
mod tokio_codec {
use crate::{codec::Error, Request, Response};
use async_trait::async_trait;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[async_trait]
pub trait TokioCodec {
async fn read_from<T: AsyncReadExt + std::marker::Unpin + Send>(
stream: &mut T,
) -> Result<Self, Error>
where
Self: std::marker::Sized;
async fn write_to<T: AsyncWriteExt + std::marker::Unpin + Send>(
&self,
stream: &mut T,
) -> Result<(), Error>;
}
#[async_trait]
impl TokioCodec for Request {
async fn read_from<T: AsyncReadExt + std::marker::Unpin + Send>(
stream: &mut T,
) -> Result<Self, Error> {
let mut len_bytes = [0; 4];
stream
.read_exact(&mut len_bytes)
.await
.map_err(|e| match e.kind() {
std::io::ErrorKind::UnexpectedEof => Error::Eof,
_ => e.into(),
})?;
let len = u32::from_ne_bytes(len_bytes);
let mut body_bytes = vec![0; len as usize];
stream.read_exact(&mut body_bytes).await?;
let body = serde_json::from_slice(&body_bytes)?;
Ok(body)
}
async fn write_to<T: AsyncWriteExt + std::marker::Unpin + Send>(
&self,
stream: &mut T,
) -> Result<(), Error> {
let body_bytes = serde_json::to_vec(self)?;
let len_bytes = (body_bytes.len() as u32).to_ne_bytes();
stream.write_all(&len_bytes).await?;
stream.write_all(&body_bytes).await?;
Ok(())
}
}
#[async_trait]
impl TokioCodec for Response {
async fn read_from<T: AsyncReadExt + std::marker::Unpin + Send>(
stream: &mut T,
) -> Result<Self, Error> {
let mut len_bytes = [0; 4];
stream
.read_exact(&mut len_bytes)
.await
.map_err(|e| match e.kind() {
std::io::ErrorKind::UnexpectedEof => Error::Eof,
_ => e.into(),
})?;
let len = u32::from_ne_bytes(len_bytes);
let mut body_bytes = vec![0; len as usize];
stream.read_exact(&mut body_bytes).await?;
let body = serde_json::from_slice(&body_bytes)?;
Ok(body)
}
async fn write_to<T: AsyncWriteExt + std::marker::Unpin + Send>(
&self,
stream: &mut T,
) -> Result<(), Error> {
let body_bytes = serde_json::to_vec(self)?;
let len_bytes = (body_bytes.len() as u32).to_ne_bytes();
stream.write_all(&len_bytes).await?;
stream.write_all(&body_bytes).await?;
Ok(())
}
}
}
#[cfg(feature = "tokio-codec")]
pub use tokio_codec::TokioCodec;
}