greetd_ipc: Split codecs into separate files

This commit is contained in:
Kenny Levinsen
2020-05-06 23:33:09 +02:00
parent 04b9ab2367
commit 391a3069d5
4 changed files with 228 additions and 230 deletions

View File

@@ -0,0 +1,45 @@
//! Reader/writer codecs for Request/Response.
//!
//! This is implemented in the form of two traits, SyncCodec and TokioCodec,
//! which operate on the `std` and `tokio` implementations of reader/writer
//! traits. The former is intended as the name suggests for synchronous
//! operation, while the latter is for asynchronous operation when using tokio.
//!
//! These codecs are hidden behind the `sync-codec` and `tokio-codec` features,
//! respectively. These features also implicitly enable the `codec` feature,
//! which controls the entire `codec` module.
//!
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;
#[cfg(feature = "sync-codec")]
pub use self::sync::SyncCodec;
#[cfg(feature = "tokio-codec")]
mod tokio;
#[cfg(feature = "tokio-codec")]
pub use self::tokio::TokioCodec;

View File

@@ -0,0 +1,78 @@
//! Synchronous reader/writer implementation, operating on an implementor of std::io::{Read, Write}.
//!
//! # Example
//!
//! ```no_run
//! use std::env;
//! use std::os::unix::net::UnixStream;
//! use greetd_ipc::{Request, Response};
//! use greetd_ipc::codec::SyncCodec;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut stream = UnixStream::connect(env::var("GREETD_SOCK")?)?;
//! Request::CreateSession { username: "john".to_string() }.write_to(&mut stream)?;
//! let resp = Response::read_from(&mut stream)?;
//! Ok(())
//! }
//! ```
use crate::{codec::Error, Request, Response};
use std::io::{Read, Write};
/// Reader/writer implementation over 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(())
}
}

View File

@@ -0,0 +1,101 @@
//! Asynchronous reader/writer implementation, operating on an implementor of tokio::io::{AsyncReadExt, AsyncWriteExt}.
//!
//! # Example
//!
//! ```no_run
//! use std::env;
//! use tokio::net::UnixStream;
//! use greetd_ipc::{Request, Response};
//! use greetd_ipc::codec::TokioCodec;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut stream = UnixStream::connect(env::var("GREETD_SOCK")?).await?;
//! Request::CreateSession { username: "john".to_string() }.write_to(&mut stream).await?;
//! let resp = Response::read_from(&mut stream).await?;
//! Ok(())
//! }
//! ```
use crate::{codec::Error, Request, Response};
use async_trait::async_trait;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
/// Reader/writer implementation over 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(())
}
}

View File

@@ -33,6 +33,10 @@
//!
use serde::{Deserialize, Serialize};
#[cfg(feature = "codec")]
#[cfg_attr(docsrs, doc(cfg(feature = "codec")))]
pub mod codec;
/// A request from a greeter to greetd. The request type is internally tagged
/// with the"type" field, with the type written in snake_case.
///
@@ -147,233 +151,3 @@ pub enum Response {
auth_message: String,
},
}
/// Reader/writer codecs for Request/Response.
///
/// This is implemented in the form of two traits, SyncCodec and TokioCodec,
/// which operate on the `std` and `tokio` implementations of reader/writer
/// traits. The former is intended as the name suggests for synchronous
/// operation, while the latter is for asynchronous operation when using tokio.
///
/// These codecs are hidden behind the `sync-codec` and `tokio-codec` features,
/// respectively. These features also implicitly enable the `codec` feature,
/// which controls the entire `codec` module.
///
#[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))
}
}
/// Synchronous reader/writer implementation, operating on an implementor of std::io::{Read, Write}.
///
/// # Example
///
/// ```no_run
/// use std::env;
/// use std::os::unix::net::UnixStream;
/// use greetd_ipc::{Request, Response};
/// use greetd_ipc::codec::SyncCodec;
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut stream = UnixStream::connect(env::var("GREETD_SOCK")?)?;
/// Request::CreateSession { username: "john".to_string() }.write_to(&mut stream)?;
/// let resp = Response::read_from(&mut stream)?;
/// Ok(())
/// }
/// ```
#[cfg(feature = "sync-codec")]
mod sync_codec {
use crate::{codec::Error, Request, Response};
use std::io::{Read, Write};
/// Reader/writer implementation over 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;
/// Asynchronous reader/writer implementation, operating on an implementor of tokio::io::{AsyncReadExt, AsyncWriteExt}.
///
/// # Example
///
/// ```no_run
/// use std::env;
/// use tokio::net::UnixStream;
/// use greetd_ipc::{Request, Response};
/// use greetd_ipc::codec::TokioCodec;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut stream = UnixStream::connect(env::var("GREETD_SOCK")?).await?;
/// Request::CreateSession { username: "john".to_string() }.write_to(&mut stream).await?;
/// let resp = Response::read_from(&mut stream).await?;
/// Ok(())
/// }
/// ```
#[cfg(feature = "tokio-codec")]
mod tokio_codec {
use crate::{codec::Error, Request, Response};
use async_trait::async_trait;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
/// Reader/writer implementation over 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;
}