jsonx 0.1.0

A serde-enabled implementation of the JSONX extended-JSON format: typed value constructors, unquoted keys, and trailing commas.
Documentation
//! Helpers that opt a `#[derive(Serialize, Deserialize)]` field into a specific
//! JSONX extended type.
//!
//! serde's data model can't tell, on its own, that a `Vec<u8>` should be encoded
//! as `bytes("...")` rather than an array, or that an [`IpAddr`] should be
//! `ip("...")` rather than a plain string. Two mechanisms bridge the gap:
//!
//! * newtype wrappers ([`Bytes`], [`Int`], [`Uint`], [`Ip`], [`IpPort`],
//!   [`Datetime`]) that carry the JSONX type selection themselves — no
//!   attribute needed, and transparent (a plain string/integer) in every other
//!   serde format; and
//! * `#[serde(with = ...)]` modules ([`ip`], [`ipport`], and
//!   [`datetime`](crate::datetime)) for when you must keep a bare [`std::net`]
//!   address or [`DateTime`](crate::DateTime) field.
//!
//! The wrappers are the recommended default. They are themselves built on the
//! open [`constructor`](crate::constructor) API.
//!
//! ```
//! use jsonx::{Bytes, Ip};
//!
//! #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
//! struct Record {
//!     addr: Ip,
//!     blob: Bytes,
//! }
//!
//! let record = Record {
//!     addr: Ip("10.0.0.1".parse().unwrap()),
//!     blob: Bytes(b"hello".to_vec()),
//! };
//! let text = jsonx::to_string(&record).unwrap();
//! assert_eq!(text, r#"{addr:ip("10.0.0.1"),blob:bytes("aGVsbG8=")}"#);
//! assert_eq!(jsonx::from_str::<Record>(&text).unwrap(), record);
//! ```

use std::fmt;
use std::net::{AddrParseError, IpAddr, SocketAddr};
use std::ops::{Deref, DerefMut};

use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::constructor::JsonxConstructor;
use crate::datetime::DateTime;
use crate::tokens::{TOKEN_DATETIME, TOKEN_INT, TOKEN_IP, TOKEN_IPPORT, TOKEN_UINT};

/// Wraps a byte buffer so it (de)serializes as `bytes("...")` (Base64).
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Bytes(pub Vec<u8>);

/// Wraps an `i64` so it serializes as `int(...)` (the machine-width signed
/// integer). It deserializes from any JSONX integer form.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct Int(pub i64);

/// Wraps a `u64` so it serializes as `uint(...)` (the machine-width unsigned
/// integer). It deserializes from any non-negative JSONX integer form.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct Uint(pub u64);

/// Wraps an [`IpAddr`] so it (de)serializes as JSONX `ip("...")` with no
/// `#[serde(with = ...)]` attribute. Other serde formats see a transparent
/// newtype, so it stays a plain string there (e.g. `"10.0.0.1"` in TOML/JSON).
///
/// Use this when you own the struct and want it to be format-agnostic. Use the
/// [`ip`] module instead when you must keep a bare [`IpAddr`] field.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Ip(pub IpAddr);

/// Wraps a [`SocketAddr`] so it (de)serializes as JSONX `ipport("...")` with no
/// attribute, and as a plain string in other formats. The [`IpPort`] analogue
/// of [`Ip`]; see [`ipport`] for the bare-[`SocketAddr`] alternative.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct IpPort(pub SocketAddr);

/// Wraps a [`DateTime`] so it (de)serializes as JSONX `datetime("...")` with no
/// attribute, and as a plain RFC 3339 string in other formats.
///
/// Note the casing: [`DateTime`](crate::DateTime) is the bare
/// `chrono::DateTime<FixedOffset>` alias, while this `Datetime` is the
/// JSONX-tagged wrapper around it. See the [`datetime`](crate::datetime) module
/// for the bare-field alternative.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Datetime(pub DateTime);

// --- Deref niceties ---------------------------------------------------------

impl Deref for Bytes {
    type Target = Vec<u8>;
    fn deref(&self) -> &Vec<u8> {
        &self.0
    }
}
impl DerefMut for Bytes {
    fn deref_mut(&mut self) -> &mut Vec<u8> {
        &mut self.0
    }
}

macro_rules! wrapper_conversions {
    ($wrapper:ident, $inner:ty) => {
        impl Deref for $wrapper {
            type Target = $inner;
            fn deref(&self) -> &$inner {
                &self.0
            }
        }
        impl DerefMut for $wrapper {
            fn deref_mut(&mut self) -> &mut $inner {
                &mut self.0
            }
        }
        impl From<$inner> for $wrapper {
            fn from(v: $inner) -> $wrapper {
                $wrapper(v)
            }
        }
        impl From<$wrapper> for $inner {
            fn from(w: $wrapper) -> $inner {
                w.0
            }
        }
    };
}

wrapper_conversions!(Ip, IpAddr);
wrapper_conversions!(IpPort, SocketAddr);
wrapper_conversions!(Datetime, DateTime);

// --- ip / ipport `#[serde(with = ...)]` modules -----------------------------

/// (De)serializes an [`IpAddr`](std::net::IpAddr) field as JSONX `ip("...")`.
///
/// Use it through serde's `with` attribute:
///
/// ```
/// use std::net::IpAddr;
///
/// #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
/// struct Peer {
///     #[serde(with = "jsonx::ip")]
///     addr: IpAddr,
/// }
///
/// let peer = Peer { addr: "::1".parse().unwrap() };
/// let text = jsonx::to_string(&peer).unwrap();
/// assert_eq!(text, r#"{addr:ip("::1")}"#);
/// assert_eq!(jsonx::from_str::<Peer>(&text).unwrap(), peer);
/// ```
pub mod ip {
    use std::net::IpAddr;

