pipa-lang 1.0.0-alpha.1

A tiny template language
Documentation
// SPDX-FileCopyrightText: Copyright 2026 olav@occy.org
// SPDX-License-Identifier: MPL-2.0

use indexmap::IndexMap;
use serde::Deserializer;
use serde::Serializer;
use serde::de::DeserializeSeed;
use serde::de::MapAccess;
use serde::de::Visitor;
use serde::ser::Error;
use serde::ser::SerializeMap;

struct IndexMapVisitor;

impl<'de> Visitor<'de> for IndexMapVisitor {
    type Value = IndexMap<Vec<u8>, Vec<u8>, ahash::RandomState>;

    fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        formatter.write_str("map")
    }

    fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<Self::Value, M::Error> {
        let mut map = IndexMap::with_capacity_and_hasher(
            access.size_hint().unwrap_or(0),
            ahash::RandomState::default(),
        );

        while let Some((k, v)) = access.next_entry_seed(BytePart, BytePart)? {
            map.insert(k, v);
        }

        Ok(map)
    }
}

struct BytePart;

impl<'de> DeserializeSeed<'de> for BytePart {
    type Value = Vec<u8>;

    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
        deserializer.deserialize_str(self)
    }
}

impl Visitor<'_> for BytePart {
    type Value = Vec<u8>;

    fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        formatter.write_str("string")
    }

    fn visit_str<E: serde::de::Error>(self, string: &str) -> Result<Self::Value, E> {
        Ok(string.as_bytes().to_vec())
    }
}

pub(crate) fn serialize<S: Serializer>(
    data: &IndexMap<Vec<u8>, Vec<u8>, ahash::RandomState>,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    let mut map = serializer.serialize_map(Some(data.len()))?;

    for (k, v) in data {
        let k = core::str::from_utf8(k).map_err(Error::custom)?;
        let v = core::str::from_utf8(v).map_err(Error::custom)?;
        map.serialize_entry(k, v)?;
    }

    map.end()
}

pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
    deserializer: D,
) -> Result<IndexMap<Vec<u8>, Vec<u8>, ahash::RandomState>, D::Error> {
    deserializer.deserialize_map(IndexMapVisitor)
}

#[cfg(test)]
mod test {
    use indexmap::IndexMap;
    use serde::Deserialize;
    use serde::Serialize;

    #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
    struct Map {
        #[serde(with = "super")]
        data: IndexMap<Vec<u8>, Vec<u8>, ahash::RandomState>,
    }

    #[test]
    fn test() {
        let mut map = Map::default();
        map.data.insert(b"a".to_vec(), b"b".to_vec());
        map.data.insert("".as_bytes().to_vec(), b"".to_vec());
        assert_eq!(round_trip_json_str(&map), map);
        assert_eq!(round_trip_json_vec(&map), map);
    }

    #[track_caller]
    fn round_trip_json_str(map: &Map) -> Map {
        serde_json::from_str(&serde_json::to_string(map).unwrap()).unwrap()
    }

    #[track_caller]
    fn round_trip_json_vec(map: &Map) -> Map {
        serde_json::de::from_slice(&serde_json::to_vec(map).unwrap()).unwrap()
    }
}