utcnow 0.2.1

Get the current unixtime in a no-std context
Documentation
#![cfg_attr(docsrs, doc(cfg(feature = "rkyv")))]

use core::{cmp, fmt, mem};

use rkyv::{Archive, Archived, Deserialize, Fallible, Resolver, Serialize};

use crate::u30::U30;
use crate::UtcTime;

/// An archived [`UtcTime`]
#[cfg_attr(docsrs, doc(cfg(feature = "rkyv")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ArchivedUtcTime {
    secs: Archived<i64>,
    nanos: Archived<u32>,
}

/// The resolver for an archived [`UtcTime`]
#[cfg_attr(docsrs, doc(cfg(feature = "rkyv")))]
#[derive(Debug, Clone, Copy)]
pub struct UtcTimeResolver {
    secs: Resolver<i64>,
    nanos: Resolver<u32>,
}

impl Archive for UtcTime {
    type Archived = ArchivedUtcTime;
    type Resolver = UtcTimeResolver;

    #[allow(trivial_casts)]
    #[inline]
    unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
        let (fp, fo) = {
            let fo = &mut (*out).secs as *mut i64;
            (fo.cast::<u8>().offset_from(out.cast::<u8>()) as usize, fo)
        };
        Archive::resolve(&self.secs, pos + fp, resolver.secs, fo);

        let (fp, fo) = {
            let fo = &mut (*out).nanos as *mut u32;
            (fo.cast::<u8>().offset_from(out.cast::<u8>()) as usize, fo)
        };
        let nanos: &u32 = mem::transmute(&self.nanos);
        Archive::resolve(nanos, pos + fp, resolver.nanos, fo);
    }
}

impl<D: Fallible + ?Sized> Deserialize<UtcTime, D> for Archived<UtcTime> {
    #[inline]
    fn deserialize(&self, deserializer: &mut D) -> Result<UtcTime, D::Error> {
        let secs = Deserialize::<i64, D>::deserialize(&self.secs, deserializer)?;

        let nanos: &u32 = unsafe { mem::transmute(&self.nanos) };
        let nanos = Deserialize::<u32, D>::deserialize(nanos, deserializer)?;
        let nanos = unsafe { U30::new_unchecked(nanos) };

        Ok(UtcTime { secs, nanos })
    }
}

impl<S: Fallible + ?Sized> Serialize<S> for UtcTime {
    #[inline]
    fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
        let secs = Serialize::<S>::serialize(&self.secs, serializer)?;

        let nanos: &u32 = unsafe { mem::transmute(&self.nanos) };
        let nanos = Serialize::<S>::serialize(nanos, serializer)?;

        Ok(UtcTimeResolver { secs, nanos })
    }
}

impl PartialEq<UtcTime> for ArchivedUtcTime {
    fn eq(&self, other: &UtcTime) -> bool {
        self.secs == other.secs && self.nanos == other.nanos.get()
    }
}

impl PartialEq<ArchivedUtcTime> for UtcTime {
    fn eq(&self, other: &ArchivedUtcTime) -> bool {
        self.secs == other.secs && self.nanos.get() == other.nanos
    }
}

impl PartialOrd<UtcTime> for ArchivedUtcTime {
    fn partial_cmp(&self, other: &UtcTime) -> Option<cmp::Ordering> {
        match self.secs.partial_cmp(&other.secs) {
            Some(cmp::Ordering::Equal) => self.nanos.partial_cmp(&other.nanos.get()),
            ord => ord,
        }
    }
}

impl PartialOrd<ArchivedUtcTime> for UtcTime {
    fn partial_cmp(&self, other: &ArchivedUtcTime) -> Option<cmp::Ordering> {
        match self.secs.partial_cmp(&other.secs) {
            Some(cmp::Ordering::Equal) => self.nanos.get().partial_cmp(&other.nanos),
            ord => ord,
        }
    }
}

impl fmt::Display for ArchivedUtcTime {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{:09}", self.secs, self.nanos)
    }
}

#[cfg(test)]
#[test]
fn test() {
    let value = UtcTime::new(1_661_209_811, 467_621_425).unwrap();

    let bytes = rkyv::to_bytes::<_, 32>(&value).unwrap();
    let archived = unsafe { rkyv::archived_root::<UtcTime>(&bytes) };
    let deserialized: UtcTime = archived.deserialize(&mut rkyv::Infallible).unwrap();
    assert_eq!(&value, archived);
    assert_eq!(&value, &deserialized);
    assert_eq!(archived, &deserialized);
}