preserves 4.996.0

Implementation of the Preserves serialization format via serde.
Documentation
#![doc(hidden)]

//! A horrifying hack to Serde-serialize [IOValue] instances to Preserves *as themselves*.
//!
//! Frankly I think this portion of the codebase might not survive for long. I can't think of a
//! better way of achieving this, but the drawbacks of having this functionality are *severe*.
//!
//! See <https://gitlab.com/preserves/preserves/-/issues/42>.

use super::repr::IOValue;

pub static MAGIC: &str = "$____Preserves_Serde_Magic";

struct IOValueVisitor;
impl<'de> serde::de::Visitor<'de> for IOValueVisitor {
    type Value = IOValue;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a magic encoding of an embedded Preserves Value")
    }
    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        let b = unsafe { Box::from_raw(v as *mut IOValue) };
        Ok(*b)
    }
}

#[inline(always)]
pub fn output_value<S: serde::Serializer>(serializer: S, v: IOValue) -> Result<S::Ok, S::Error> {
    serializer.serialize_newtype_struct(MAGIC, &(Box::into_raw(Box::new(v)) as u64))
}

#[inline(always)]
pub fn input_value<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<IOValue, D::Error> {
    deserializer.deserialize_newtype_struct(MAGIC, IOValueVisitor)
}

//---------------------------------------------------------------------------

#[inline]
pub fn receive_output_value<T: ?Sized>(name: &'static str, magic_value: &T) -> Option<IOValue> {
    if name == MAGIC {
        let b =
            unsafe { Box::from_raw(*((magic_value as *const T) as *const u64) as *mut IOValue) };
        let v: IOValue = *b;
        Some(v)
    } else {
        None
    }
}

#[inline]
pub fn transmit_input_value<F>(name: &'static str, f: F) -> Result<Option<u64>, crate::error::Error>
where
    F: FnOnce() -> Result<IOValue, crate::error::Error>,
{
    if name == MAGIC {
        let b: Box<IOValue> = Box::new(f()?);
        Ok(Some(Box::into_raw(b) as u64))
    } else {
        Ok(None)
    }
}