1pub mod cli;
2pub mod detector;
3pub mod error;
4pub mod formats;
5pub mod output;
6pub mod selector;
7pub mod streaming;
8
9use cli::{Cli, InputFormat};
10use error::PickError;
11use selector::{Expression, execute};
12use serde_json::Value;
13
14pub fn run(cli: &Cli, input: &str) -> Result<String, PickError> {
15 if input.trim().is_empty() {
16 return Err(PickError::NoInput);
17 }
18
19 let selector_str = cli.selector.as_deref().unwrap_or("");
20 let expression = Expression::parse(selector_str)?;
21
22 let format = match cli.input {
24 InputFormat::Auto => detector::detect_format(input),
25 ref f => f.clone(),
26 };
27
28 let results = match parse_and_execute(input, &format, &expression, selector_str) {
30 Ok(r) => r,
31 Err(e) => {
32 if let Some(ref default) = cli.default {
33 return Ok(default.clone());
34 }
35 return Err(e);
36 }
37 };
38
39 if results.is_empty() {
41 if let Some(ref default) = cli.default {
42 return Ok(default.clone());
43 }
44 return Err(PickError::KeyNotFound(selector_str.to_string()));
45 }
46
47 if cli.exists {
49 return Ok(String::new());
50 }
51
52 if cli.count {
54 return Ok(results.len().to_string());
55 }
56
57 let results = if cli.first {
59 vec![results.into_iter().next().unwrap()]
60 } else {
61 results
62 };
63
64 Ok(output::format_output(
65 &results,
66 cli.json,
67 cli.lines,
68 &cli.output,
69 ))
70}
71
72fn parse_and_execute(
73 input: &str,
74 format: &InputFormat,
75 expression: &Expression,
76 selector_str: &str,
77) -> Result<Vec<Value>, PickError> {
78 if *format == InputFormat::Text {
80 return parse_and_extract_text(input, expression, selector_str);
81 }
82
83 let value = parse_input(input, format)?;
84 execute(&value, expression)
85}
86
87fn parse_and_extract_text(
88 input: &str,
89 expression: &Expression,
90 selector_str: &str,
91) -> Result<Vec<Value>, PickError> {
92 let value = formats::text::parse(input)?;
93
94 if let Ok(results) = execute(&value, expression)
96 && !results.is_empty()
97 {
98 return Ok(results);
99 }
100
101 if !selector_str.is_empty()
103 && let Some(found) = formats::text::search_text(input, selector_str)
104 {
105 return Ok(vec![found]);
106 }
107
108 Err(PickError::KeyNotFound(selector_str.to_string()))
109}
110
111fn parse_input(input: &str, format: &InputFormat) -> Result<Value, PickError> {
112 match format {
113 InputFormat::Json => formats::json::parse(input),
114 InputFormat::Yaml => formats::yaml::parse(input),
115 InputFormat::Toml => formats::toml_format::parse(input),
116 InputFormat::Env => formats::env::parse(input),
117 InputFormat::Headers => formats::headers::parse(input),
118 InputFormat::Logfmt => formats::logfmt::parse(input),
119 InputFormat::Csv => formats::csv_format::parse(input),
120 InputFormat::Text => formats::text::parse(input),
121 InputFormat::Auto => {
122 Err(PickError::UnknownFormat)
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use cli::OutputFormat;
132
133 fn make_cli(selector: Option<&str>) -> Cli {
134 Cli {
135 selector: selector.map(String::from),
136 input: InputFormat::Auto,
137 output: OutputFormat::Auto,
138 file: None,
139 json: false,
140 raw: false,
141 first: false,
142 lines: false,
143 default: None,
144 quiet: false,
145 exists: false,
146 count: false,
147 stream: false,
148 }
149 }
150
151 #[test]
152 fn run_json_simple() {
153 let cli = make_cli(Some("name"));
154 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
155 assert_eq!(result, "Alice");
156 }
157
158 #[test]
159 fn run_json_nested() {
160 let cli = make_cli(Some("user.email"));
161 let result = run(&cli, r#"{"user": {"email": "a@b.com"}}"#).unwrap();
162 assert_eq!(result, "a@b.com");
163 }
164
165 #[test]
166 fn run_json_array_index() {
167 let cli = make_cli(Some("items[0]"));
168 let result = run(&cli, r#"{"items": ["first", "second"]}"#).unwrap();
169 assert_eq!(result, "first");
170 }
171
172 #[test]
173 fn run_json_wildcard() {
174 let cli = make_cli(Some("items[*].name"));
175 let result = run(&cli, r#"{"items": [{"name": "a"}, {"name": "b"}]}"#).unwrap();
176 assert_eq!(result, "a\nb");
177 }
178
179 #[test]
180 fn run_yaml() {
181 let mut cli = make_cli(Some("name"));
182 cli.input = InputFormat::Yaml;
183 let result = run(&cli, "name: Alice\nage: 30").unwrap();
184 assert_eq!(result, "Alice");
185 }
186
187 #[test]
188 fn run_toml() {
189 let mut cli = make_cli(Some("package.name"));
190 cli.input = InputFormat::Toml;
191 let result = run(&cli, "[package]\nname = \"pick\"").unwrap();
192 assert_eq!(result, "pick");
193 }
194
195 #[test]
196 fn run_env() {
197 let mut cli = make_cli(Some("PORT"));
198 cli.input = InputFormat::Env;
199 let result = run(&cli, "DATABASE_URL=pg://localhost\nPORT=3000").unwrap();
200 assert_eq!(result, "3000");
201 }
202
203 #[test]
204 fn run_headers() {
205 let mut cli = make_cli(Some("content-type"));
206 cli.input = InputFormat::Headers;
207 let result = run(&cli, "Content-Type: application/json\nX-Request-Id: abc").unwrap();
208 assert_eq!(result, "application/json");
209 }
210
211 #[test]
212 fn run_logfmt() {
213 let mut cli = make_cli(Some("msg"));
214 cli.input = InputFormat::Logfmt;
215 let result = run(&cli, "level=info msg=hello status=200").unwrap();
216 assert_eq!(result, "hello");
217 }
218
219 #[test]
220 fn run_csv() {
221 let mut cli = make_cli(Some("[0].name"));
222 cli.input = InputFormat::Csv;
223 let result = run(&cli, "name,age\nAlice,30\nBob,25").unwrap();
224 assert_eq!(result, "Alice");
225 }
226
227 #[test]
228 fn run_no_selector_returns_whole() {
229 let cli = make_cli(None);
230 let result = run(&cli, r#"{"a": 1}"#).unwrap();
231 assert!(result.contains("\"a\""));
232 }
233
234 #[test]
235 fn run_empty_input() {
236 let cli = make_cli(Some("x"));
237 assert!(run(&cli, "").is_err());
238 assert!(run(&cli, " ").is_err());
239 }
240
241 #[test]
242 fn run_key_not_found() {
243 let cli = make_cli(Some("missing"));
244 assert!(run(&cli, r#"{"a": 1}"#).is_err());
245 }
246
247 #[test]
248 fn run_default_on_missing() {
249 let mut cli = make_cli(Some("missing"));
250 cli.default = Some("fallback".into());
251 let result = run(&cli, r#"{"a": 1}"#).unwrap();
252 assert_eq!(result, "fallback");
253 }
254
255 #[test]
256 fn run_exists_found() {
257 let mut cli = make_cli(Some("a"));
258 cli.exists = true;
259 let result = run(&cli, r#"{"a": 1}"#).unwrap();
260 assert_eq!(result, "");
261 }
262
263 #[test]
264 fn run_exists_not_found() {
265 let mut cli = make_cli(Some("b"));
266 cli.exists = true;
267 assert!(run(&cli, r#"{"a": 1}"#).is_err());
268 }
269
270 #[test]
271 fn run_count() {
272 let mut cli = make_cli(Some("items[*]"));
273 cli.count = true;
274 let result = run(&cli, r#"{"items": [1, 2, 3]}"#).unwrap();
275 assert_eq!(result, "3");
276 }
277
278 #[test]
279 fn run_first() {
280 let mut cli = make_cli(Some("items[*]"));
281 cli.first = true;
282 let result = run(&cli, r#"{"items": [1, 2, 3]}"#).unwrap();
283 assert_eq!(result, "1");
284 }
285
286 #[test]
287 fn run_json_output() {
288 let mut cli = make_cli(Some("name"));
289 cli.json = true;
290 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
291 assert_eq!(result, "\"Alice\"");
292 }
293
294 #[test]
295 fn run_lines_output() {
296 let mut cli = make_cli(Some("items"));
297 cli.lines = true;
298 let result = run(&cli, r#"{"items": ["a", "b", "c"]}"#).unwrap();
299 assert_eq!(result, "a\nb\nc");
300 }
301
302 #[test]
303 fn run_text_kv_fallback() {
304 let mut cli = make_cli(Some("name"));
305 cli.input = InputFormat::Text;
306 let result = run(&cli, "name=Alice\nage=30").unwrap();
307 assert_eq!(result, "Alice");
308 }
309
310 #[test]
311 fn run_text_search_fallback() {
312 let mut cli = make_cli(Some("error"));
313 cli.input = InputFormat::Text;
314 let result = run(&cli, "info: all good\nerror: something failed").unwrap();
315 assert_eq!(result, "something failed");
316 }
317
318 #[test]
319 fn run_auto_detect_json() {
320 let cli = make_cli(Some("x"));
321 let result = run(&cli, r#"{"x": 42}"#).unwrap();
322 assert_eq!(result, "42");
323 }
324
325 #[test]
326 fn run_auto_detect_env() {
327 let cli = make_cli(Some("PORT"));
328 let result = run(&cli, "PORT=3000\nHOST=localhost").unwrap();
329 assert_eq!(result, "3000");
330 }
331
332 #[test]
333 fn run_negative_index() {
334 let cli = make_cli(Some("[*][-1]"));
335 let result = run(&cli, "[[1,2],[3,4]]").unwrap();
336 assert_eq!(result, "2\n4");
337 }
338
339 #[test]
342 fn run_slice() {
343 let cli = make_cli(Some("items[1:3]"));
344 let result = run(&cli, r#"{"items": [10, 20, 30, 40, 50]}"#).unwrap();
345 assert_eq!(result, "20\n30");
346 }
347
348 #[test]
351 fn run_keys() {
352 let cli = make_cli(Some("keys()"));
353 let result = run(&cli, r#"{"b": 2, "a": 1}"#).unwrap();
354 assert!(result.contains("\"a\""));
355 assert!(result.contains("\"b\""));
356 }
357
358 #[test]
359 fn run_length() {
360 let cli = make_cli(Some("items.length()"));
361 let result = run(&cli, r#"{"items": [1, 2, 3]}"#).unwrap();
362 assert_eq!(result, "3");
363 }
364
365 #[test]
368 fn run_recursive() {
369 let cli = make_cli(Some("..name"));
370 let result = run(&cli, r#"{"a": {"name": "deep"}}"#).unwrap();
371 assert_eq!(result, "deep");
372 }
373
374 #[test]
377 fn run_multi_selector() {
378 let cli = make_cli(Some("name, age"));
379 let result = run(&cli, r#"{"name": "Alice", "age": 30}"#).unwrap();
380 assert_eq!(result, "Alice\n30");
381 }
382
383 #[test]
386 fn run_pipeline_select() {
387 let cli = make_cli(Some("items[*] | select(.price > 100) | name"));
388 let input = r#"{"items": [{"name": "a", "price": 50}, {"name": "b", "price": 200}]}"#;
389 let result = run(&cli, input).unwrap();
390 assert_eq!(result, "b");
391 }
392
393 #[test]
394 fn run_pipeline_builtin() {
395 let cli = make_cli(Some("data | keys()"));
396 let result = run(&cli, r#"{"data": {"x": 1, "y": 2}}"#).unwrap();
397 assert!(result.contains("\"x\""));
398 assert!(result.contains("\"y\""));
399 }
400
401 #[test]
404 fn run_pipeline_regex() {
405 let cli = make_cli(Some("items[*] | select(.name ~ \"^a\") | name"));
406 let input = r#"{"items": [{"name": "apple"}, {"name": "banana"}, {"name": "avocado"}]}"#;
407 let result = run(&cli, input).unwrap();
408 assert_eq!(result, "apple\navocado");
409 }
410
411 #[test]
414 fn run_set() {
415 let mut cli = make_cli(Some("set(.name, \"Bob\")"));
416 cli.json = true;
417 let result = run(&cli, r#"{"name": "Alice", "age": 30}"#).unwrap();
418 assert!(result.contains("\"Bob\""));
419 assert!(result.contains("30"));
420 }
421
422 #[test]
423 fn run_del() {
424 let mut cli = make_cli(Some("del(.temp)"));
425 cli.json = true;
426 let result = run(&cli, r#"{"name": "Alice", "temp": "x"}"#).unwrap();
427 assert!(result.contains("Alice"));
428 assert!(!result.contains("temp"));
429 }
430
431 #[test]
434 fn run_output_yaml() {
435 let mut cli = make_cli(None);
436 cli.output = OutputFormat::Yaml;
437 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
438 assert!(result.contains("name:"));
439 }
440
441 #[test]
442 fn run_output_toml() {
443 let mut cli = make_cli(None);
444 cli.output = OutputFormat::Toml;
445 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
446 assert!(result.contains("name = "));
447 }
448
449 #[test]
456 fn run_slice_from_start() {
457 let cli = make_cli(Some("items[:2]"));
458 let result = run(&cli, r#"{"items": [10, 20, 30, 40]}"#).unwrap();
459 assert_eq!(result, "10\n20");
460 }
461
462 #[test]
463 fn run_slice_to_end() {
464 let cli = make_cli(Some("items[2:]"));
465 let result = run(&cli, r#"{"items": [10, 20, 30, 40]}"#).unwrap();
466 assert_eq!(result, "30\n40");
467 }
468
469 #[test]
470 fn run_slice_negative() {
471 let cli = make_cli(Some("items[-2:]"));
472 let result = run(&cli, r#"{"items": [10, 20, 30, 40]}"#).unwrap();
473 assert_eq!(result, "30\n40");
474 }
475
476 #[test]
477 fn run_slice_all() {
478 let cli = make_cli(Some("items[:]"));
479 let result = run(&cli, r#"{"items": [1, 2, 3]}"#).unwrap();
480 assert_eq!(result, "1\n2\n3");
481 }
482
483 #[test]
484 fn run_slice_empty_result() {
485 let cli = make_cli(Some("items[10:20]"));
486 let result = run(&cli, r#"{"items": [1, 2, 3]}"#);
487 assert!(result.is_err());
488 }
489
490 #[test]
491 fn run_slice_deeply_nested() {
492 let cli = make_cli(Some("data[0].items[1:3]"));
493 let result = run(&cli, r#"{"data": [{"items": [10, 20, 30, 40]}]}"#).unwrap();
494 assert_eq!(result, "20\n30");
495 }
496
497 #[test]
500 fn run_values() {
501 let cli = make_cli(Some("values()"));
502 let result = run(&cli, r#"{"a": 1, "b": 2}"#).unwrap();
503 assert!(result.contains("1"));
504 assert!(result.contains("2"));
505 }
506
507 #[test]
508 fn run_length_string() {
509 let cli = make_cli(Some("name.length()"));
510 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
511 assert_eq!(result, "5");
512 }
513
514 #[test]
515 fn run_length_object() {
516 let cli = make_cli(Some("length()"));
517 let result = run(&cli, r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
518 assert_eq!(result, "3");
519 }
520
521 #[test]
522 fn run_keys_on_array() {
523 let cli = make_cli(Some("keys()"));
524 let result = run(&cli, "[10, 20, 30]").unwrap();
525 assert!(result.contains("0"));
526 assert!(result.contains("1"));
527 assert!(result.contains("2"));
528 }
529
530 #[test]
533 fn run_recursive_multiple_matches() {
534 let cli = make_cli(Some("..id"));
535 let result = run(&cli, r#"{"a": {"id": 1}, "b": {"id": 2}}"#).unwrap();
536 assert!(result.contains("1"));
537 assert!(result.contains("2"));
538 }
539
540 #[test]
541 fn run_recursive_deep() {
542 let cli = make_cli(Some("..target"));
543 let result = run(&cli, r#"{"a": {"b": {"c": {"target": 42}}}}"#).unwrap();
544 assert_eq!(result, "42");
545 }
546
547 #[test]
548 fn run_recursive_not_found() {
549 let cli = make_cli(Some("..missing"));
550 assert!(run(&cli, r#"{"a": 1, "b": 2}"#).is_err());
551 }
552
553 #[test]
556 fn run_multi_selector_three() {
557 let cli = make_cli(Some("a, b, c"));
558 let result = run(&cli, r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
559 assert_eq!(result, "1\n2\n3");
560 }
561
562 #[test]
563 fn run_multi_selector_partial_missing() {
564 let cli = make_cli(Some("name, missing, age"));
565 let result = run(&cli, r#"{"name": "Alice", "age": 30}"#).unwrap();
566 assert_eq!(result, "Alice\n30");
567 }
568
569 #[test]
570 fn run_multi_selector_all_missing() {
571 let cli = make_cli(Some("x, y"));
572 let result = run(&cli, r#"{"a": 1}"#);
573 assert!(result.is_err());
574 }
575
576 #[test]
579 fn run_pipeline_three_stages() {
580 let cli = make_cli(Some("items[*] | select(.active) | name"));
581 let input = r#"{"items": [{"name": "a", "active": true}, {"name": "b", "active": false}, {"name": "c", "active": true}]}"#;
582 let result = run(&cli, input).unwrap();
583 assert_eq!(result, "a\nc");
584 }
585
586 #[test]
587 fn run_pipeline_four_stages() {
588 let cli = make_cli(Some("items[*] | select(.active) | name | length()"));
589 let input = r#"{"items": [{"name": "ab", "active": true}, {"name": "cde", "active": false}, {"name": "fgh", "active": true}]}"#;
590 let result = run(&cli, input).unwrap();
591 assert_eq!(result, "2\n3");
592 }
593
594 #[test]
595 fn run_pipeline_keys_then_length() {
596 let cli = make_cli(Some("keys() | length()"));
597 let result = run(&cli, r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
598 assert_eq!(result, "3");
599 }
600
601 #[test]
602 fn run_pipeline_values_then_length() {
603 let cli = make_cli(Some("values() | length()"));
604 let result = run(&cli, r#"{"a": 1, "b": 2}"#).unwrap();
605 assert_eq!(result, "2");
606 }
607
608 #[test]
611 fn run_select_lt() {
612 let cli = make_cli(Some("[*] | select(. < 10)"));
613 let result = run(&cli, "[1, 5, 10, 15, 20]").unwrap();
614 assert_eq!(result, "1\n5");
615 }
616
617 #[test]
618 fn run_select_gte() {
619 let cli = make_cli(Some("[*] | select(. >= 10)"));
620 let result = run(&cli, "[1, 5, 10, 15, 20]").unwrap();
621 assert_eq!(result, "10\n15\n20");
622 }
623
624 #[test]
625 fn run_select_ne() {
626 let cli = make_cli(Some("[*] | select(.status != \"deleted\") | name"));
627 let input = r#"[{"name": "a", "status": "active"}, {"name": "b", "status": "deleted"}, {"name": "c", "status": "active"}]"#;
628 let result = run(&cli, input).unwrap();
629 assert_eq!(result, "a\nc");
630 }
631
632 #[test]
633 fn run_select_or() {
634 let cli = make_cli(Some(
635 "[*] | select(.price > 100 or .featured == true) | name",
636 ));
637 let input = r#"[{"name": "a", "price": 5, "featured": true}, {"name": "b", "price": 50, "featured": false}, {"name": "c", "price": 500, "featured": false}]"#;
638 let result = run(&cli, input).unwrap();
639 assert_eq!(result, "a\nc");
640 }
641
642 #[test]
643 fn run_select_not() {
644 let cli = make_cli(Some("[*] | select(not .active) | name"));
645 let input = r#"[{"name": "a", "active": true}, {"name": "b", "active": false}]"#;
646 let result = run(&cli, input).unwrap();
647 assert_eq!(result, "b");
648 }
649
650 #[test]
651 fn run_select_all_filtered_out() {
652 let cli = make_cli(Some("[*] | select(. > 100)"));
653 let result = run(&cli, "[1, 2, 3]");
654 assert!(result.is_err());
655 }
656
657 #[test]
658 fn run_select_eq_null() {
659 let cli = make_cli(Some("[*] | select(.email == null) | name"));
660 let input = r#"[{"name": "a", "email": null}, {"name": "b", "email": "b@x.com"}]"#;
661 let result = run(&cli, input).unwrap();
662 assert_eq!(result, "a");
663 }
664
665 #[test]
666 fn run_select_eq_bool() {
667 let cli = make_cli(Some("[*] | select(.done == true) | name"));
668 let input = r#"[{"name": "a", "done": true}, {"name": "b", "done": false}]"#;
669 let result = run(&cli, input).unwrap();
670 assert_eq!(result, "a");
671 }
672
673 #[test]
674 fn run_select_regex_case_insensitive() {
675 let cli = make_cli(Some("[*] | select(. ~ \"(?i)^hello$\")"));
676 let result = run(&cli, r#"["Hello", "hello", "HELLO", "world"]"#).unwrap();
677 assert_eq!(result, "Hello\nhello\nHELLO");
678 }
679
680 #[test]
683 fn run_set_nested() {
684 let mut cli = make_cli(Some("set(.user.name, \"Bob\")"));
685 cli.json = true;
686 let result = run(&cli, r#"{"user": {"name": "Alice", "age": 30}}"#).unwrap();
687 assert!(result.contains("\"Bob\""));
688 assert!(result.contains("30"));
689 }
690
691 #[test]
692 fn run_set_new_key() {
693 let mut cli = make_cli(Some("set(.b, 2)"));
694 cli.json = true;
695 let result = run(&cli, r#"{"a": 1}"#).unwrap();
696 assert!(result.contains("\"a\": 1"));
697 assert!(result.contains("\"b\": 2"));
698 }
699
700 #[test]
701 fn run_set_number() {
702 let cli = make_cli(Some("set(.count, 42) | count"));
703 let result = run(&cli, r#"{"count": 0}"#).unwrap();
704 assert_eq!(result, "42");
705 }
706
707 #[test]
708 fn run_set_bool() {
709 let cli = make_cli(Some("set(.active, true) | active"));
710 let result = run(&cli, r#"{"active": false}"#).unwrap();
711 assert_eq!(result, "true");
712 }
713
714 #[test]
715 fn run_set_null() {
716 let cli = make_cli(Some("set(.temp, null) | temp"));
717 let result = run(&cli, r#"{"temp": "data"}"#).unwrap();
718 assert_eq!(result, "null");
719 }
720
721 #[test]
722 fn run_del_nested() {
723 let mut cli = make_cli(Some("del(.user.temp)"));
724 cli.json = true;
725 let result = run(&cli, r#"{"user": {"name": "Alice", "temp": "x"}}"#).unwrap();
726 assert!(result.contains("Alice"));
727 assert!(!result.contains("temp"));
728 }
729
730 #[test]
731 fn run_del_array_element() {
732 let mut cli = make_cli(Some("del(.items[1])"));
733 cli.json = true;
734 let result = run(&cli, r#"{"items": [1, 2, 3]}"#).unwrap();
735 assert!(result.contains("1"));
736 assert!(result.contains("3"));
737 assert!(!result.contains(" 2")); }
739
740 #[test]
741 fn run_set_then_del() {
742 let mut cli = make_cli(Some("set(.c, 3) | del(.a)"));
743 cli.json = true;
744 let result = run(&cli, r#"{"a": 1, "b": 2}"#).unwrap();
745 assert!(result.contains("\"b\": 2"));
746 assert!(result.contains("\"c\": 3"));
747 assert!(!result.contains("\"a\""));
748 }
749
750 #[test]
751 fn run_multiple_set() {
752 let cli = make_cli(Some("set(.x, 1) | set(.y, 2) | keys() | length()"));
753 let result = run(&cli, r#"{"a": 0}"#).unwrap();
754 assert_eq!(result, "3");
756 }
757
758 #[test]
759 fn run_multiple_del() {
760 let mut cli = make_cli(Some("del(.a) | del(.b)"));
761 cli.json = true;
762 let result = run(&cli, r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
763 assert!(result.contains("\"c\": 3"));
764 assert!(!result.contains("\"a\""));
765 assert!(!result.contains("\"b\""));
766 }
767
768 #[test]
771 fn run_output_json_explicit() {
772 let mut cli = make_cli(Some("name"));
773 cli.output = OutputFormat::Json;
774 let result = run(&cli, r#"{"name": "Alice"}"#).unwrap();
775 assert_eq!(result, "\"Alice\"");
776 }
777
778 #[test]
779 fn run_output_yaml_nested() {
780 let mut cli = make_cli(Some("user"));
781 cli.output = OutputFormat::Yaml;
782 let result = run(&cli, r#"{"user": {"name": "Alice", "age": 30}}"#).unwrap();
783 assert!(result.contains("name:"));
784 assert!(result.contains("Alice"));
785 }
786
787 #[test]
790 fn run_slice_then_select() {
791 let cli = make_cli(Some("items[1:4] | select(.price > 100) | name"));
792 let input = r#"{"items": [{"name": "a", "price": 10}, {"name": "b", "price": 200}, {"name": "c", "price": 50}, {"name": "d", "price": 300}]}"#;
793 let result = run(&cli, input).unwrap();
794 assert_eq!(result, "b\nd");
795 }
796
797 #[test]
798 fn run_recursive_then_select() {
799 let cli = make_cli(Some("..items[*] | select(.active) | name"));
800 let input = r#"{"data": {"items": [{"name": "a", "active": true}, {"name": "b", "active": false}]}}"#;
801 let result = run(&cli, input).unwrap();
802 assert_eq!(result, "a");
803 }
804
805 #[test]
806 fn run_wildcard_then_length() {
807 let cli = make_cli(Some("items[*].name | length()"));
808 let input = r#"{"items": [{"name": "ab"}, {"name": "cde"}]}"#;
809 let result = run(&cli, input).unwrap();
810 assert_eq!(result, "2\n3");
811 }
812
813 #[test]
814 fn run_select_then_set() {
815 let mut cli = make_cli(Some("[*] | select(.active) | set(.selected, true)"));
816 cli.json = true;
817 let input = r#"[{"name": "a", "active": true}, {"name": "b", "active": false}]"#;
818 let result = run(&cli, input).unwrap();
819 assert!(result.contains("selected"));
820 assert!(result.contains("\"a\""));
821 }
822
823 #[test]
826 fn run_count_with_select() {
827 let mut cli = make_cli(Some("[*] | select(. > 10)"));
828 cli.count = true;
829 let result = run(&cli, "[1, 5, 15, 20, 25]").unwrap();
830 assert_eq!(result, "3");
831 }
832
833 #[test]
834 fn run_first_with_select() {
835 let mut cli = make_cli(Some("[*] | select(. > 10)"));
836 cli.first = true;
837 let result = run(&cli, "[1, 5, 15, 20, 25]").unwrap();
838 assert_eq!(result, "15");
839 }
840
841 #[test]
842 fn run_exists_with_pipeline() {
843 let mut cli = make_cli(Some("items[*] | select(.active)"));
844 cli.exists = true;
845 let input = r#"{"items": [{"active": true}]}"#;
846 let result = run(&cli, input).unwrap();
847 assert_eq!(result, "");
848 }
849
850 #[test]
851 fn run_default_with_pipeline() {
852 let mut cli = make_cli(Some("[*] | select(. > 100)"));
853 cli.default = Some("none".into());
854 let result = run(&cli, "[1, 2, 3]").unwrap();
855 assert_eq!(result, "none");
856 }
857
858 #[test]
861 fn run_unicode_key() {
862 let cli = make_cli(Some("\"名前\""));
863 let result = run(&cli, r#"{"名前": "太郎"}"#).unwrap();
864 assert_eq!(result, "太郎");
865 }
866
867 #[test]
868 fn run_unicode_value() {
869 let cli = make_cli(Some("emoji"));
870 let result = run(&cli, r#"{"emoji": "🎉🎊🎈"}"#).unwrap();
871 assert_eq!(result, "🎉🎊🎈");
872 }
873
874 #[test]
877 fn run_default_on_parse_error() {
878 let mut cli = make_cli(Some("name"));
879 cli.default = Some("fallback".into());
880 cli.input = InputFormat::Json;
881 let result = run(&cli, "not valid json").unwrap();
882 assert_eq!(result, "fallback");
883 }
884}