Skip to content

Commit 3a9713f

Browse files
committed
Slicing docs + --no-extensions
1 parent 21bcb6f commit 3a9713f

6 files changed

Lines changed: 59 additions & 2 deletions

File tree

docs/manual.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ See the [installation guide](`crate::installation_guide`) for installation instr
3333

3434
## Overview
3535

36+
<details>
37+
<summary>celq command line options</summary>
38+
3639
```none
3740
A CEL command-line query tool for JSON data
3841
@@ -56,12 +59,15 @@ Options:
5659
-r, --raw-output If the output is a JSON string, output it raw without quotes
5760
-S, --sort-keys Output the fields of each object with the keys in sorted order
5861
-f, --from-file <FILE> Read CEL expression from a file
59-
-p, --pretty-print
62+
-p, --pretty-print Output JSON with identation and line breaks for human readability
6063
-g, --greppable Output in a greppable format (gron style)
64+
--no-extensions Disable extensions and use only standard CEL functions
6165
-h, --help Print help
6266
-V, --version Print version
6367
```
6468

69+
</details>
70+
6571
## Quick Start
6672

6773
`celq` reads JSON from the input and lets users process it with CEL:
@@ -137,6 +143,7 @@ This file contains the simplified response from the Yahoo Finance Unofficial JSO
137143
* [Reading Files](#reading-files)
138144
* [Writing Files](#writing-files)
139145
* [Output JSON](#output-json)
146+
* [Slicing lists](#slicing-lists)
140147
* [Reading CEL from a file](#reading-cel-from-a-file)
141148
* [Dealing with NDJSON](#dealing-with-ndjson)
142149
* [Slurping](#slurping)
@@ -197,6 +204,19 @@ Notice that by default `celq` does not guarantee the key order of the output. If
197204
cat yfinance.json | celq --sort-keys '{"symbol": this.chart.result[0].meta.longName, "price": this.chart.result[0].meta.regularMarketPrice}'
198205
```
199206

207+
### Slicing lists
208+
209+
`celq` implements the popular slice extension for CEL. For example:
210+
211+
```bash
212+
echo '["apples", "bananas", "blueberry"]' | celq 'this.slice(1, 3)'
213+
# Outputs: ["bananas", "blueberry"]
214+
```
215+
216+
Slicing follows Python conventions: it is 0-indexed and works with negative indices. The `.slice()` calls always requires two arguments. If you need to slice until the end of the list, do `this.slice(pos, size(this.slice))`. Similarly, do `this.slice(0, pos)` to start from the beginning.
217+
218+
If you want to keep your CEL code portable, pass the `--no-extensions` arguments to disable slicing and all other extensions.
219+
200220
### Reading CEL from a file
201221

202222
In the previous example, the CEL expression for the JSON became long. Let's say we saved the expression in `stock.cel` with the following contents:

src/cli.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,18 @@ pub struct Cli {
150150
#[arg(short = 'f', long = "from-file", value_name = "FILE")]
151151
pub from_file: Option<std::path::PathBuf>,
152152

153+
/// Output JSON with identation and line breaks for human readability
153154
#[arg(short = 'p', long = "pretty-print")]
154155
pub pretty_print: bool,
155156

156157
/// Output in a greppable format (gron style)
157158
#[arg(short = 'g', long = "greppable")]
158159
pub greppable: bool,
159160

161+
/// Disable extensions and use only standard CEL functions
162+
#[arg(long = "no-extensions")]
163+
pub no_extensions: bool,
164+
160165
/// CEL expression to evaluate
161166
#[arg(value_name = "expr")]
162167
pub expression: Option<String>,
@@ -190,4 +195,5 @@ pub struct InputParameters {
190195
pub pretty_print: bool,
191196
pub raw_output: bool,
192197
pub greppable: bool,
198+
pub no_extensions: bool,
193199
}

src/input_handler.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ fn handle_json(
175175
) -> Result<(String, bool)> {
176176
// Create context with default values
177177
let mut context = Context::default();
178-
context.add_function("slice", slice_extension::slice);
178+
179+
if !input_params.no_extensions {
180+
context.add_function("slice", slice_extension::slice);
181+
}
179182

180183
// Add argument variables to context
181184
for (name, value) in arg_variables {

src/input_handler_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn default_params() -> InputParameters {
1515
pretty_print: false,
1616
raw_output: false,
1717
greppable: false,
18+
no_extensions: false,
1819
}
1920
}
2021

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ fn main() -> io::Result<()> {
9191
pretty_print: cli.pretty_print,
9292
raw_output: cli.raw_output,
9393
greppable: cli.greppable,
94+
no_extensions: cli.no_extensions,
9495
};
9596

9697
match handle_input(&program, &arg_variables, &input_params) {

tests/golden.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,3 +917,29 @@ fn test_boolean_false_exit_code() -> io::Result<()> {
917917

918918
Ok(())
919919
}
920+
921+
#[test]
922+
fn test_slice_fails_without_extensions() -> io::Result<()> {
923+
let mut child = process::Command::new(env!("CARGO_BIN_EXE_celq"))
924+
.args(["--no-extensions", "this.items.slice(0, 2)"])
925+
.stdin(process::Stdio::piped())
926+
.stdout(process::Stdio::piped())
927+
.stderr(process::Stdio::piped())
928+
.spawn()?;
929+
930+
// Write test input
931+
if let Some(mut stdin) = child.stdin.take() {
932+
use std::io::Write;
933+
stdin.write_all(br#"{"items":[1,2,3,4,5]}"#)?;
934+
}
935+
936+
let output = child.wait_with_output()?;
937+
938+
// Check that the command failed (non-zero exit code)
939+
assert!(
940+
!output.status.success(),
941+
"Expected command to fail without extensions, but it succeeded"
942+
);
943+
944+
Ok(())
945+
}

0 commit comments

Comments
 (0)