Skip to main content

jaq_all/
lib.rs

1//! Compile & run jq filters and read & write data.
2//!
3//! This crate provides an opinionated, high-level API to
4//! integrate jaq into applications.
5//! It combines the functionality of several crates, which it re-exports:
6//!
7//! - `jaq-core`: basic compilation/execution of jq filters
8//! - `jaq-std`:  standard library
9//! - `jaq-json`: type of values
10//! - `jaq-fmts`: multi-format support
11//!
12//! In contrast to these crates, which aim for
13//! minimal functionality and maximal flexibility, this crate aims for
14//! maximal functionality and minimal flexibility.
15//! This makes this crate easier to use,
16//! at the price of not covering all use cases.
17//! You could say that this crate is the "instant food" of jaq crates:
18//! it is easy to use, but difficult to customise.
19//!
20//! This crate is expected to change more frequently.
21//! However, because of its small amount of exposed functions,
22//! it may be easier to stay up-to-date with it than with its dependencies.
23//!
24//! # Usage
25//!
26//! Embedding jaq into your own application involves three steps:
27//!
28//! 1. Compile a filter.
29//!    This takes a jq filter as string and parses & compiles it to
30//!    a filter ready to be executed by jaq.
31//!    You can do this with
32//!    [`data::compile`], or with
33//!    [`compile_with`] if you need more flexibility.
34//!    If this fails, e.g. due to parse errors,
35//!    you can use [`load::FileReportsDisp`] to pretty-print errors.
36//! 2. Read input data.
37//!    Every jq filter needs to be supplied with input.
38//!    This may be
39//!    read from an input stream ([`std::io::Read`]) or
40//!    parsed from a [`&str`]/bytes.
41//!    You can do this with the functions in the [`fmts::read`] module, such as
42//!    [`fmts::read::json::read_many`] or
43//!    [`fmts::read::json::parse_many`].
44//! 3. Run the filter.
45//!    You can do this with [`data::run`].
46//! 4. Write output data (optional).
47//!    During or after filter execution, you may want to print output values.
48//!    You can do this with the functions in the [`fmts::write`] module, such as
49//!    [`fmts::write::write`].
50//!
51//! The `main` example in this crate shows you how to do all of this.
52//! You can run it with `cargo run --example main`.
53#![warn(missing_docs)]
54
55extern crate alloc;
56extern crate std;
57
58pub mod data;
59pub mod load;
60
61pub use jaq_core;
62pub use jaq_fmts as fmts;
63pub use jaq_json as json;
64pub use jaq_std;
65
66use jaq_core::load::{import, parse::Def, Arena, File, Loader};
67use jaq_core::{compile::Compiler, native::Fun, DataT, Filter};
68use load::{compile_errors, load_errors, FileReports};
69
70/// Compile a filter without access to external files.
71///
72/// A simplified version of this function is [`data::compile`].
73pub fn compile_with<D: DataT>(
74    code: &str,
75    defs: impl Iterator<Item = Def>,
76    funs: impl Iterator<Item = Fun<D>>,
77    vars: &[String],
78) -> Result<Filter<D>, Vec<FileReports>> {
79    let vars: Vec<_> = vars.iter().map(|v| format!("${v}")).collect();
80    let arena = Arena::default();
81    let loader = Loader::new(defs);
82    let modules = loader
83        .load(&arena, File { path: (), code })
84        .map_err(load_errors)?;
85
86    import(&modules, |_path| Err("file loading not supported".into())).map_err(load_errors)?;
87
88    Compiler::default()
89        .with_funs(funs)
90        .with_global_vars(vars.iter().map(|v| &**v))
91        .compile(modules)
92        .map_err(compile_errors)
93}
94
95/// Definitions from `jaq_core`, `jaq_std` and `jaq_json`.
96pub fn defs() -> impl Iterator<Item = Def> {
97    jaq_core::defs()
98        .chain(jaq_std::defs())
99        .chain(jaq_json::defs())
100}