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 list of values.

use crate::value::Value;
use crate::value::equivalent::Equivalent;
use crate::value::meta::Meta;
use crate::value::print::Print;
use crate::value::tracer::Tracer;
use ahash::HashSet;
use ahash::HashSetExt;
use serde::Deserialize;
use serde::Serialize;

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct List {
    #[serde(default, skip_serializing_if = "Tracer::is_empty")]
    pub(crate) tracer: Tracer,
    #[serde(default, skip_serializing_if = "Meta::is_empty")]
    pub(crate) meta: Meta,
    pub(crate) data: Vec<Value>,
}

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

impl<V: Into<Value>> FromIterator<V> for List {
    fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
        Self {
            data: iter.into_iter().map(Into::into).collect(),
            ..Default::default()
        }
    }
}

impl From<Vec<Value>> for List {
    fn from(value: Vec<Value>) -> Self {
        Self {
            data: value,
            ..Default::default()
        }
    }
}

impl From<List> for Vec<Value> {
    fn from(value: List) -> Self {
        value.data
    }
}

impl IntoIterator for List {
    type Item = Value;
    type IntoIter = <Vec<Value> as IntoIterator>::IntoIter;

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

impl List {
    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<V: Into<Value>>(mut self, value: V) -> Self {
        self.data.push(value.into());
        self
    }

    pub fn get(&self, index: usize) -> Option<&Value> {
        self.data.get(index)
    }

    pub fn set(&mut self, index: usize, value: Value) {
        self.data[index] = value;
    }

    pub fn push(&mut self, value: Value) {
        self.data.push(value);
    }

    pub fn first(&self) -> Option<&Value> {
        self.data.first()
    }

    pub fn last(&self) -> Option<&Value> {
        self.data.last()
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

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

    pub fn contains(&self, other: &Value) -> bool {
        self.data.iter().any(|d| d.equivalent(other))
    }

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

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

    pub fn intersection(mut lists: Vec<List>) -> List {
        if lists.is_empty() {
            return List::default();
        }

        let mut data = lists.remove(0).data;
        data.retain(|d| lists.iter().all(|v| v.contains(d)));
        data.into_iter().collect()
    }

    pub fn union(lists: Vec<List>) -> List {
        let len = lists.first().map(|v| v.data.len()).unwrap_or_default();
        let mut data = Vec::with_capacity(len);
        let mut seen = HashSet::<Vec<u8>>::with_capacity(len);

        for list in lists {
            for value in list.data {
                if seen.insert(value.clone().join()) {
                    data.push(value);
                }
            }
        }

        data.into_iter().collect()
    }

    pub(crate) fn fold(&mut self, mut value: Self) {
        match value.meta.get(b"internal-insert") {
            Some(b"prepend") => {
                value.data.append(&mut self.data);
                self.data = value.data;
            }
            Some(b"append") => {
                self.data.append(&mut value.data);
            }
            _ => {
                *self = value;
            }
        }
    }
}