Skip to content

Commit cc155f3

Browse files
committed
rewrite jsondocck from JSONPath to jq
i tried to keep the original code structure, but it's an almost entire rewrite. thankfully, there are more deletions than insertions, i expect jq to be incredibly powerful to supersede previous set of directives. no tests yet, but it seems to be working. i also simplified the `LINE_PATTERN` regex and added a comment for each section. jaq's guts are a bit arcane to me, but it seems to be only related to its setup, a resulted API turned out to be pretty straightforward
1 parent 48751ae commit cc155f3

File tree

6 files changed

+200
-314
lines changed

6 files changed

+200
-314
lines changed

Cargo.lock

Lines changed: 68 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ version = "0.21.7"
246246
source = "registry+https://github.com/rust-lang/crates.io-index"
247247
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
248248

249+
[[package]]
250+
name = "base64"
251+
version = "0.22.1"
252+
source = "registry+https://github.com/rust-lang/crates.io-index"
253+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
254+
249255
[[package]]
250256
name = "basic-toml"
251257
version = "0.1.10"
@@ -1081,6 +1087,12 @@ version = "1.0.10"
10811087
source = "registry+https://github.com/rust-lang/crates.io-index"
10821088
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
10831089

1090+
[[package]]
1091+
name = "dyn-clone"
1092+
version = "1.0.19"
1093+
source = "registry+https://github.com/rust-lang/crates.io-index"
1094+
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
1095+
10841096
[[package]]
10851097
name = "either"
10861098
version = "1.15.0"
@@ -1931,6 +1943,46 @@ version = "1.0.15"
19311943
source = "registry+https://github.com/rust-lang/crates.io-index"
19321944
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
19331945

1946+
[[package]]
1947+
name = "jaq-core"
1948+
version = "2.2.0"
1949+
source = "registry+https://github.com/rust-lang/crates.io-index"
1950+
checksum = "4c3dcf2d6c22e94aef9da5823024d0f7b726332c993ee5b337ee06bdf999672a"
1951+
dependencies = [
1952+
"dyn-clone",
1953+
"once_cell",
1954+
"typed-arena",
1955+
]
1956+
1957+
[[package]]
1958+
name = "jaq-json"
1959+
version = "1.1.2"
1960+
source = "registry+https://github.com/rust-lang/crates.io-index"
1961+
checksum = "848f085cdaa2c0508c87e021392ec5fe3fd7da90d6d548f3c1790e1d499f3080"
1962+
dependencies = [
1963+
"foldhash",
1964+
"indexmap",
1965+
"jaq-core",
1966+
"jaq-std",
1967+
"serde_json",
1968+
]
1969+
1970+
[[package]]
1971+
name = "jaq-std"
1972+
version = "2.1.1"
1973+
source = "registry+https://github.com/rust-lang/crates.io-index"
1974+
checksum = "9360182e2b7837fe24ad8da58e21b29c5bff9eb722e68603986535c1e1b39a46"
1975+
dependencies = [
1976+
"aho-corasick",
1977+
"base64 0.22.1",
1978+
"chrono",
1979+
"jaq-core",
1980+
"libm",
1981+
"log",
1982+
"regex-lite",
1983+
"urlencoding",
1984+
]
1985+
19341986
[[package]]
19351987
name = "jiff"
19361988
version = "0.2.15"
@@ -1981,10 +2033,11 @@ version = "0.0.0"
19812033
dependencies = [
19822034
"fs-err",
19832035
"getopts",
1984-
"jsonpath-rust",
2036+
"jaq-core",
2037+
"jaq-json",
2038+
"jaq-std",
19852039
"regex",
19862040
"serde_json",
1987-
"shlex",
19882041
]
19892042

19902043
[[package]]
@@ -2000,19 +2053,6 @@ dependencies = [
20002053
"serde_json",
20012054
]
20022055

2003-
[[package]]
2004-
name = "jsonpath-rust"
2005-
version = "1.0.2"
2006-
source = "registry+https://github.com/rust-lang/crates.io-index"
2007-
checksum = "5b37465feaf9d41f74df7da98c6c1c31ca8ea06d11b5bf7869c8f1ccc51a793f"
2008-
dependencies = [
2009-
"pest",
2010-
"pest_derive",
2011-
"regex",
2012-
"serde_json",
2013-
"thiserror 2.0.12",
2014-
]
2015-
20162056
[[package]]
20172057
name = "lazy_static"
20182058
version = "1.5.0"
@@ -2694,51 +2734,6 @@ dependencies = [
26942734
"libc",
26952735
]
26962736

