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

//! A map from bytes to values.

use crate::serde::byte_keys_as_strings;
use crate::value::Value;
use crate::value::equivalent::Equivalent;
use crate::value::keys::Keys;
use crate::value::meta::Meta;
use crate::value::print::Print;
use crate::value::tracer::Tracer;
use indexmap::IndexMap;
use indexmap::map::Entry;
use serde::Deserialize;
use serde::Serialize;

#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Map {
    #[serde(default, skip_serializing_if = "Tracer::is_empty")]
    pub(crate) tracer: Tracer,
    #[serde(default, skip_serializing_if = "Meta::is_empty")]
    pub(crate) meta: Meta,
    #[serde(with = "byte_keys_as_strings")]
    pub(crate) data: IndexMap<Vec<u8>, Value, ahash::RandomState>,
}

impl core::fmt::Debug for Map {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("Map")
            .field("tracer", &self.tracer)
            .field("meta", &self.meta)
            .field("data", &DebugMap(&self.data))
            .finish()
    }
}

impl core::fmt::Display for Map {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(&String::from_utf8_lossy(&self.clone().print_syntax()))
    }
}

struct DebugMap<'a>(&'a IndexMap<Vec<u8>, Value, ahash::RandomState>);

impl core::hash::Hash for Map {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        for (k, v) in &self.data {
            k.hash(state);
            v.hash(state);
        }
    }
}

impl core::fmt::Debug for DebugMap<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_map()
            .entries(self.0.iter().map(|(k, v)| (String::from_utf8_lossy(k), v)))
            .finish()
    }
}

impl<K: Into<Vec<u8>>, V: Into<Value>> From<(K, V)> for Map {
    fn from(value: (K, V)) -> Self {
        [value].into_iter().collect()
    }
}

impl<K: Into<Vec<u8>>, V: Into<Value>> FromIterator<(K, V)> for Map {
    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
        Self {
            data: iter
                .into_iter()
                .map(|(k, v)| (k.into(), v.into()))
                .collect(),
            ..Default::default()
        }
    }
}

impl FromIterator<Map> for Map {
    fn from_iter<T: IntoIterator<Item = Map>>(iter: T) -> Self {
        Self {
            data: iter.into_iter().flat_map(Map::into_iter).collect(),
            ..Default::default()
        }
    }
}

impl FromIterator<(Keys, Value)> for Map {
    fn from_iter<T: IntoIterator<Item = (Keys, Value)>>(iter: T) -> Self {
        Self {
            data: iter.into_iter().map(|(k, v)| (k.join(), v)).collect(),
            ..Default::default()
        }
    }
}

impl IntoIterator for Map {
    type Item = (Vec<u8>, Value);
    type IntoIter = <IndexMap<Vec<u8>, Value> as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.data.into_iter()
    }
}

impl Map {
    pub(crate) fn trace<T: Into<Tracer>>(&mut self, tracer: T) {
        self.tracer.trace(tracer);
    }

    pub(crate) fn tracer(&self) -> &Tracer {
        &self.tracer
    }

    pub fn with<K: Into<Vec<u8>>, V: Into<Value>>(mut self, key: K, value: V) -> Self {
        self.fold_key(key.into(), value.into());
        self
    }

    pub fn with_fold(mut self, other: Self) -> Self {
        self.fold(other);
        self
    }

    pub fn contains_key<K: AsRef<[u8]>>(&self, key: K) -> bool {
        self.data.contains_key(key.as_ref())
    }

    pub fn contains_value(&self, value: &Value) -> bool {
        self.data.iter().any(|(_, v)| v.equivalent(value))
    }

    pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &Value)> {
        self.data.iter()
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Vec<u8>, &mut Value)> {
        self.data.iter_mut()
    }

    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    pub fn clear(mut self) -> Self {
        self.data = IndexMap::default();
        self
    }

    pub fn join(self) -> Vec<u8> {
        self.data.into_values().flat_map(Value::join).collect()
    }

    pub fn sort(&mut self) {
        self.data.sort_keys();
    }

    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Option<&Value> {
        self.data.get(key.as_ref())
    }

    pub fn get_mut<'a>(&'a mut self, key: &[u8]) -> Option<&'a mut Value> {
        self.data.get_mut(key)
    }

    pub fn shift_remove<K: AsRef<[u8]>>(&mut self, key: K) -> Option<Value> {
        self.data.shift_remove(key.as_ref())
    }

    pub fn swap_remove<K: AsRef<[u8]>>(&mut self, key: K) -> Option<Value> {
        self.data.swap_remove(key.as_ref())
    }

    #[doc(hidden)]
    pub fn fold(&mut self, other: Self) {
        for (key, value) in other.data {
            match self.data.entry(key.clone()) {
                Entry::Occupied(mut e) => {
                    e.get_mut().fold(value);
                }
                Entry::Vacant(_) => match other.meta.get(b"internal-insert") {
                    Some(b"prepend") => self.insert_prepend(key, value),
                    _ => self.insert_append(key, value),
                },
            }
        }
    }

    #[doc(hidden)]
    pub fn fold_key<K: Into<Vec<u8>>, V: Into<Value>>(&mut self, key: K, value: V) {
        let key = key.into();
        let value = value.into();

        match self.data.entry(key.clone()) {
            Entry::Occupied(mut e) => e.get_mut().fold(value),
            Entry::Vacant(_) => drop(self.insert(key, value)),
        }
    }

    pub fn insert<K: Into<Vec<u8>>, V: Into<Value>>(&mut self, key: K, value: V) {
        let key = key.into();
        let value = value.into();

        match value.meta().get(b"internal-insert") {
            Some(b"prepend") => self.insert_prepend(key, value),
            _ => self.insert_append(key, value),
        }
    }

    fn insert_append<K: Into<Vec<u8>>, V: Into<Value>>(&mut self, key: K, value: V) {
        self.data.insert(key.into(), value.into());
    }

    fn insert_prepend<K: Into<Vec<u8>>, V: Into<Value>>(&mut self, key: K, value: V) {
        self.data.insert_before(0, key.into(), value.into());
    }
}