midiserde 0.1.1

When mini isn't enough and serde is too much
Documentation
//! `chrono::TimeDelta` ↔ `[seconds, nanoseconds]` JSON array.
//!
//! Matches [chrono's serde format](https://docs.rs/chrono/latest/src/chrono/time_delta.rs.html#673-677):
//! a 2-element array of `[secs: i64, nanos: i32]`.
//!
//! Requires the `chrono` feature.
//!
//! # Usage
//!
//! ```rust
//! use midiserde::{Deserialize, Serialize};
//!
//! #[derive(Deserialize, Serialize)]
//! struct Config {
//!     #[mini(with = "midiserde::with::time_delta")]
//!     timeout: chrono::TimeDelta,
//! }
//! ```
//!
//! JSON: `{"timeout": [30, 0]}` for 30 seconds

use chrono::TimeDelta;
use miniserde::de::{Deserialize, Seq, Visitor};
use miniserde::ser::{Fragment, Seq as SerSeq};

/// Deserialization: parses `[secs, nanos]` array into `TimeDelta`.
pub fn begin(out: &mut Option<TimeDelta>) -> &mut dyn Visitor {
    Place::new(out)
}

/// Serialization: outputs `TimeDelta` as `[secs, nanos]` array.
pub fn serialize(value: &TimeDelta) -> Fragment<'_> {
    let secs = value.num_seconds();
    let nanos = value.subsec_nanos();
    Fragment::Seq(Box::new(TimeDeltaSeq {
        values: vec![secs, nanos as i64],
        index: 0,
    }))
}

struct TimeDeltaSeq {
    values: Vec<i64>,
    index: usize,
}

impl SerSeq for TimeDeltaSeq {
    fn next(&mut self) -> Option<&dyn miniserde::Serialize> {
        let i = self.index;
        self.index += 1;
        self.values.get(i).map(|v| v as &dyn miniserde::Serialize)
    }
}

#[repr(C)]
struct Place {
    out: Option<TimeDelta>,
}

impl Place {
    fn new(out: &mut Option<TimeDelta>) -> &mut Self {
        unsafe { &mut *std::ptr::addr_of_mut!(*out).cast::<Place>() }
    }
}

impl Visitor for Place {
    fn seq(&mut self) -> miniserde::Result<Box<dyn Seq + '_>> {
        Ok(Box::new(TimeDeltaSeqVisitor {
            out: &mut self.out,
            elements: [None, None],
            index: 0,
        }))
    }
}

struct TimeDeltaSeqVisitor<'a> {
    out: &'a mut Option<TimeDelta>,
    elements: [Option<i64>; 2],
    index: usize,
}

impl Seq for TimeDeltaSeqVisitor<'_> {
    fn element(&mut self) -> miniserde::Result<&mut dyn Visitor> {
        if self.index >= 2 {
            return Err(miniserde::Error);
        }
        let slot = &mut self.elements[self.index];
        self.index += 1;
        Ok(Deserialize::begin(slot))
    }

    fn finish(&mut self) -> miniserde::Result<()> {
        let secs = self.elements[0].ok_or(miniserde::Error)?;
        let nanos = self.elements[1].ok_or(miniserde::Error)?;
        // Chrono expects nanos in 0..1_000_000_000
        if nanos < 0 || nanos >= 1_000_000_000 {
            return Err(miniserde::Error);
        }
        *self.out = Some(
            TimeDelta::new(secs, nanos as u32).ok_or(miniserde::Error)?,
        );
        Ok(())
    }
}