libhaystack 3.1.6

Rust implementation of the Haystack 4 data types, defs, filter, units, and encodings
Documentation
// Copyright (C) 2020 - 2022, J2 Innovations

//!
//! Implement Hayson encoding
//!

use chrono::SecondsFormat;
use std::borrow::Cow;

use crate::{
    haystack::val::{
        Column, Coord, Date, DateTime, Dict, Grid, Marker, Na, Number, Ref, Remove, Symbol, Time,
        Uri, Value as HVal, XStr,
    },
    val::HaystackDict,
};

use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};

/// Serialize a Grid column
impl Serialize for Column {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(if self.meta.is_none() { 1 } else { 2 }))?;
        map.serialize_entry("name", &self.name)?;
        if self.meta.is_some() {
            map.serialize_entry("meta", &self.meta)?;
        }
        map.end()
    }
}

impl Serialize for Marker {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry("_kind", "marker")?;
        map.end()
    }
}

impl Serialize for Na {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry("_kind", "na")?;
        map.end()
    }
}

impl Serialize for Remove {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry("_kind", "remove")?;
        map.end()
    }
}

impl Serialize for Ref {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(if self.dis.is_none() { 2 } else { 3 }))?;
        map.serialize_entry("_kind", "ref")?;
        map.serialize_entry("val", &self.value)?;
        if self.dis.is_some() {
            let dis = self.dis.clone();
            map.serialize_entry("dis", &dis)?;
        }
        map.end()
    }
}

impl Serialize for Uri {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("_kind", "uri")?;
        map.serialize_entry("val", &self.value)?;
        map.end()
    }
}

impl Serialize for Symbol {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("_kind", "symbol")?;
        map.serialize_entry("val", &self.value)?;
        map.end()
    }
}

impl Serialize for Number {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if let Some(unit) = self.unit {
            let mut map = serializer.serialize_map(Some(3))?;
            map.serialize_entry("_kind", "number")?;
            map.serialize_entry("val", &self.value)?;
            map.serialize_entry("unit", unit.symbol())?;
            map.end()
        } else if self.value.fract() == 0.0 {
            serializer.serialize_i64(self.value as i64)
        } else {
            serializer.serialize_f64(self.value)
        }
    }
}

impl Serialize for Date {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("_kind", "date")?;
        map.serialize_entry("val", &self.to_string())?;
        map.end()
    }
}

impl Serialize for Time {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("_kind", "time")?;
        map.serialize_entry("val", &self.to_string())?;
        map.end()
    }
}

impl Serialize for DateTime {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("_kind", "dateTime")?;
        map.serialize_entry("val", &self.to_rfc3339_opts(SecondsFormat::AutoSi, true))?;
        if !self.is_utc() {
            map.serialize_entry("tz", &self.timezone_short_name())?;
        }
        map.end()
    }
}

impl Serialize for Coord {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(3))?;
        map.serialize_entry("_kind", "coord")?;
        map.serialize_entry("lat", &self.lat)?;
        map.serialize_entry("lng", &self.long)?;
        map.end()
    }
}

impl Serialize for XStr {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(3))?;
        map.serialize_entry("_kind", "xstr")?;
        map.serialize_entry("type", &self.r#type)?;
        map.serialize_entry("val", &self.value)?;
        map.end()
    }
}

impl Serialize for Dict {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(self.len()))?;
        for (k, v) in self.iter() {
            map.serialize_entry(k, v)?;
        }
        map.end()
    }
}

impl Serialize for Grid {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(4))?;
        map.serialize_entry("_kind", "grid")?;

        let meta = ensure_meta_has_ver(self.meta.as_ref());
        map.serialize_entry("meta", meta.as_ref())?;

        map.serialize_entry("cols", &self.columns)?;
        map.serialize_entry("rows", &self.rows)?;
        map.end()
    }
}

fn ensure_meta_has_ver<'a>(meta: Option<&'a Dict>) -> Cow<'a, Dict> {
    match meta {
        Some(meta) if meta.has("ver") => Cow::Borrowed(meta),
        Some(meta) => {
            let mut with_ver = meta.clone();
            with_ver.insert("ver".into(), "3.0".into());
            Cow::Owned(with_ver)
        }
        None => {
            let mut meta = Dict::new();
            meta.insert("ver".into(), "3.0".into());
            Cow::Owned(meta)
        }
    }
}

///
/// Serializes a haystack value to Haystack JSON (Hayson)
///
impl Serialize for HVal {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            HVal::Null => serializer.serialize_none(),

            HVal::Remove => Remove::serialize(&Remove, serializer),

            HVal::Marker => Marker::serialize(&Marker, serializer),

            HVal::Bool(value) => serializer.serialize_bool(value.value),

            HVal::Na => Na::serialize(&Na, serializer),

            HVal::Number(val) => Number::serialize(val, serializer),

            HVal::Str(val) => serializer.serialize_str(val.value.as_str()),

            HVal::Ref(val) => Ref::serialize(val, serializer),

            HVal::Symbol(val) => Symbol::serialize(val, serializer),

            HVal::Uri(val) => Uri::serialize(val, serializer),

            HVal::Date(val) => Date::serialize(val, serializer),

            HVal::Time(val) => Time::serialize(val, serializer),

            HVal::DateTime(val) => DateTime::serialize(val, serializer),

            HVal::Coord(val) => Coord::serialize(val, serializer),

            HVal::XStr(val) => XStr::serialize(val, serializer),

            HVal::List(val) => {
                let mut seq = serializer.serialize_seq(Some(val.len()))?;
                for el in val {
                    seq.serialize_element(el)?;
                }
                seq.end()
            }

            HVal::Dict(val) => Dict::serialize(val, serializer),

            HVal::Grid(val) => Grid::serialize(val, serializer),
        }
    }
}

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

    #[test]
    fn ensure_meta_has_ver_when_ver_exists() {
        let mut meta = Dict::new();
        meta.insert("ver".into(), "3.0".into());
        meta.insert("site".into(), "alpha".into());

        let result = ensure_meta_has_ver(Some(&meta));

        assert!(result.has("ver"));
        assert!(result.has("site"));
    }

    #[test]
    fn ensure_meta_has_ver_when_missing() {
        let mut meta = Dict::new();
        meta.insert("site".into(), "alpha".into());

        let result = ensure_meta_has_ver(Some(&meta));

        assert!(result.has("ver"));
        assert!(result.has("site"));
        assert!(!meta.has("ver"));
    }

    #[test]
    fn ensure_meta_has_ver_creates_meta_when_none() {
        let result = ensure_meta_has_ver(None);

        assert!(result.has("ver"));
        assert_eq!(result.len(), 1);
    }
}