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