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.
@@ -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.
@@ -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, or CLI trace flag.
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
 
@@ -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"], paths.key?("validate"), paths.key?("pretty"),
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
@@ -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
- HELP = "#{USAGE}\n" \
10
- "Read one UTF-8 JSON document from --file PATH, or stdin when omitted.\n" \
11
- "Supply one query as EXPRESSION or --query-file PATH, read as UTF-8 without trimming.\n" \
12
- "Options may precede or follow EXPRESSION; -- ends option parsing.\n" \
13
- "Quote spaces: --file 'my document.json'. Use --file=--document.json for option-like names.\n" \
14
- "Paths are literal: --file - reads the file named -.\n" \
15
- "Query paths follow the same rules: --query-file - reads the query file named -.\n" \
16
- "Output is compact JSON by default; --pretty uses two-space indentation and a final newline.\n" \
17
- "Use --raw-output for literal string results; other results remain compact JSON.\n" \
18
- "Raw strings retain all their newlines and receive one additional output newline.\n" \
19
- "Output flags cannot be repeated or combined with each other or --validate.\n" \
20
- "Use --json-lines for one JSON record per physical input line and one compact result per record.\n" \
21
- "JSON Lines compiles once before input; empty input succeeds with a valid query.\n" \
22
- "Blank records are invalid; failures report the input line, retain earlier output, and exit 1.\n" \
23
- "Memory follows the current record and result; output uses normal buffering.\n" \
24
- "The --json-lines flag cannot be repeated or combined with --pretty, --raw-output, or --validate.\n" \
25
- "Function arguments and expression-reference results are validated; failures exit with status 1.\n" \
26
- "Unknown functions suggest similar registered names.\n" \
27
- "Query failures include source locations when available.\n" \
28
- "Use --interactive --file PATH for successive queries against one loaded document.\n" \
29
- "Enter one query per line; blank lines are ignored. :help shows help; :quit or EOF exits successfully.\n" \
30
- "Query errors are reported and the session continues; startup or stream failures exit 1.\n" \
31
- "Results are compact JSON on stdout; prompts and diagnostics use stderr.\n" \
32
- "Prompts appear only when stdin is a terminal; results are flushed after each query.\n" \
33
- "Interactive mode requires --file and rejects query sources, other modes, and information flags.\n" \
34
- "Use --validate to check syntax only, without reading stdin; unknown functions remain valid.\n" \
35
- "Validate saved queries with --validate --query-file PATH; --file is unavailable during validation.\n" \
36
- "The two-argument form --validate EXPRESSION always treats EXPRESSION as query text.\n" \
37
- "Validation exits with status 0 for valid syntax, 1 for query failures, and 2 for usage failures.\n" \
38
- "Use standalone --functions to list built-in signatures and descriptions without reading stdin.\n" \
39
- "The --functions flag cannot be repeated or combined with queries, paths, modes, or information flags.\n" \
40
- "Listing exits with status 0 on success, 1 for output failures, and 2 for usage failures.\n" \
41
- "Manual Bash and Zsh completion: docs/guides/completion.md.\n#{FunctionListing::LEGEND}".freeze
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