ax_types 0.1.0

Core types for use by and with the ax family
Documentation
use base64::DecodeError;
use derive_more::{From, Into};
use fmt::Display;
use serde::{
    de::{self, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
    fmt::{self, Formatter},
    str::FromStr,
};

#[derive(Debug, Clone, From, Into, Ord, PartialOrd, Eq, PartialEq)]
pub struct Binary(Box<[u8]>);

struct X;
impl<'de> Visitor<'de> for X {
    type Value = Binary;
    fn expecting(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
        f.write_str("binary data (possibly base64-encoded)")
    }
    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
        Ok(Binary(v.into()))
    }
    fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
        let bytes = base64::decode(v).map_err(|e| E::custom(e.to_string()))?;
        Ok(Binary(bytes.into()))
    }
}

impl<'de> Deserialize<'de> for Binary {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        deserializer.deserialize_any(X)
    }
}

impl Serialize for Binary {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            serializer.serialize_str(base64::encode(&*self.0).as_str())
        } else {
            serializer.serialize_bytes(&self.0)
        }
    }
}

impl FromStr for Binary {
    type Err = DecodeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let bytes = base64::decode(s)?;
        Ok(Binary(bytes.into()))
    }
}

impl AsRef<[u8]> for Binary {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Display for Binary {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str(base64::encode(&*self.0).as_str())
    }
}

impl From<&[u8]> for Binary {
    fn from(v: &[u8]) -> Self {
        Binary(v.into())
    }
}

impl From<Vec<u8>> for Binary {
    fn from(v: Vec<u8>) -> Self {
        Binary(v.into())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn must_serialize_json() {
        let binary = Binary((&[1, 2, 3, 255][..]).into());
        let text = serde_json::to_string(&binary).unwrap();
        assert_eq!(text, r#""AQID/w==""#);
        assert_eq!(serde_json::from_str::<Binary>(&text).unwrap(), binary);
    }

    #[test]
    fn must_serialize_cbor() {
        let binary = Binary((&[1, 2, 3][..]).into());
        let cbor = serde_cbor::to_vec(&binary).unwrap();
        assert_eq!(cbor, vec![67, 1, 2, 3]);
        assert_eq!(serde_cbor::from_slice::<Binary>(&cbor).unwrap(), binary);
    }
}