jaq_core/
lib.rs

1//! JSON query language interpreter.
2//!
3//! This crate allows you to execute jq-like filters.
4//!
5//! The example below demonstrates how to use this crate.
6//! See the implementation in the `jaq` crate if you are interested in how to:
7//!
8//! * enable usage of the standard library,
9//! * load JSON files lazily,
10//! * handle errors etc.
11//!
12//! (This example requires enabling the `serde_json` feature for `jaq-json`.)
13//!
14//! ~~~
15//! use jaq_core::{load, Compiler, Ctx, Error, FilterT, RcIter};
16//! use jaq_json::Val;
17//! use serde_json::{json, Value};
18//!
19//! let input = json!(["Hello", "world"]);
20//! let program = File { code: ".[]", path: () };
21//!
22//! use load::{Arena, File, Loader};
23//!
24//! let loader = Loader::new(jaq_std::defs().chain(jaq_json::defs()));
25//! let arena = Arena::default();
26//!
27//! // parse the filter
28//! let modules = loader.load(&arena, program).unwrap();
29//!
30//! // compile the filter
31//! let filter = jaq_core::Compiler::default()
32//!     .with_funs(jaq_std::funs().chain(jaq_json::funs()))
33//!     .compile(modules)
34//!     .unwrap();
35//!
36//! let inputs = RcIter::new(core::iter::empty());
37//!
38//! // iterator over the output values
39//! let mut out = filter.run((Ctx::new([], &inputs), Val::from(input)));
40//!
41//! assert_eq!(out.next(), Some(Ok(Val::from(json!("Hello")))));;
42//! assert_eq!(out.next(), Some(Ok(Val::from(json!("world")))));;
43//! assert_eq!(out.next(), None);;
44//! ~~~
45#![no_std]
46#![forbid(unsafe_code)]
47#![warn(missing_docs)]
48
49extern crate alloc;
50#[cfg(feature = "std")]
51extern crate std;
52
53pub mod box_iter;
54pub mod compile;
55mod exn;
56mod filter;
57mod fold;
58mod into_iter;
59pub mod load;
60pub mod ops;
61pub mod path;
62mod rc_iter;
63mod rc_lazy_list;
64mod rc_list;
65mod stack;
66pub mod val;
67
68pub use compile::Compiler;
69pub use exn::{Error, Exn};
70pub use filter::{Ctx, Cv, FilterT, Native, RunPtr, UpdatePtr};
71pub use rc_iter::RcIter;
72pub use val::{ValR, ValT, ValX, ValXs};
73
74use alloc::string::String;
75use rc_list::List as RcList;
76use stack::Stack;
77
78type Inputs<'i, V> = RcIter<dyn Iterator<Item = Result<V, String>> + 'i>;
79
80/// Argument of a definition, such as `$v` or `f` in `def foo($v; f): ...`.
81///
82/// In jq, we can bind filters in three different ways:
83///
84/// 1. `f as $x | ...`
85/// 2. `def g($x): ...; g(f)`
86/// 3. `def g(fx): ...; g(f)`
87///
88/// In the first two cases, we bind the outputs of `f` to a variable `$x`.
89/// In the third case, we bind `f` to a filter `fx`
90///
91/// When writing a native filter, this is used to declare its arguments.
92/// It is passed to [`compile::Compiler::with_funs`].
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum Bind<V = (), F = V> {
95    /// binding to a variable
96    Var(V),
97    /// binding to a filter
98    Fun(F),
99}
100
101impl<V, F> Bind<V, F> {
102    /// Move references inward.
103    pub(crate) fn as_ref(&self) -> Bind<&V, &F> {
104        match self {
105            Self::Var(x) => Bind::Var(x),
106            Self::Fun(x) => Bind::Fun(x),
107        }
108    }
109}
110
111impl<T> Bind<T, T> {
112    /// Apply a function to both binding types.
113    pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> Bind<U, U> {
114        match self {
115            Self::Var(x) => Bind::Var(f(x)),
116            Self::Fun(x) => Bind::Fun(f(x)),
117        }
118    }
119}
120
121/// Function from a value to a stream of value results.
122#[derive(Debug, Clone)]
123pub struct Filter<F>(compile::TermId, compile::Lut<F>);
124
125impl<F: FilterT> Filter<F> {
126    /// Run a filter on given input, yielding output values.
127    pub fn run<'a>(&'a self, cv: Cv<'a, F::V>) -> impl Iterator<Item = ValR<F::V>> + 'a {
128        self.0
129            .run(&self.1, cv)
130            .map(|v| v.map_err(|e| e.get_err().ok().unwrap()))
131    }
132
133    /// Run a filter on given input, panic if it does not yield the given output.
134    ///
135    /// This is for testing purposes.
136    pub fn yields(&self, x: F::V, ys: impl Iterator<Item = ValR<F::V>>) {
137        let inputs = RcIter::new(core::iter::empty());
138        let out = self.run((Ctx::new([], &inputs), x));
139        assert!(out.eq(ys));
140    }
141}