parent
d708f41fb8
commit
38ffad2120
@ -0,0 +1,202 @@
|
|||||||
|
//! 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(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue