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 crate::lang::adapter::Adapter;
use crate::lang::args::eval_args;
use crate::lang::arity::Arity;
use crate::lang::arity::assert_func_arity;
use crate::lang::error::Error;
use crate::lang::eval::replace_operation;
use crate::lang::machine::Machine;
use crate::lang::node::Node;
use crate::value::Value;
use crate::value::list::List;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Strategy {
    Clone,
    Mutate,
}

pub(crate) fn meta(
    adapter: &impl Adapter,
    machine: &mut Machine<'_>,
    stdin: Option<&Value>,
    node: Node,
) -> Result<Value, Error> {
    assert_func_arity(adapter, machine, stdin, &node, &Arity::minmax(1, 3))?;

    let strategy = Strategy::Mutate;
    let mut node = eval_args(adapter, machine, stdin, node)?;
    let value = node.pop().flatten().unwrap_or_default();
    let a1 = node.pop().flatten();
    let a0 = node.pop().flatten();

    match (a0, a1) {
        (Some(k), Some(s)) => meta_set(adapter, machine, value, strategy, &k.join(), Some(s)),
        (Some(k), None) => Ok(value.meta().get(&k.join()).unwrap_or_default().into()),
        (None, Some(k)) => Ok(value.meta().get(&k.join()).unwrap_or_default().into()),
        (None, None) => Ok(meta_into_keys(&value)),
    }
}

pub(crate) fn meta_facade_boolean(
    adapter: &impl Adapter,
    machine: &mut Machine<'_>,
    stdin: Option<&Value>,
    node: Node,
    strategy: Strategy,
    key: &[u8],
) -> Result<Value, Error> {
    meta_facade_unary(adapter, machine, stdin, node, strategy, key, key)
}

pub(crate) fn meta_facade_unary(
    adapter: &impl Adapter,
    machine: &mut Machine<'_>,
    stdin: Option<&Value>,
    node: Node,
    strategy: Strategy,
    key: &[u8],
    set: &[u8],
) -> Result<Value, Error> {
    debug_assert!(!set.is_empty(), "{}", String::from_utf8_lossy(set));

    assert_func_arity(adapter, machine, stdin, &node, &Arity::new(1))?;

    let mut node = eval_args(adapter, machine, stdin, node)?;
    let value = node.pop().flatten().unwrap_or_default();
    let set = Value::from(set);

    meta_set(adapter, machine, value, strategy, key, Some(set))
}

pub(crate) fn meta_facade_binary(
    adapter: &impl Adapter,
    machine: &mut Machine<'_>,
    stdin: Option<&Value>,
    node: Node,
    strategy: Strategy,
    key: &[u8],
) -> Result<Value, Error> {
    assert_func_arity(adapter, machine, stdin, &node, &Arity::minmax(1, 2))?;

    let mut node = eval_args(adapter, machine, stdin, node)?;
    let value = node.pop().flatten().unwrap_or_default();
    let set = node.pop().flatten();

    meta_set(adapter, machine, value, strategy, key, set)
}

fn meta_set(
    adapter: &impl Adapter,
    machine: &mut Machine<'_>,
    value: Value,
    strategy: Strategy,
    key: &[u8],
    set: Option<Value>,
) -> Result<Value, Error> {
    let Some(set) = set else {
        return Ok(value.meta().get(key).unwrap_or_default().into());
    };

    let prev = value.clone();
    let next = value.with_meta(key, set.join());

    if strategy == Strategy::Mutate {
        if let Some(keys) = next.tracer().keys.clone() {
            replace_operation(adapter, machine, &keys, &prev, &next)?;
        }
    }

    Ok(next)
}

fn meta_into_keys(value: &Value) -> Value {
    value
        .meta()
        .clone()
        .into_keys()
        .into_iter()
        .map(Value::from)
        .collect::<List>()
        .into()
}