2697-
[[package]]
2698-
name = "pest"
2699-
version = "2.8.0"
2700-
source = "registry+https://github.com/rust-lang/crates.io-index"
2701-
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
2702-
dependencies = [
2703-
"memchr",
2704-
"thiserror 2.0.12",
2705-
"ucd-trie",
2706-
]
2707-
2708-
[[package]]
2709-
name = "pest_derive"
2710-
version = "2.8.0"
2711-
source = "registry+https://github.com/rust-lang/crates.io-index"
2712-
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
2713-
dependencies = [
2714-
"pest",
2715-
"pest_generator",
2716-
]
2717-
2718-
[[package]]
2719-
name = "pest_generator"
2720-
version = "2.8.0"
2721-
source = "registry+https://github.com/rust-lang/crates.io-index"
2722-
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
2723-
dependencies = [
2724-
"pest",
2725-
"pest_meta",
2726-
"proc-macro2",
2727-
"quote",
2728-
"syn 2.0.103",
2729-
]
2730-
2731-
[[package]]
2732-
name = "pest_meta"
2733-
version = "2.8.0"
2734-
source = "registry+https://github.com/rust-lang/crates.io-index"
2735-
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
2736-
dependencies = [
2737-
"once_cell",
2738-
"pest",
2739-
"sha2",
2740-
]
2741-
27422737
[[package]]
27432738
name = "phf"
27442739
version = "0.11.3"
@@ -4663,7 +4658,7 @@ version = "0.0.0"
46634658
dependencies = [
46644659
"arrayvec",
46654660
"askama",
4666-
"base64",
4661+
"base64 0.21.7",
46674662
"expect-test",
46684663
"indexmap",
46694664
"itertools",
@@ -5524,6 +5519,12 @@ dependencies = [
55245519
"rustc-hash 2.1.1",
55255520
]
55265521

5522+
[[package]]
5523+
name = "typed-arena"
5524+
version = "2.0.2"
5525+
source = "registry+https://github.com/rust-lang/crates.io-index"
5526+
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
5527+
55275528
[[package]]
55285529
name = "typenum"
55295530
version = "1.18.0"
@@ -5539,12 +5540,6 @@ dependencies = [
55395540
"regex-lite",
55405541
]
55415542

5542-
[[package]]
5543-
name = "ucd-trie"
5544-
version = "0.1.7"
5545-
source = "registry+https://github.com/rust-lang/crates.io-index"
5546-
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
5547-
55485543
[[package]]
55495544
name = "ui_test"
55505545
version = "0.29.2"
@@ -5716,6 +5711,12 @@ dependencies = [
57165711
"percent-encoding",
57175712
]
57185713

5714+
[[package]]
5715+
name = "urlencoding"
5716+
version = "2.1.3"
5717+
source = "registry+https://github.com/rust-lang/crates.io-index"
5718+
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
5719+
57195720
[[package]]
57205721
name = "utf-8"
57215722
version = "0.7.6"

src/tools/compiletest/src/header.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -789,8 +789,7 @@ const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[
789789
"!snapshot",
790790
];
791791

792-
const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
793-
&["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"];
792+
const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = &["jq", "arg"];
794793

795794
/// The (partly) broken-down contents of a line containing a test directive,
796795
/// which [`iter_header`] passes to its callback function.

src/tools/jsondocck/Cargo.toml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ name = "jsondocck"
33
edition = "2024"
44

55
[dependencies]
6-
jsonpath-rust = "1.0.0"
76
getopts = "0.2"
8-
regex = "1.4"
9-
shlex = "1.0"
10-
serde_json = "1.0"
11-
fs-err = "2.5.0"
7+
regex = "1"
8+
serde_json = "1"
9+
fs-err = "2"
10+
jaq-core = "2"
11+
# FIXME: Depends on `jaq-std` with default features (a ton of unused by `jsondocck` stuff). Consider
12+
# disabling them when it'll be possible.
13+
jaq-json = { version = "1", default-features = false, features = [
14+
"serde_json",
15+
] }
16+
jaq-std = { version = "2", default-features = false }

src/tools/jsondocck/src/cache.rs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,77 @@
1+
use std::borrow::Cow;
12
use std::collections::HashMap;
2-
use std::path::Path;
3+
use std::iter;
4+
use std::path::{Path, PathBuf};
35

46
use fs_err as fs;
7+
use jaq_core::load::{Arena, File, Loader};
8+
use jaq_core::{Compiler, Ctx, RcIter};
9+
use jaq_json::Val;
510
use serde_json::Value;
611

712
use crate::config::Config;
813

914
#[derive(Debug)]
1015
pub struct Cache {
11-
value: Value,
12-
pub variables: HashMap<String, Value>,
16+
value: Val,
17+
global_vars: HashMap<String, Val>,
1318
}
1419

1520
impl Cache {
16-
/// Create a new cache, used to read files only once and otherwise store their contents.
17-
pub fn new(config: &Config) -> Cache {
18-
let root = Path::new(&config.doc_dir);
21+
/// Create a new cache, used to read a documentation JSON file only once and otherwise store its
22+
/// content.
23+
pub fn new(Config { doc_dir, template }: &Config) -> Self {
24+
let root = Path::new(doc_dir);
1925
// `filename` needs to replace `-` with `_` to be sure the JSON path will always be valid.
20-
let filename =
21-
Path::new(&config.template).file_stem().unwrap().to_str().unwrap().replace('-', "_");
22-
let file_path = root.join(&Path::with_extension(Path::new(&filename), "json"));
23-
let content = fs::read_to_string(&file_path).expect("failed to read JSON file");
26+
let mut filename: PathBuf =
27+
Path::new(template).file_stem().unwrap().to_str().unwrap().replace('-', "_").into();
28+
29+
filename.set_extension("json");
30+
31+
let content = fs::read(root.join(filename)).expect("failed to read a JSON file");
2432

2533
Cache {
26-
value: serde_json::from_str::<Value>(&content).expect("failed to convert from JSON"),
27-
variables: HashMap::from([("FILE".to_owned(), config.template.clone().into())]),
34+
value: serde_json::from_slice::<Value>(&content)
35+
.expect("failed to convert from JSON")
36+
.into(),
37+
// FIXME: Replace this with empty `HashMap` and use `input_filename` instead of `$FILE`
38+
// in tests once https://github.com/01mf02/jaq/issues/144 is fixed.
39+
global_vars: [("$FILE".into(), template.to_owned().into())].into(),
2840
}
2941
}
3042

31-
// FIXME: Make this failible, so jsonpath syntax error has line number.
32-
pub fn select(&self, path: &str) -> Vec<&Value> {
33-
jsonpath_rust::query::js_path_vals(path, &self.value).unwrap()
43+
pub fn arg(&mut self, name: &str, value: Val) {
44+
self.global_vars.insert(format!("${name}"), value);
45+
}
46+
47+
pub fn filter(&self, code: &str) -> Result<Val, Cow<'static, str>> {
48+
let arena = Arena::default();
49+
let modules = Loader::new(
50+
// `tonumber` depends on `fromjson` that requires adding the extra "hifijson"
51+
// dependency.
52+
jaq_std::defs().chain(jaq_json::defs().filter(|def| !matches!(def.name, "tonumber"))),
53+
)
54+
.load(&arena, File { code, path: () })
55+
.map_err(|e| format!("failed to parse the given filter: {:?}", e.first().unwrap().1))?;
56+
let filter = Compiler::default()
57+
.with_funs(jaq_std::funs().chain(jaq_json::base_funs()))
58+
.with_global_vars(self.global_vars.keys().map(String::as_ref))
59+
.compile(modules)
60+
.map_err(|e| {
61+
format!("failed to compile the given filter: {:?}", e.first().unwrap().1)
62+
})?;
63+
let inputs = RcIter::new(iter::empty());
64+
let mut outputs =
65+
filter.run((Ctx::new(self.global_vars.values().cloned(), &inputs), self.value.clone()));
66+
let output = outputs
67+
.next()
68+
.ok_or::<Cow<'_, _>>("directive returned nothing".into())?
69+
.map_err(|e| format!("failed to execute the given filter: {e}"))?;
70+
71+
if outputs.next().is_some() {
72+
return Err("expected a single output, received multiple".into());
73+
}
74+
75+
Ok(output)
3476
}
3577
}

0 commit comments

Comments
 (0)