use crate::{compile_with, FileReports, Fun};
use jaq_core::{data, unwrap_valr, DataT, Lut, Vars};
use jaq_fmts::write::Writer;
use jaq_json::{Val, ValR};
use jaq_std::input::{self, Inputs, RcIter};
pub type Filter = jaq_core::Filter<DataKind>;
pub type Ctx<'a> = jaq_core::Ctx<'a, DataKind>;
pub struct DataKind;
impl DataT for DataKind {
type V<'a> = Val;
type Data<'a> = &'a Data<'a>;
}
pub struct Data<'a> {
pub runner: &'a Runner,
pub lut: &'a Lut<DataKind>,
pub inputs: Inputs<'a, Val>,
}
impl<'a> data::HasLut<'a, DataKind> for &'a Data<'a> {
fn lut(&self) -> &'a Lut<DataKind> {
self.lut
}
}
impl<'a> input::HasInputs<'a, Val> for &'a Data<'a> {
fn inputs(&self) -> Inputs<'a, Val> {
self.inputs
}
}
#[derive(Default)]
pub struct Runner {
pub null_input: bool,
pub color_err: bool,
pub writer: Writer,
}
impl Runner {
pub fn color_stdout(&self) -> bool {
!self.writer.pp.styles.reset.is_empty()
}
}
pub fn base_funs() -> impl Iterator<Item = Fun<DataKind>> {
let run = jaq_core::native::run::<DataKind>;
let core = jaq_core::funs::<DataKind>();
let std = jaq_std::funs::<DataKind>();
let input = input::funs::<DataKind>().into_vec().into_iter().map(run);
core.chain(std).chain(jaq_json::funs()).chain(input)
}
#[cfg(feature = "formats")]
pub fn funs() -> impl Iterator<Item = Fun<DataKind>> {
base_funs().chain(jaq_fmts::funs())
}
#[cfg(feature = "formats")]
pub fn compile(code: &str) -> Result<Filter, Vec<FileReports>> {
let defs = jaq_core::defs()
.chain(jaq_std::defs())
.chain(jaq_json::defs());
compile_with(code, defs, funs(), &[])
}
pub fn run<E>(
runner: &Runner,
filter: &Filter,
vars: Vars<Val>,
inputs: impl Iterator<Item = Result<Val, impl ToString>>,
fi: impl Fn(String) -> E,
mut f: impl FnMut(ValR) -> Result<(), E>,
) -> Result<(), E> {
let inputs = Box::new(inputs.map(|r| r.map_err(|e| e.to_string())));
let null = Box::new(core::iter::once(Ok(Val::Null)));
let null = &RcIter::new(null);
let data = Data {
runner,
lut: &filter.lut,
inputs: &RcIter::new(inputs),
};
let ctx = Ctx::new(&data, vars);
let outputs = |x| filter.id.run((ctx.clone(), x));
(if runner.null_input { null } else { data.inputs }).try_for_each(|x| match x {
Ok(x) => outputs(x).try_for_each(|y| f(unwrap_valr(y))),
Err(e) => Err(fi(e)),
})
}