goosedump 0.12.1

Coding agent context data browser
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) Jarkko Sakkinen 2026

use crate::display::SessionJson;
use crate::index::IndexEntry;
use jaq_core::load::{Arena, File, Loader};
use jaq_core::{Filter, ValT, Vars, data, unwrap_valr};
use jaq_json::Val;

pub struct QueryFilter {
    _arena: Arena,
    filter: Filter<data::JustLut<Val>>,
}

impl QueryFilter {
    pub fn parse(code: &str) -> anyhow::Result<Self> {
        let arena = Arena::default();
        let defs = jaq_core::defs()
            .chain(jaq_std::defs())
            .chain(jaq_json::defs());
        let funs = jaq_core::funs::<data::JustLut<Val>>()
            .chain(jaq_std::funs::<data::JustLut<Val>>())
            .chain(jaq_json::funs::<data::JustLut<Val>>());

        let loader = Loader::new(defs);
        let program = File { code, path: () };
        let modules = loader
            .load(&arena, program)
            .map_err(|errs| anyhow::anyhow!("failed to parse query filter: {errs:?}"))?;

        let filter = jaq_core::Compiler::<_, data::JustLut<Val>>::default()
            .with_funs(funs)
            .compile(modules)
            .map_err(|errs| anyhow::anyhow!("failed to compile query filter: {errs:?}"))?;

        Ok(Self {
            _arena: arena,
            filter,
        })
    }

    #[must_use]
    pub fn matches(&self, entry: &IndexEntry) -> bool {
        let session_json = SessionJson::from(entry);
        let Ok(json_val) = serde_json::to_value(&session_json) else {
            return false;
        };
        let Ok(val): Result<Val, _> = serde_json::from_value(json_val) else {
            return false;
        };

        let ctx = jaq_core::Ctx::<data::JustLut<Val>>::new(&self.filter.lut, Vars::new([]));
        let mut out = self.filter.id.run((ctx, val)).map(unwrap_valr);

        out.any(|res| match res {
            Ok(v) => v.as_bool(),
            Err(_) => false,
        })
    }
}