You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.2 KiB

//! Serialisation helpers & traits
use super::*;
use tokio::io::{AsyncWrite,AsyncBufRead,AsyncRead,};
use std::{
marker::Unpin,
fmt,
};
pub trait TextSerialiseable: Sized
{
fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>;
fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T) -> LocalBoxFuture<'a, Result<Self, eyre::Report>>
where Self: 'a;
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
{
(0,None)
}
}
pub trait BinarySerialisable: Sized
{
fn serialise_bytes<'a, 'b, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>
where 'b: 'a;
fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized>(input: &'b mut T) -> LocalBoxFuture<'b, Result<Self, eyre::Report>>
where Self: 'b;
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
{
(0,None)
}
}
pub trait Serialisable: BinarySerialisable + TextSerialiseable
{
/// Attempt to immediately serialise this object into memory.
///
/// Returns a newly allocated `Vec<u8>` containing the bytes.
/// Attempts to use the appropriate `size_hint` to allocate vector capacity if possible.
///
/// # Notes
/// This function will fail if any `await`ed future within the `serialise_*` method cannot complete immediately.
#[instrument(skip(self))]
fn into_memory(&self, mode: Mode) -> Result<Vec<u8>, eyre::Report>
{
let mut output = match mode.size_hint(self)
{
(0, None) | (_, Some(0)) => Vec::new(),
(_, Some(x)) | (x, None) => Vec::with_capacity(x),
};
trace!("Cap for membuf is {} bytes", output.capacity());
let note = || format!("While serialising {} into memory", std::any::type_name::<Self>());
let written = match mode {
Mode::Text => self.serialise_text(&mut output).now_or_never(),
Mode::Binary => self.serialise_bytes(&mut output).now_or_never(),
}
.ok_or_else(|| eyre::eyre!("Failed to complete {} write syncronously. A future returned `Poll::Pending`.", mode))
.with_note(note.clone())?
.wrap_err_with(|| eyre::eyre!("Failed to write {} into memory", mode))
.with_note(note)?;
if written != output.len()
{
error!("Function reported {} bytes written, but actually wrote {}", written, output.len());
}
Ok(output)
}
/// Attempt to immediately deserialise this object from memory
///
/// Returns the object and the number of bytes read from `buf`.
///
/// # Notes
/// This function will fail if any `await`ed future within the `deserialise_*` method cannot complete immediately.
#[instrument(skip(buf),fields(buf = ?buf.as_ref()))]
fn from_memory<T: AsRef<[u8]>>(buf: T, mode: Mode) -> Result<(Self, usize), eyre::Report>
{
let buf = buf.as_ref();
let mut reader = &buf[..];
trace!("Attempting read of up to {} bytes in {} mode to instantiate type {}", buf.len(), mode, std::any::type_name::<Self>());
let note = || format!("While deserialising {} from memory", std::any::type_name::<Self>());
let bytes = || format!("{}", buf.fmt_view()).header("Binary buffer was");
let text = || String::from_utf8_lossy(buf).into_owned().header("Text buffer was");
let value = match mode {
Mode::Binary => Self::deserialise_bytes(&mut reader).now_or_never(),
Mode::Text => Self::deserialise_text(&mut reader).now_or_never(),
}
.ok_or_else(|| eyre::eyre!("Failed to complete {} read syncronously. A future returned `Poll::Pending`.", mode))
.with_note(note.clone())?
.wrap_err_with(|| eyre::eyre!("Failed to read {} from memory", mode))
.with_note(note)
.with_section(bytes)
.with_section(text)?;
let diff = (&reader[0] as *const u8 as usize) - (&buf[0] as *const u8 as usize);
Ok((value, diff))
}
}
impl<T> Serialisable for T
where T: BinarySerialisable + TextSerialiseable{}
/// Serialisation mode for `Serialisable` types
#[derive(Debug, Clone, Copy)]
pub enum Mode
{
/// Serialise as text
Text,
/// Serialise as binary
Binary,
}
impl fmt::Display for Mode
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Text => write!(f, "text"),
Self::Binary => write!(f, "binary"),
}
}
}
impl Mode
{
#[inline(always)] fn size_hint<T: Serialisable>(&self, other: &T) -> (usize, Option<usize>)
{
match self {
Self::Text => TextSerialiseable::size_hint(other),
Self::Binary => BinarySerialisable::size_hint(other),
}
}
}
/// Serialise this object into base64 text
///
/// # TODO
/// Line wrapping
#[instrument(err, skip(value), fields(value_type=?std::any::type_name::<T>()))]
pub fn into_text<T: Serialize>(value: &T) -> Result<String, Error>
{
let bytes = serde_cbor::to_vec(value).map_err(Error::Serialise)?;
Ok(base64::encode(bytes))
}
#[derive(Debug)]
pub enum Error
{
Base64(base64::DecodeError),
Serialise(serde_cbor::Error),
Deserialise(serde_cbor::Error),
}
impl std::error::Error for Error
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
Self::Base64(b) => Some(b),
Self::Serialise(b) => Some(b),
Self::Deserialise(b) => Some(b),
}
}
}
impl std::fmt::Display for Error
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
match self {
Self::Base64(_) => write!(f, "base64 decode failed"),
Self::Serialise(_) => write!(f, "serialisation failed"),
Self::Deserialise(_) => write!(f, "deserialisation failed"),
}
}
}
/// Try to deserialise this object from text.
#[instrument(err, skip(string), fields(string = ?string.as_ref()))]
pub fn from_text<T: serde::de::DeserializeOwned>(string: impl AsRef<[u8]>) -> Result<T, Error>
{
let bytes = base64::decode(string).map_err(Error::Base64)?;
serde_cbor::from_reader(&bytes[..]).map_err(Error::Deserialise)
}
#[cfg(test)]
mod tests
{
use crate::*;
#[test]
fn test_ser() -> Result<(), eyre::Report>
{
let object = "hello world";
let enc = super::into_text(&String::from(object)).wrap_err(eyre::eyre!("failed to encode"))?;
println!("Enc: {:?}", enc);
let dec: String = super::from_text(enc).wrap_err(eyre::eyre!("failed to decode"))?;
assert_eq!(object,dec);
Ok(())
}
}