sim-lib-agent 0.1.2

Agent runtime surfaces for SIM.
Documentation
use super::super::store::snapshot_entries;
use super::types::MemoryBackend;
use sim_kernel::{Cx, Error, Expr, Result, Value};

pub(super) fn answer_memory_request(
    memory: &dyn MemoryBackend,
    cx: &mut Cx,
    expr: Expr,
) -> Result<Value> {
    let (op, args) = memory_request_parts(expr)?;
    match op.as_str() {
        "append" | "memory/append" => {
            let [msg] = args.as_slice() else {
                return Err(Error::Eval(
                    "memory append frame expects one message".to_owned(),
                ));
            };
            let msg = crate::expr_to_value(cx, msg)?;
            memory.append(cx, msg)?;
            cx.factory().nil()
        }
        "recent" | "memory/recent" => {
            let [count] = args.as_slice() else {
                return Err(Error::Eval(
                    "memory recent frame expects one count".to_owned(),
                ));
            };
            let count =
                crate::u32_from_expr(count, "memory recent frame expects an integer count")?;
            let recent = memory.recent(cx, count)?;
            cx.factory().list(recent)
        }
        "scan" | "memory/scan" => {
            let entries = match args.as_slice() {
                [] => snapshot_entries(memory.snapshot(cx)?)?
                    .into_iter()
                    .map(|entry| crate::expr_to_value(cx, &entry))
                    .collect::<Result<Vec<_>>>()?,
                [count] => {
                    let count =
                        crate::u32_from_expr(count, "memory scan frame expects an integer count")?;
                    memory.recent(cx, count)?
                }
                _ => {
                    return Err(Error::Eval(
                        "memory scan frame expects zero or one count".to_owned(),
                    ));
                }
            };
            cx.factory().list(entries)
        }
        "search" | "memory/search" => {
            let [query, k] = args.as_slice() else {
                return Err(Error::Eval(
                    "memory search frame expects a query and count".to_owned(),
                ));
            };
            let k = crate::u32_from_expr(k, "memory search frame expects an integer count")?;
            let results = memory.search(cx, query.clone(), k)?;
            cx.factory().list(results)
        }
        "snapshot" | "memory/snapshot" => {
            let snapshot = memory.snapshot(cx)?;
            cx.factory().expr(snapshot)
        }
        "restore" | "memory/restore" => {
            let [snapshot] = args.as_slice() else {
                return Err(Error::Eval(
                    "memory restore frame expects one snapshot".to_owned(),
                ));
            };
            memory.restore(cx, snapshot.clone())?;
            cx.factory().nil()
        }
        _ => Err(Error::Eval(format!("unknown memory frame operation {op}"))),
    }
}

fn memory_request_parts(expr: Expr) -> Result<(String, Vec<Expr>)> {
    let (Expr::List(items) | Expr::Vector(items)) = expr else {
        return Err(Error::Eval(
            "memory request frames expect a list or vector payload".to_owned(),
        ));
    };
    let Some((head, rest)) = items.split_first() else {
        return Err(Error::Eval(
            "memory request frames cannot be empty".to_owned(),
        ));
    };
    let Expr::Symbol(symbol) = head else {
        return Err(Error::Eval(
            "memory request frames expect a symbol operation".to_owned(),
        ));
    };
    Ok((symbol.to_string(), rest.to_vec()))
}