    use serde::de;
    use serde::{Deserializer, Serializer};

    use crate::tokens::TOKEN_IP;

    /// Serializes an [`IpAddr`] as `ip("...")`.
    pub fn serialize<S: Serializer>(addr: &IpAddr, serializer: S) -> Result<S::Ok, S::Error> {
        crate::constructor::serialize_constructor(serializer, TOKEN_IP, &addr.to_string())
    }

    /// Deserializes an [`IpAddr`] from `ip("...")` (or a bare string).
    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<IpAddr, D::Error> {
        let arg = crate::constructor::deserialize_constructor(deserializer, TOKEN_IP)?;
        arg.parse().map_err(de::Error::custom)
    }
}

/// (De)serializes a [`SocketAddr`](std::net::SocketAddr) field as JSONX
/// `ipport("...")`.
///
/// Use it through serde's `with` attribute:
///
/// ```
/// use std::net::SocketAddr;
///
/// #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
/// struct Listener {
///     #[serde(with = "jsonx::ipport")]
///     listen: SocketAddr,
/// }
///
/// let l = Listener { listen: "[::1]:8080".parse().unwrap() };
/// let text = jsonx::to_string(&l).unwrap();
/// assert_eq!(text, r#"{listen:ipport("[::1]:8080")}"#);
/// assert_eq!(jsonx::from_str::<Listener>(&text).unwrap(), l);
/// ```
pub mod ipport {
    use std::net::SocketAddr;

    use serde::de;
    use serde::{Deserializer, Serializer};

    use crate::tokens::TOKEN_IPPORT;

    /// Serializes a [`SocketAddr`] as `ipport("...")`.
    pub fn serialize<S: Serializer>(addr: &SocketAddr, serializer: S) -> Result<S::Ok, S::Error> {
        crate::constructor::serialize_constructor(serializer, TOKEN_IPPORT, &addr.to_string())
    }

    /// Deserializes a [`SocketAddr`] from `ipport("...")` (or a bare string).
    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<SocketAddr, D::Error> {
        let arg = crate::constructor::deserialize_constructor(deserializer, TOKEN_IPPORT)?;
        arg.parse().map_err(de::Error::custom)
    }
}

// --- Bytes ------------------------------------------------------------------

impl Serialize for Bytes {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_bytes(&self.0)
    }
}

impl<'de> Deserialize<'de> for Bytes {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct V;
        impl<'de> Visitor<'de> for V {
            type Value = Bytes;
            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.write_str("a byte buffer")
            }
            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Bytes, E> {
                Ok(Bytes(v.to_vec()))
            }
            fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Bytes, E> {
                Ok(Bytes(v))
            }
            fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Bytes, A::Error> {
                let mut out = Vec::new();
                while let Some(b) = seq.next_element::<u8>()? {
                    out.push(b);
                }
                Ok(Bytes(out))
            }
        }
        deserializer.deserialize_byte_buf(V)
    }
}

// --- Int / Uint -------------------------------------------------------------

impl Serialize for Int {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_newtype_struct(TOKEN_INT, &self.0)
    }
}

impl<'de> Deserialize<'de> for Int {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        i64::deserialize(deserializer).map(Int)
    }
}

impl Serialize for Uint {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_newtype_struct(TOKEN_UINT, &self.0)
    }
}

impl<'de> Deserialize<'de> for Uint {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        u64::deserialize(deserializer).map(Uint)
    }
}

// --- Ip / IpPort / Datetime -------------------------------------------------
//
// These wrappers are the first consumers of the open-constructor API: each is
// a `JsonxConstructor`, and its serde impls just delegate to `crate::constructor`.

impl JsonxConstructor for Ip {
    const TOKEN: &'static str = TOKEN_IP;
    fn to_jsonx_arg(&self) -> String {
        self.0.to_string()
    }
    fn from_jsonx_arg(arg: &str) -> Result<Self, String> {
        arg.parse().map(Ip).map_err(|e: AddrParseError| e.to_string())
    }
}

impl JsonxConstructor for IpPort {
    const TOKEN: &'static str = TOKEN_IPPORT;
    fn to_jsonx_arg(&self) -> String {
        self.0.to_string()
    }
    fn from_jsonx_arg(arg: &str) -> Result<Self, String> {
        arg.parse().map(IpPort).map_err(|e: AddrParseError| e.to_string())
    }
}

impl JsonxConstructor for Datetime {
    const TOKEN: &'static str = TOKEN_DATETIME;
    fn to_jsonx_arg(&self) -> String {
        crate::datetime::to_jsonx_string(&self.0)
    }
    fn from_jsonx_arg(arg: &str) -> Result<Self, String> {
        crate::datetime::parse(arg).map(Datetime).map_err(|e| e.to_string())
    }
}

macro_rules! delegate_serde_to_constructor {
    ($($wrapper:ident),* $(,)?) => {
        $(
            impl Serialize for $wrapper {
                fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
                    crate::constructor::serialize(self, serializer)
                }
            }
            impl<'de> Deserialize<'de> for $wrapper {
                fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
                    crate::constructor::deserialize(deserializer)
                }
            }
        )*
    };
}

delegate_serde_to_constructor!(Ip, IpPort, Datetime);