jmespath-ruby 0.1.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -1
- data/README.md +76 -214
- data/completions/_jmespath +9 -0
- data/completions/jmespath.bash +13 -0
- data/docs/guides/command-line.md +233 -0
- data/docs/guides/compilation.md +2 -1
- data/docs/guides/completion.md +5 -0
- data/lib/jmespath/cli/arguments.rb +5 -8
- data/lib/jmespath/cli/help.rb +115 -33
- data/lib/jmespath/cli/inspection_report.rb +76 -0
- data/lib/jmespath/cli/interactive_commands.rb +58 -0
- data/lib/jmespath/cli/interactive_completion.rb +93 -0
- data/lib/jmespath/cli/interactive_editor.rb +129 -0
- data/lib/jmespath/cli/interactive_editor_state.rb +52 -0
- data/lib/jmespath/cli/interactive_help.rb +36 -0
- data/lib/jmespath/cli/interactive_key_stroke.rb +80 -0
- data/lib/jmespath/cli/interactive_line_editor.rb +11 -0
- data/lib/jmespath/cli/interactive_session.rb +35 -9
- data/lib/jmespath/cli.rb +22 -11
- data/lib/jmespath/version.rb +1 -1
- metadata +24 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Command-line reference
|
|
2
|
+
|
|
3
|
+
## Quick start
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Both `jmespath` and `jmespath.rb` accept an expression and a JSON document from stdin or `--file PATH`.
|
|
7
|
+
Use `--query-file PATH` to read the expression from a UTF-8 file.
|
|
8
|
+
See [shell completion](completion.md) for manual Bash and Zsh setup for both executable names.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
printf '%s\n' '{"user":{"name":"Ada"}}' | jmespath 'user.name'
|
|
12
|
+
jmespath --file document.json 'user.name'
|
|
13
|
+
jmespath 'user.name' --file document.json
|
|
14
|
+
jmespath --file 'my document.json' 'user.name'
|
|
15
|
+
jmespath --file=--document.json 'user.name'
|
|
16
|
+
jmespath --file ./--document.json 'user.name'
|
|
17
|
+
jmespath --file - 'user.name'
|
|
18
|
+
jmespath --file document.json -- 'user.name'
|
|
19
|
+
jmespath --query-file query.jmespath < document.json
|
|
20
|
+
jmespath --query-file query.jmespath --file document.json
|
|
21
|
+
jmespath --pretty 'items' < document.json
|
|
22
|
+
jmespath --raw-output 'user.name' < document.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Input
|
|
26
|
+
|
|
27
|
+
Document files are read as UTF-8 and closed after parsing, before query evaluation in single-document mode.
|
|
28
|
+
File input leaves stdin unused; omitting `--file` selects stdin.
|
|
29
|
+
Paths are literal: `--file -` reads a file named `-`.
|
|
30
|
+
Quote filenames with spaces; use the equals form or `./` prefix for option-like filenames.
|
|
31
|
+
Options can precede or follow the expression; `--` ends option parsing.
|
|
32
|
+
Supply exactly one query source: a positional expression or `--query-file PATH`.
|
|
33
|
+
Repeated file options, unknown options, missing values, and conflicting or absent query sources are usage failures.
|
|
34
|
+
Version flags must appear alone; help accepts one optional topic, as described under [Help](#help).
|
|
35
|
+
|
|
36
|
+
Query files retain all source text, including trailing newlines and Unicode diagnostic coordinates.
|
|
37
|
+
An empty query file produces a syntax failure.
|
|
38
|
+
Query files close after reading, before document input is accessed.
|
|
39
|
+
Missing or unreadable query files leave document input untouched.
|
|
40
|
+
In single-document mode, execution parses JSON before compiling the expression.
|
|
41
|
+
Query paths follow the document path rules: `--query-file -` reads a literal file named `-`.
|
|
42
|
+
|
|
43
|
+
## Output
|
|
44
|
+
|
|
45
|
+
The result goes to standard output as compact JSON followed by a newline.
|
|
46
|
+
Use `--pretty` for JSON with two-space indentation and one final newline.
|
|
47
|
+
Both formats support objects, arrays, strings, numbers, booleans, and null, with Unicode preserved.
|
|
48
|
+
Pretty output works with positional or file queries and either document source.
|
|
49
|
+
Repeated `--pretty` options are usage failures.
|
|
50
|
+
Pretty formatting preserves stderr diagnostics, source coordinates, and exit statuses; failures before output starts leave stdout empty.
|
|
51
|
+
Use `--raw-output` to print string results without JSON quotes or escape encoding.
|
|
52
|
+
Other result types retain compact JSON encoding.
|
|
53
|
+
Raw output appends one newline and preserves every embedded and trailing newline in the result.
|
|
54
|
+
An empty string produces one newline; a string ending with one newline produces two.
|
|
55
|
+
Repeated `--raw-output` options and combinations with `--pretty` are usage failures.
|
|
56
|
+
Raw output preserves stderr diagnostics, source coordinates, and exit statuses; failures before output starts leave stdout empty.
|
|
57
|
+
Syntax, function type, and arity failures include source coordinates, a query excerpt, and a caret when available.
|
|
58
|
+
Unknown functions suggest similar registered names.
|
|
59
|
+
Malformed JSON input reports `Invalid JSON input` without including document fragments.
|
|
60
|
+
These failures go to standard error and exit with status 1.
|
|
61
|
+
Usage failures exit with status 2; `--help` and `--version` exit with status 0 without reading input.
|
|
62
|
+
|
|
63
|
+
### Record processing
|
|
64
|
+
|
|
65
|
+
Process independent JSON records with these shell commands:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
jmespath --json-lines 'user.name' < events.jsonl
|
|
69
|
+
jmespath --json-lines --file events.jsonl --query-file query.jmespath
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
JSON Lines mode compiles the query once before opening document input or reading stdin.
|
|
73
|
+
Each physical line must contain one complete JSON value; blank and whitespace-only lines fail.
|
|
74
|
+
LF, CRLF, and a final record without a newline are accepted.
|
|
75
|
+
Each result is compact JSON followed by a newline, including `null` and `false`.
|
|
76
|
+
Empty input succeeds without output when the query is valid; an invalid query still fails.
|
|
77
|
+
|
|
78
|
+
Processing stops at the first failure with status 1 and an `Input line N:` diagnostic prefix.
|
|
79
|
+
Earlier results remain on stdout; check the exit status before treating output as complete.
|
|
80
|
+
Query compilation and file-open errors have no input line prefix.
|
|
81
|
+
Memory is proportional to the current record, its evaluation, and its serialized result, plus the compiled query.
|
|
82
|
+
Output uses normal buffering.
|
|
83
|
+
Owned files close after processing; caller-owned streams remain open.
|
|
84
|
+
Injected `CLI.run` input needs `gets` for JSON Lines or `read` for document mode; output needs only `puts`.
|
|
85
|
+
Repeated `--json-lines` flags and combinations with `--pretty`, `--raw-output`, or validation are usage failures.
|
|
86
|
+
|
|
87
|
+
## Validation
|
|
88
|
+
|
|
89
|
+
Validate syntax without providing a JSON document by running this shell command:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
jmespath --validate 'items[*].name'
|
|
93
|
+
jmespath --validate --query-file query.jmespath
|
|
94
|
+
jmespath --query-file query.jmespath --validate
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Both executables support `--validate EXPRESSION` without reading standard input.
|
|
98
|
+
File validation also leaves stdin untouched and rejects document options such as `--file`.
|
|
99
|
+
Combining `--pretty` with validation is a usage failure, rejected before opening input files or reading stdin.
|
|
100
|
+
Combining `--raw-output` with validation follows the same rule.
|
|
101
|
+
The exact two-argument form `--validate EXPRESSION` always treats the second argument as query text, including flag-like text.
|
|
102
|
+
Thus `jmespath --validate --pretty` validates the literal expression `--pretty`.
|
|
103
|
+
Likewise, `jmespath --validate --raw-output` validates the literal expression `--raw-output`.
|
|
104
|
+
Valid syntax produces no output and exits with status 0.
|
|
105
|
+
Query failures print a diagnostic to standard error and exit with status 1; incorrect usage exits with status 2.
|
|
106
|
+
Validation does not check function availability or runtime argument types.
|
|
107
|
+
The Ruby interface is `JMESPath.validate(expression, max_depth: 128)`.
|
|
108
|
+
See [CLI diagnostics](errors.md#cli-diagnostics) for examples and fallback behavior.
|
|
109
|
+
|
|
110
|
+
## Functions
|
|
111
|
+
|
|
112
|
+
List built-in functions from the shell without reading stdin:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
jmespath --functions
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Each line shows a name, accepted argument types, and the description from public function metadata.
|
|
119
|
+
The listing is alphabetical and ends with a notation legend.
|
|
120
|
+
Unions use `|`, homogeneous arrays use `array<T>`, and expression references use `expref`.
|
|
121
|
+
A final `...` means the last argument repeats and remains required.
|
|
122
|
+
Runtime checks can be narrower than the declared types.
|
|
123
|
+
See [the function listing](functions.md#command-line-reference) for examples.
|
|
124
|
+
|
|
125
|
+
Use `--functions` alone; queries, paths, other modes, repeated flags, and values are usage failures.
|
|
126
|
+
The two-argument form `--validate --functions` retains literal query validation.
|
|
127
|
+
Listing exits with status 0 on success, 1 for output failures, or 2 for usage failures.
|
|
128
|
+
Usage failures leave stdout empty and print usage guidance on stderr.
|
|
129
|
+
|
|
130
|
+
## Interactive
|
|
131
|
+
|
|
132
|
+
Start a session against one loaded document:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
jmespath --interactive --file document.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Enter one UTF-8 query per line; empty lines and lines containing only spaces or tabs are ignored.
|
|
139
|
+
Use `:help` for session instructions and `:quit` to finish, or end input with EOF.
|
|
140
|
+
Queries accept LF, CRLF, and a final line without a newline.
|
|
141
|
+
Each successful query prints compact JSON and a final newline to stdout, then flushes when the stream supports it.
|
|
142
|
+
Prompts appear only when stdin is a terminal; prompts, help, and diagnostics use stderr.
|
|
143
|
+
The document loads once and its file closes before the first prompt or command read.
|
|
144
|
+
|
|
145
|
+
Queries can also come from a pipe without prompts:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
printf 'user.name\nlength(user.name)\n' | jmespath --interactive --file document.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Query errors, unknown commands, and invalid UTF-8 query lines produce diagnostics and allow the session to continue.
|
|
152
|
+
Failed queries produce no JSON result; successful results remain available.
|
|
153
|
+
EOF and `:quit` return status 0 even after query failures.
|
|
154
|
+
Document and stream failures terminate with status 1; usage failures return status 2.
|
|
155
|
+
Interactive mode requires `--file PATH` and rejects positional queries, `--query-file`, other modes, information flags, and repeated flags.
|
|
156
|
+
Readers supplied to `CLI.run` need `gets`; `tty?` is optional and absent detection means no prompts.
|
|
157
|
+
Writers need `puts`; `flush` is optional except for terminal prompts, whose diagnostic stream needs both `print` and `flush`.
|
|
158
|
+
Caller-owned streams remain open.
|
|
159
|
+
|
|
160
|
+
On supported terminals, Reline provides single-line editing and previous-query recall.
|
|
161
|
+
Editing requires terminal IO on stdin and stderr and a usable `TERM`; redirected stdout remains available for JSON results.
|
|
162
|
+
Keep up to 500 submitted queries in memory for the session, including failed queries and duplicates.
|
|
163
|
+
Commands, blanks, and invalid UTF-8 input are excluded; the oldest query is discarded when the limit is reached.
|
|
164
|
+
History starts empty for each session and creates no files or shell configuration.
|
|
165
|
+
Ctrl-C discards the current edit without evaluating partial input.
|
|
166
|
+
Pasted physical lines are submitted as separate queries after confirmation.
|
|
167
|
+
Pipes, injected readers, redirected stderr, and unsupported terminals use the plain line reader without loading Reline.
|
|
168
|
+
The plain reader retains its interruption behavior.
|
|
169
|
+
Tab completes built-in function names at the cursor, replacing the complete name token while preserving surrounding Unicode text.
|
|
170
|
+
Completion inserts no parentheses or spaces and does not inspect the document or evaluate queries.
|
|
171
|
+
Quoted identifiers, single-quoted strings, and backtick literals remain unchanged.
|
|
172
|
+
A unique match inserts the name; multiple matches extend their common prefix when possible.
|
|
173
|
+
When no extension is possible, sorted alternatives appear on stderr and the query remains unchanged.
|
|
174
|
+
Empty and unknown prefixes remain unchanged without diagnostics.
|
|
175
|
+
Multiline editing and document reloads are unavailable.
|
|
176
|
+
Terminal editing is tested on macOS through a pseudo terminal.
|
|
177
|
+
CI includes Linux checks on Ruby 3.3, 3.4, and 4.0 and a macOS terminal check; Windows terminal behavior is unverified.
|
|
178
|
+
|
|
179
|
+
Use `:functions` to list built-in names, signatures, descriptions, and the type legend on stderr.
|
|
180
|
+
Use `:explain QUERY` to inspect an operation tree without evaluating the document.
|
|
181
|
+
Each tree node shows its public operation ID, role, details, source line and character column, and byte range.
|
|
182
|
+
Byte ranges have exclusive ends; missing source locations are labeled explicitly.
|
|
183
|
+
The query is the complete remainder after the command separator, preserving trailing whitespace.
|
|
184
|
+
Explanations show unknown function names without checking their availability.
|
|
185
|
+
Both commands work with terminal or piped input and leave stdout empty.
|
|
186
|
+
Command errors produce diagnostics and leave the session usable.
|
|
187
|
+
Tab completes function names within explanation arguments on supported terminals.
|
|
188
|
+
For structured inspection, use the public [explanation API](compilation.md#query-inspection).
|
|
189
|
+
|
|
190
|
+
Use `:trace QUERY` to evaluate once and inspect null fields, filter exclusions, and omitted projection results.
|
|
191
|
+
The readable report uses stderr; the successful value is written once as compact JSON on stdout, including null and false.
|
|
192
|
+
Events retain public operation IDs, source locations, reasons, and zero-based item context.
|
|
193
|
+
Traces retain at most 1,000 events; a truncation notice states that evaluation completed.
|
|
194
|
+
An empty report prints `Trace: no diagnostic events.`
|
|
195
|
+
Serialization failures produce diagnostics without a report or result; the session remains usable.
|
|
196
|
+
Stream failures terminate the session with status 1.
|
|
197
|
+
Tab completes function names in trace arguments on supported terminals.
|
|
198
|
+
Reports use private text layouts; use the [tracing API](compilation.md#evaluation-tracing) for structured data.
|
|
199
|
+
|
|
200
|
+
## Help
|
|
201
|
+
|
|
202
|
+
Run `jmespath --help` or `jmespath -h` for usage, options, common examples, and topic commands.
|
|
203
|
+
Read detailed rules without accessing stdin or opening document or query files:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
jmespath --help input
|
|
207
|
+
jmespath --help output
|
|
208
|
+
jmespath --help validate
|
|
209
|
+
jmespath --help functions
|
|
210
|
+
jmespath --help interactive
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Both executable names accept `-h TOPIC` as an alias.
|
|
214
|
+
Help must be the first argument, with zero or one exact, case-sensitive topic name.
|
|
215
|
+
Abbreviations, equals forms, unknown topics, extra arguments, and combinations with execution flags are usage failures.
|
|
216
|
+
Invalid help requests leave stdout empty and print usage guidance to stderr without accessing any input.
|
|
217
|
+
Valid help writes to stdout with status 0; output failures return 1 and usage failures return 2.
|
|
218
|
+
The exact two-argument form `jmespath --validate --help` still validates the literal query `--help`.
|
|
219
|
+
|
|
220
|
+
Run `jmespath --version` for the gem version.
|
|
221
|
+
The version flag must appear alone and leaves document and query input untouched.
|
|
222
|
+
|
|
223
|
+
## Exit statuses
|
|
224
|
+
|
|
225
|
+
| Status | Meaning |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| 0 | Successful execution, validation, information output, or normal session termination |
|
|
228
|
+
| 1 | Query, document, file, or stream failure |
|
|
229
|
+
| 2 | Invalid command-line usage |
|
|
230
|
+
|
|
231
|
+
Usage failures leave stdout empty and write usage guidance to stderr before accessing input.
|
|
232
|
+
Function arguments and expression-reference results are validated; failures exit with status 1.
|
|
233
|
+
See [CLI diagnostics](errors.md#cli-diagnostics) for source locations and fallback messages.
|
data/docs/guides/compilation.md
CHANGED
|
@@ -321,7 +321,8 @@ Trace state is separate for each call, including concurrent calls and nested cal
|
|
|
321
321
|
Ordinary search builds no trace collector or operation map.
|
|
322
322
|
Tracing propagates original evaluation and input exceptions without attaching a report or retrying evaluation.
|
|
323
323
|
Suppression retains its ordinary behavior; a suppressed nil result can produce a downstream omission.
|
|
324
|
-
There is no dedicated validation-failure event, automatic logging, module-level trace method
|
|
324
|
+
There is no dedicated validation-failure event, automatic logging, or module-level trace method.
|
|
325
|
+
Interactive sessions expose tracing through `:trace QUERY`; see the [command-line guide](command-line.md#interactive).
|
|
325
326
|
|
|
326
327
|
## Saved options
|
|
327
328
|
|
data/docs/guides/completion.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The gem includes Bash and Zsh completion definitions for `jmespath` and `jmespath.rb`.
|
|
4
4
|
They suggest option names and filesystem paths after `--file` and `--query-file`.
|
|
5
|
+
They also suggest `input`, `output`, `validate`, `functions`, and `interactive` after an initial `--help` or `-h`.
|
|
5
6
|
Both `--file PATH` and `--file=PATH` forms support completion, including spaces in filenames.
|
|
6
7
|
|
|
7
8
|
## Bash setup
|
|
@@ -54,6 +55,8 @@ These examples show the key as `<Tab>`:
|
|
|
54
55
|
jmespath --que<Tab>
|
|
55
56
|
jmespath --query-file queries/rep<Tab>
|
|
56
57
|
jmespath.rb --file=my<Tab>
|
|
58
|
+
jmespath --help val<Tab>
|
|
59
|
+
jmespath.rb -h int<Tab>
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
Directories remain available for further path completion, without filtering by file extension.
|
|
@@ -63,6 +66,8 @@ Hyphen-leading filenames follow the CLI's literal-path rules, such as `--file=--
|
|
|
63
66
|
Options remain available before or after an expression until a standalone `--` delimiter.
|
|
64
67
|
Repeated or conflicting options remain visible; the CLI validates combinations when you run the command.
|
|
65
68
|
Completion does not suggest JMESPath expressions or complete arguments after the delimiter.
|
|
69
|
+
Help topics are offered only as the first argument after an initial help flag, with no suggestions after the topic.
|
|
70
|
+
The literal-validation form `--validate --help` does not offer help topics.
|
|
66
71
|
It lists filesystem names without running the CLI, evaluating queries, or reading document or query contents.
|
|
67
72
|
Ruby runs only during the installation-path lookup shown above.
|
|
68
73
|
|
|
@@ -12,16 +12,13 @@ module JMESPath
|
|
|
12
12
|
|
|
13
13
|
# @api private
|
|
14
14
|
def self.parse_arguments(argv)
|
|
15
|
-
if argv.one? && !argv.fetch(0).start_with?("-")
|
|
16
|
-
return [argv.fetch(0), nil, nil, false, false, false, false, false]
|
|
17
|
-
end
|
|
15
|
+
return [argv.fetch(0), nil, nil, nil] if argv.one? && !argv.fetch(0).start_with?("-")
|
|
18
16
|
|
|
19
17
|
paths = {} # : Hash[String, String]
|
|
20
18
|
arguments = argument_parser(paths).permute(argv)
|
|
21
|
-
check_sources(arguments, paths)
|
|
19
|
+
mode = check_sources(arguments, paths)
|
|
22
20
|
|
|
23
|
-
[arguments.first, paths["file"], paths["query-file"],
|
|
24
|
-
paths.key?("raw-output"), paths.key?("json-lines"), paths.key?("interactive")]
|
|
21
|
+
[arguments.first, paths["file"], paths["query-file"], mode]
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
# @api private
|
|
@@ -41,8 +38,8 @@ module JMESPath
|
|
|
41
38
|
def self.check_modes(paths)
|
|
42
39
|
modes = paths.keys & %w[validate pretty raw-output json-lines interactive]
|
|
43
40
|
raise OptionParser::InvalidArgument, "conflicting modes" if modes.length > 1
|
|
44
|
-
return unless paths.key?("validate")
|
|
45
|
-
return if paths.key?("query-file") && !paths.key?("file")
|
|
41
|
+
return modes.first unless paths.key?("validate")
|
|
42
|
+
return modes.first if paths.key?("query-file") && !paths.key?("file")
|
|
46
43
|
|
|
47
44
|
raise OptionParser::InvalidArgument, "validation requires a query file"
|
|
48
45
|
end
|
data/lib/jmespath/cli/help.rb
CHANGED
|
@@ -1,43 +1,125 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "interactive_help"
|
|
4
|
+
|
|
3
5
|
module JMESPath
|
|
4
6
|
module CLI
|
|
5
7
|
USAGE = "Usage: jmespath [--pretty | --raw-output | --json-lines] [--file PATH] " \
|
|
6
8
|
"(EXPRESSION | --query-file PATH)\n " \
|
|
7
9
|
"jmespath --validate (EXPRESSION | --query-file PATH)\n jmespath --functions\n " \
|
|
8
10
|
"jmespath --interactive --file PATH"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
11
|
+
|
|
12
|
+
# @api private
|
|
13
|
+
module Help
|
|
14
|
+
GUIDE = "Reference: docs/guides/command-line.md (included in the gem).\n" \
|
|
15
|
+
"https://github.com/svyatov/jmespath-ruby/blob/main/docs/guides/command-line.md\n"
|
|
16
|
+
TOPICS = {
|
|
17
|
+
"input" => <<~TEXT,
|
|
18
|
+
Input: jmespath --file document.json 'user.name'
|
|
19
|
+
jmespath --query-file query.jmespath < document.json
|
|
20
|
+
Read one UTF-8 JSON document from --file PATH, or stdin when omitted.
|
|
21
|
+
Supply exactly one query: EXPRESSION or --query-file PATH, read as UTF-8 without trimming.
|
|
22
|
+
Query text retains newlines and Unicode coordinates; an empty query file is a syntax failure.
|
|
23
|
+
Query files close before document input; missing or unreadable queries leave documents untouched.
|
|
24
|
+
In single-document mode, JSON is parsed before query compilation.
|
|
25
|
+
Document files close after parsing, before evaluation. Caller-owned streams remain open.
|
|
26
|
+
Options may precede or follow EXPRESSION; -- ends option parsing. Option names match exactly.
|
|
27
|
+
Quote spaces: --file 'my document.json'. Use --file=--document.json or ./--document.json.
|
|
28
|
+
Paths are literal: --file - and --query-file - read files named -.
|
|
29
|
+
Repeated options, unknown options, missing values, and conflicting or absent queries fail before input.
|
|
30
|
+
CLI.run document input needs read; line modes need gets; output needs puts.
|
|
31
|
+
Query failures include source locations when available. Unknown functions suggest similar registered names.
|
|
32
|
+
Malformed JSON reports Invalid JSON input without document fragments.
|
|
33
|
+
Results use stdout; diagnostics use stderr. Success returns status 0, input/query/stream failures 1,
|
|
34
|
+
and usage failures 2 with empty stdout. Information modes never access document or query input.
|
|
35
|
+
Version and function listing flags must stand alone; help accepts only one optional topic.
|
|
36
|
+
TEXT
|
|
37
|
+
"output" => <<~TEXT,
|
|
38
|
+
Output: jmespath --pretty 'items' < document.json
|
|
39
|
+
jmespath --raw-output 'user.name' < document.json
|
|
40
|
+
jmespath --json-lines --file events.jsonl --query-file query.jmespath
|
|
41
|
+
Output is compact JSON by default; --pretty uses two-space indentation and a final newline.
|
|
42
|
+
Both preserve Unicode and support objects, arrays, strings, numbers, booleans, and null.
|
|
43
|
+
Pretty output supports either query and document source.
|
|
44
|
+
--raw-output prints strings literally; other results remain compact JSON.
|
|
45
|
+
Raw strings retain all embedded and trailing newlines and receive one additional output newline.
|
|
46
|
+
An empty string prints one newline; a string ending in one newline prints two.
|
|
47
|
+
Output flags cannot be repeated or combined with each other or --validate.
|
|
48
|
+
JSON Lines compiles once before opening document input; valid empty input succeeds without output.
|
|
49
|
+
Each physical line must contain one JSON value; blank and whitespace-only records are invalid.
|
|
50
|
+
LF, CRLF, and a final record without a newline are accepted.
|
|
51
|
+
Each result is compact JSON plus a newline, including null and false.
|
|
52
|
+
Processing stops on failure; earlier stdout remains. Check status before treating output as complete.
|
|
53
|
+
Record failures use Input line N: on stderr; compilation and file-open errors have no line prefix.
|
|
54
|
+
Memory follows the current record, evaluation, and result plus the compiled query.
|
|
55
|
+
Output uses normal buffering. Owned files close; caller-owned streams remain open.
|
|
56
|
+
CLI.run line input needs gets and output needs puts.
|
|
57
|
+
Formatting preserves diagnostics and source coordinates; failures before output leave stdout empty.
|
|
58
|
+
Results use stdout; diagnostics use stderr. Success returns status 0, query/input/output failures 1,
|
|
59
|
+
and usage failures 2. See jmespath --help input for query and path rules.
|
|
60
|
+
TEXT
|
|
61
|
+
"validate" => <<~TEXT,
|
|
62
|
+
Validation: jmespath --validate EXPRESSION
|
|
63
|
+
jmespath --validate 'items[*].name'
|
|
64
|
+
jmespath --validate --query-file query.jmespath
|
|
65
|
+
jmespath --query-file query.jmespath --validate
|
|
66
|
+
Check syntax only, without reading stdin. Unknown functions and invalid runtime types remain valid syntax.
|
|
67
|
+
Query files follow input topic rules. --file, output flags, and interactive mode are unavailable.
|
|
68
|
+
Invalid combinations fail before opening files or reading stdin.
|
|
69
|
+
The exact two-argument form --validate EXPRESSION always treats EXPRESSION as query text.
|
|
70
|
+
Thus --validate --help, --validate -h, --validate --pretty, and --validate --raw-output validate literals.
|
|
71
|
+
Validation exits with status 0 for valid syntax, 1 for query failures, and 2 for usage failures.
|
|
72
|
+
Valid syntax produces no stdout or stderr; failures leave stdout empty and print diagnostics to stderr.
|
|
73
|
+
Query failures include source locations when available.
|
|
74
|
+
Ruby callers can use JMESPath.validate(expression, max_depth: 128).
|
|
75
|
+
TEXT
|
|
76
|
+
"functions" => <<~TEXT + FunctionListing::LEGEND,
|
|
77
|
+
Functions: jmespath --functions
|
|
78
|
+
Use standalone --functions to list built-in signatures and descriptions without reading stdin.
|
|
79
|
+
Names are alphabetical and use public function metadata; the listing ends with a notation legend.
|
|
80
|
+
--functions cannot be repeated or combined with queries, paths, modes, information flags, or values.
|
|
81
|
+
The two-argument form --validate --functions validates the literal query.
|
|
82
|
+
Function arguments and expression-reference results are validated; failures exit with status 1.
|
|
83
|
+
Unknown functions suggest similar registered names. Query failures include source locations when available.
|
|
84
|
+
Listing writes to stdout with empty stderr and returns status 0; output failures return 1.
|
|
85
|
+
Usage failures return 2, leave stdout empty, and print usage guidance to stderr before accessing input.
|
|
86
|
+
TEXT
|
|
87
|
+
"interactive" => InteractiveHelp::TEXT
|
|
88
|
+
}.freeze
|
|
89
|
+
|
|
90
|
+
def self.render(topic)
|
|
91
|
+
return HELP unless topic
|
|
92
|
+
|
|
93
|
+
TOPICS.fetch(topic) { raise OptionParser::InvalidArgument, "unknown help topic" } + GUIDE
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
HELP = <<~TEXT.freeze
|
|
98
|
+
#{USAGE}
|
|
99
|
+
jmespath --help [TOPIC]
|
|
100
|
+
Options:
|
|
101
|
+
--file PATH Read a JSON document (default: stdin)
|
|
102
|
+
--query-file PATH Read the UTF-8 query instead of EXPRESSION
|
|
103
|
+
--pretty Print indented JSON
|
|
104
|
+
--raw-output Print string results literally
|
|
105
|
+
--json-lines Search each JSON input line
|
|
106
|
+
--validate Check query syntax without a document
|
|
107
|
+
--interactive Run successive queries against --file PATH
|
|
108
|
+
--functions List built-in functions
|
|
109
|
+
--version Print the gem version
|
|
110
|
+
-h, --help [TOPIC] Show help
|
|
111
|
+
Examples:
|
|
112
|
+
jmespath --file document.json 'user.name'
|
|
113
|
+
jmespath --pretty 'items' < document.json
|
|
114
|
+
jmespath --validate 'items[*].name'
|
|
115
|
+
Topic help (also accepts -h TOPIC):
|
|
116
|
+
jmespath --help input
|
|
117
|
+
jmespath --help output
|
|
118
|
+
jmespath --help validate
|
|
119
|
+
jmespath --help functions
|
|
120
|
+
jmespath --help interactive
|
|
121
|
+
Manual Bash and Zsh completion: docs/guides/completion.md.
|
|
122
|
+
#{Help::GUIDE}
|
|
123
|
+
TEXT
|
|
42
124
|
end
|
|
43
125
|
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JMESPath
|
|
4
|
+
module CLI
|
|
5
|
+
# @api private
|
|
6
|
+
class InspectionReport
|
|
7
|
+
def self.explain(explanation)
|
|
8
|
+
new(explanation).explain
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.trace(explanation, report, limit:)
|
|
12
|
+
new(explanation).trace(report, limit: limit)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(explanation)
|
|
16
|
+
@source = explanation[:source]
|
|
17
|
+
@root = explanation[:root]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def explain
|
|
21
|
+
tree(@root).join("\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def trace(report, limit:)
|
|
25
|
+
locations = {} # : Hash[Integer, String]
|
|
26
|
+
index_locations(@root, locations)
|
|
27
|
+
lines = report[:events].map { |event| event_line(event, locations) }
|
|
28
|
+
lines << "Trace: no diagnostic events." if lines.empty?
|
|
29
|
+
lines << "Trace truncated: showing first #{limit} events; evaluation completed." if report[:truncated]
|
|
30
|
+
lines.join("\n")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def index_locations(node, locations)
|
|
36
|
+
locations[node[:id]] = location(node[:source_range])
|
|
37
|
+
node[:children].each { |child| index_locations(child[:node], locations) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def event_line(event, locations)
|
|
41
|
+
[event[:type], "##{event[:operation_id]}", locations.fetch(event[:operation_id]),
|
|
42
|
+
details(event[:details]), context(event[:context])].reject(&:empty?).join(" ")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def context(frames)
|
|
46
|
+
return "" if frames.empty?
|
|
47
|
+
|
|
48
|
+
entries = frames.map { |frame| "##{frame[:operation_id]} index=#{frame[:index]}" }
|
|
49
|
+
"context=[#{entries.join(', ')}]"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tree(node, indent = "", role = nil)
|
|
53
|
+
line = indent + [role, label(node)].compact.join(": ")
|
|
54
|
+
[line] + node[:children].flat_map { |child| tree(child[:node], "#{indent} ", child[:role]) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def label(node)
|
|
58
|
+
["##{node[:id]}", node[:operation], location(node[:source_range]), details(node[:details])]
|
|
59
|
+
.reject(&:empty?).join(" ")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def location(range)
|
|
63
|
+
return "location unavailable" unless range
|
|
64
|
+
|
|
65
|
+
prefix = @source.byteslice(0, range[0]).to_s
|
|
66
|
+
line = prefix.count("\n") + 1
|
|
67
|
+
column = prefix.rpartition("\n").last.length + 1
|
|
68
|
+
"line #{line}, column #{column} (bytes #{range[0]}...#{range[1]})"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def details(values)
|
|
72
|
+
values.map { |key, value| "#{key}=#{value.inspect}" }.join(" ")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "inspection_report"
|
|
4
|
+
|
|
5
|
+
module JMESPath
|
|
6
|
+
module CLI
|
|
7
|
+
# @api private
|
|
8
|
+
module InteractiveCommands
|
|
9
|
+
TRACE_LIMIT = 1000
|
|
10
|
+
|
|
11
|
+
def self.call(line, data:)
|
|
12
|
+
parts = line.sub(/\A[ \t]+/, "").split(/[ \t]+/, 2)
|
|
13
|
+
query = parts.fetch(1, "")
|
|
14
|
+
execute(parts.fetch(0), query, line, data)
|
|
15
|
+
rescue JSON::NestingError
|
|
16
|
+
["Invalid JSON result", nil]
|
|
17
|
+
rescue Errors::Error, JSON::GeneratorError => e
|
|
18
|
+
[FailureDiagnostic.format(e, query), nil]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.execute(name, query, line, data)
|
|
22
|
+
case name
|
|
23
|
+
when ":functions" then [functions(query), nil]
|
|
24
|
+
when ":explain" then [explain(query), nil]
|
|
25
|
+
when ":trace" then trace(query, data)
|
|
26
|
+
else
|
|
27
|
+
command = line.sub(/\A[ \t]+/, "").reverse.sub(/\A[ \t]+/, "").reverse
|
|
28
|
+
raise Errors::Error, "Unknown command: #{command}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.functions(query)
|
|
33
|
+
raise Errors::Error, ":functions does not accept arguments" unless query.empty?
|
|
34
|
+
|
|
35
|
+
FunctionListing.render(Functions.default)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.explain(query)
|
|
39
|
+
InspectionReport.explain(compile(query, ":explain").explain)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.compile(query, command)
|
|
43
|
+
raise Errors::Error, "#{command} requires a query" if query.empty?
|
|
44
|
+
|
|
45
|
+
JMESPath.compile(query)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.trace(query, data)
|
|
49
|
+
expression = compile(query, ":trace")
|
|
50
|
+
report = expression.trace(data, limit: TRACE_LIMIT)
|
|
51
|
+
result = JSON.generate(report[:value])
|
|
52
|
+
[InspectionReport.trace(expression.explain, report, limit: TRACE_LIMIT), result]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private_class_method :execute, :functions, :explain, :compile, :trace
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|