From 9df913f824ad5343cb96d54f777e966306b2d3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C3=B6lfchen?= <115360611+W-lfchen@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:19:16 +0200 Subject: [PATCH 1/4] feat: add raw-output arg to jq Co-authored-by: Roman Hargrave --- crates/simplexpr/src/eval.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index ecb644e7..4d18b04c 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -456,7 +456,9 @@ fn call_expr_function(name: &str, args: Vec) -> Result Err(EvalError::WrongArgCount(name.to_string())), }, "jq" => match args.as_slice() { - [json, code] => run_jaq_function(json.as_json_value()?, code.as_string()?) + [json, code] => run_jaq_function(json.as_json_value()?, code.as_string()?, "") + .map_err(|e| EvalError::Spanned(code.span(), Box::new(e))), + [json, code, args] => run_jaq_function(json.as_json_value()?, code.as_string()?, &args.as_string()?) .map_err(|e| EvalError::Spanned(code.span(), Box::new(e))), _ => Err(EvalError::WrongArgCount(name.to_string())), }, @@ -506,16 +508,22 @@ fn prepare_jaq_filter(code: String) -> Result, EvalEr Ok(Arc::new(filter)) } -fn run_jaq_function(json: serde_json::Value, code: String) -> Result { - let filter: Arc = prepare_jaq_filter(code)?; +fn run_jaq_function(json: serde_json::Value, code: String, args: &str) -> Result { let inputs = jaq_interpret::RcIter::new(std::iter::empty()); - let out = filter + prepare_jaq_filter(code)? .run((jaq_interpret::Ctx::new([], &inputs), jaq_interpret::Val::from(json))) - .map(|x| x.map(Into::::into)) - .map(|x| x.map(|x| DynVal::from_string(serde_json::to_string(&x).unwrap()))) + .map(|r| r.map(Into::::into)) + .map(|r| { + r.map(|v| match v { + // Per jq docs, "raw-output" behavior simply omits + // quotation marks from strings, and outputs what would + // otherwise be valid JSON, so this should replicate that. + serde_json::Value::String(contents) if args == "r" => DynVal::from_string(contents), + anyval => DynVal::from_string(serde_json::to_string(&anyval).unwrap()), + }) + }) .collect::>() - .map_err(|e| EvalError::JaqError(e.to_string()))?; - Ok(out) + .map_err(|e| EvalError::JaqError(e.to_string())) } #[cfg(test)] From b7e4fbdf50f230a2792329be55c2c6acda140b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C3=B6lfchen?= <115360611+W-lfchen@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:49:48 +0100 Subject: [PATCH 2/4] test: add some test cases for jq raw output --- crates/simplexpr/src/eval.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 4d18b04c..064191c1 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -584,5 +584,9 @@ mod tests { lazy_evaluation_or(r#"true || "null".test"#) => Ok(DynVal::from(true)), lazy_evaluation_elvis(r#""test"?: "null".test"#) => Ok(DynVal::from("test")), jq_basic_index(r#"jq("[7,8,9]", ".[0]")"#) => Ok(DynVal::from(7)), + jq_raw_arg(r#"jq("[ \"foo\" ]", ".[0]", "r")"#) => Ok(DynVal::from("foo")), + jq_empty_arg(r#"jq("[ \"foo\" ]", ".[0]", "")"#) => Ok(DynVal::from(r#""foo""#)), + jq_invalid_arg(r#"jq("[ \"foo\" ]", ".[0]", "hello")"#) => Ok(DynVal::from(r#""foo""#)), + jq_no_arg(r#"jq("[ \"foo\" ]", ".[0]")"#) => Ok(DynVal::from(r#""foo""#)), } } From 18872ee6152725063347d558da6f6391ff4cf3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C3=B6lfchen?= <115360611+W-lfchen@users.noreply.github.com> Date: Sun, 27 Oct 2024 18:05:11 +0100 Subject: [PATCH 3/4] refactor: improve run_jaq_function --- crates/simplexpr/src/eval.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 064191c1..c619da03 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -509,17 +509,15 @@ fn prepare_jaq_filter(code: String) -> Result, EvalEr } fn run_jaq_function(json: serde_json::Value, code: String, args: &str) -> Result { - let inputs = jaq_interpret::RcIter::new(std::iter::empty()); + use jaq_interpret::{Ctx, RcIter, Val}; prepare_jaq_filter(code)? - .run((jaq_interpret::Ctx::new([], &inputs), jaq_interpret::Val::from(json))) + .run((Ctx::new([], &RcIter::new(std::iter::empty())), Val::from(json))) .map(|r| r.map(Into::::into)) - .map(|r| { - r.map(|v| match v { - // Per jq docs, "raw-output" behavior simply omits - // quotation marks from strings, and outputs what would - // otherwise be valid JSON, so this should replicate that. - serde_json::Value::String(contents) if args == "r" => DynVal::from_string(contents), - anyval => DynVal::from_string(serde_json::to_string(&anyval).unwrap()), + .map(|x| { + x.map(|val| match (args, val) { + ("r", serde_json::Value::String(s)) => DynVal::from_string(s), + // invalid arguments are silently ignored + (_, v) => DynVal::from_string(serde_json::to_string(&v).unwrap()), }) }) .collect::>() From 70a62e42f1a8670f50a0dd31183b630c61a650fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C3=B6lfchen?= <115360611+W-lfchen@users.noreply.github.com> Date: Sun, 27 Oct 2024 18:15:54 +0100 Subject: [PATCH 4/4] docs: document changes --- CHANGELOG.md | 1 + docs/src/expression_language.md | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3710363e..626d43b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to eww will be listed here, starting at changes since versio - Fix wayland monitor names support (By: dragonnn) ### Features +- Add jq `raw-output` support (By: RomanHargrave) - Update rust toolchain to 1.81.0 (By: w-lfchen) - Add `:fill-svg` and `:preserve-aspect-ratio` properties to images (By: hypernova7, w-lfchen) - Add `:truncate` property to labels, disabled by default (except in cases where truncation would be enabled in version `0.5.0` and before) (By: Rayzeq). diff --git a/docs/src/expression_language.md b/docs/src/expression_language.md index 0fb51e6a..1850793d 100644 --- a/docs/src/expression_language.md +++ b/docs/src/expression_language.md @@ -44,14 +44,17 @@ Supported currently are the following features: - `degtorad(number)`: Converts a number from degrees to radians - `radtodeg(number)`: Converts a number from radians to degrees - `replace(string, regex, replacement)`: Replace matches of a given regex in a string - - `search(string, regex)`: Search for a given regex in a string (returns array) - - `matches(string, regex)`: check if a given string matches a given regex (returns bool) - - `captures(string, regex)`: Get the captures of a given regex in a string (returns array) - - `strlength(value)`: Gets the length of the string + - `search(string, regex)`: Search for a given regex in a string (returns array) + - `matches(string, regex)`: check if a given string matches a given regex (returns bool) + - `captures(string, regex)`: Get the captures of a given regex in a string (returns array) + - `strlength(value)`: Gets the length of the string - `substring(string, start, length)`: Return a substring of given length starting at the given index - - `arraylength(value)`: Gets the length of the array - - `objectlength(value)`: Gets the amount of entries in the object - - `jq(value, jq_filter_string)`: run a [jq](https://stedolan.github.io/jq/manual/) style command on a json value. (Uses [jaq](https://crates.io/crates/jaq) internally). + - `arraylength(value)`: Gets the length of the array + - `objectlength(value)`: Gets the amount of entries in the object + - `jq(value, jq_filter_string)`: run a [jq](https://jqlang.github.io/jq/manual/) style command on a json value. (Uses [jaq](https://crates.io/crates/jaq) internally). + - `jq(value, jq_filter_string, args)`: Emulate command line flags for jq, see [the docs](https://jqlang.github.io/jq/manual/#invoking-jq) on invoking jq for details. Invalid flags are silently ignored. + Currently supported flags: + - `"r"`: If the result is a string, it won't be formatted as a JSON string. The equivalent jq flag is `--raw-output`. - `get_env(string)`: Gets the specified enviroment variable - `formattime(unix_timestamp, format_str, timezone)`: Gets the time in a given format from UNIX timestamp. Check [chrono's documentation](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) for more