1use std::{io, string::FromUtf8Error};
2
3use super::OutputMapping;
4use crate::{ExitStatus, UnexpectedExitStatus};
5use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum CommandExecutionError {
10 #[error(transparent)]
12 Io(#[from] io::Error),
13 #[error(transparent)]
15 UnexpectedExitStatus(#[from] UnexpectedExitStatus),
16}
17
18#[derive(Debug)]
20pub struct ReturnNothing;
21
22impl OutputMapping for ReturnNothing {
23 type Output = ();
24 type Error = CommandExecutionError;
25
26 fn capture_stdout(&self) -> bool {
27 false
28 }
29
30 fn capture_stderr(&self) -> bool {
31 false
32 }
33
34 fn map_output(
35 self: Box<Self>,
36 _stdout: Option<Vec<u8>>,
37 _stderr: Option<Vec<u8>>,
38 _exit_status: ExitStatus,
39 ) -> Result<Self::Output, Self::Error> {
40 Ok(())
41 }
42}
43
44#[derive(Debug)]
46pub struct ReturnStdout;
47
48impl OutputMapping for ReturnStdout {
49 type Output = Vec<u8>;
50 type Error = CommandExecutionError;
51
52 fn capture_stdout(&self) -> bool {
53 true
54 }
55
56 fn capture_stderr(&self) -> bool {
57 false
58 }
59
60 fn map_output(
61 self: Box<Self>,
62 stdout: Option<Vec<u8>>,
63 _stderr: Option<Vec<u8>>,
64 _exit_status: ExitStatus,
65 ) -> Result<Self::Output, Self::Error> {
66 Ok(stdout.unwrap())
67 }
68}
69
70#[derive(Debug)]
72pub struct ReturnStderr;
73
74impl OutputMapping for ReturnStderr {
75 type Output = Vec<u8>;
76 type Error = CommandExecutionError;
77
78 fn capture_stdout(&self) -> bool {
79 false
80 }
81
82 fn capture_stderr(&self) -> bool {
83 true
84 }
85
86 fn map_output(
87 self: Box<Self>,
88 _stdout: Option<Vec<u8>>,
89 stderr: Option<Vec<u8>>,
90 _exit_status: ExitStatus,
91 ) -> Result<Self::Output, Self::Error> {
92 Ok(stderr.unwrap())
93 }
94}
95
96#[derive(Debug)]
98pub struct ReturnStdoutAndErr;
99
100impl OutputMapping for ReturnStdoutAndErr {
101 type Output = CapturedStdoutAndErr;
102 type Error = CommandExecutionError;
103
104 fn capture_stdout(&self) -> bool {
105 true
106 }
107
108 fn capture_stderr(&self) -> bool {
109 true
110 }
111
112 fn map_output(
113 self: Box<Self>,
114 stdout: Option<Vec<u8>>,
115 stderr: Option<Vec<u8>>,
116 _exit_status: ExitStatus,
117 ) -> Result<Self::Output, Self::Error> {
118 Ok(CapturedStdoutAndErr {
119 stdout: stdout.unwrap(),
120 stderr: stderr.unwrap(),
121 })
122 }
123}
124
125#[derive(Debug)]
127pub struct CapturedStdoutAndErr {
128 pub stdout: Vec<u8>,
129 pub stderr: Vec<u8>,
130}
131
132#[derive(Debug)]
134pub struct MapStdout<O, E, F>(pub F)
135where
136 F: FnMut(Vec<u8>) -> Result<O, E> + 'static,
137 E: From<CommandExecutionError> + 'static,
138 O: 'static;
139
140impl<O, E, F> OutputMapping for MapStdout<O, E, F>
141where
142 F: FnMut(Vec<u8>) -> Result<O, E>,
143 E: From<CommandExecutionError>,
144{
145 type Output = O;
146 type Error = E;
147
148 fn capture_stdout(&self) -> bool {
149 true
150 }
151
152 fn capture_stderr(&self) -> bool {
153 false
154 }
155
156 fn map_output(
157 mut self: Box<Self>,
158 stdout: Option<Vec<u8>>,
159 _stderr: Option<Vec<u8>>,
160 _exit_status: ExitStatus,
161 ) -> Result<Self::Output, Self::Error> {
162 (self.0)(stdout.unwrap())
163 }
164}
165
166#[derive(Debug)]
168pub struct MapStderr<O, E, F>(pub F)
169where
170 F: FnMut(Vec<u8>) -> Result<O, E> + 'static,
171 E: From<CommandExecutionError> + 'static,
172 O: 'static;
173
174impl<O, E, F> OutputMapping for MapStderr<O, E, F>
175where
176 F: FnMut(Vec<u8>) -> Result<O, E>,
177 E: From<CommandExecutionError>,
178{
179 type Output = O;
180 type Error = E;
181
182 fn capture_stdout(&self) -> bool {
183 false
184 }
185
186 fn capture_stderr(&self) -> bool {
187 true
188 }
189
190 fn map_output(
191 mut self: Box<Self>,
192 _stdout: Option<Vec<u8>>,
193 stderr: Option<Vec<u8>>,
194 _exit_status: ExitStatus,
195 ) -> Result<Self::Output, Self::Error> {
196 (self.0)(stderr.unwrap())
197 }
198}
199
200#[derive(Debug)]
202pub struct MapStdoutAndErr<O, E, F>(pub F)
203where
204 F: FnMut(CapturedStdoutAndErr) -> Result<O, E> + 'static,
205 E: From<CommandExecutionError> + 'static,
206 O: 'static;
207
208impl<O, E, F> OutputMapping for MapStdoutAndErr<O, E, F>
209where
210 F: FnMut(CapturedStdoutAndErr) -> Result<O, E>,
211 E: From<CommandExecutionError>,
212{
213 type Output = O;
214 type Error = E;
215
216 fn capture_stdout(&self) -> bool {
217 true
218 }
219
220 fn capture_stderr(&self) -> bool {
221 true
222 }
223
224 fn map_output(
225 mut self: Box<Self>,
226 stdout: Option<Vec<u8>>,
227 stderr: Option<Vec<u8>>,
228 _exit_status: ExitStatus,
229 ) -> Result<Self::Output, Self::Error> {
230 (self.0)(CapturedStdoutAndErr {
231 stdout: stdout.unwrap(),
232 stderr: stderr.unwrap(),
233 })
234 }
235}
236
237#[derive(Debug, Error)]
239pub enum CommandExecutionWithStringOutputError {
240 #[error(transparent)]
242 Io(#[from] io::Error),
243
244 #[error(transparent)]
246 UnexpectedExitStatus(#[from] UnexpectedExitStatus),
247
248 #[error(transparent)]
250 Utf8Error(#[from] FromUtf8Error),
251}
252
253fn output_to_string(output: Vec<u8>) -> Result<String, CommandExecutionWithStringOutputError> {
258 Ok(String::from_utf8(output)?)
259}
260
261#[derive(Debug)]
263pub struct ReturnStdoutString;
264
265impl OutputMapping for ReturnStdoutString {
266 type Output = String;
267 type Error = CommandExecutionWithStringOutputError;
268
269 fn capture_stdout(&self) -> bool {
270 true
271 }
272
273 fn capture_stderr(&self) -> bool {
274 false
275 }
276
277 fn map_output(
278 self: Box<Self>,
279 stdout: Option<Vec<u8>>,
280 _stderr: Option<Vec<u8>>,
281 _exit_status: ExitStatus,
282 ) -> Result<Self::Output, Self::Error> {
283 Ok(output_to_string(stdout.unwrap())?)
284 }
285}
286
287#[derive(Debug)]
289pub struct ReturnStderrString;
290
291impl OutputMapping for ReturnStderrString {
292 type Output = String;
293 type Error = CommandExecutionWithStringOutputError;
294
295 fn capture_stdout(&self) -> bool {
296 false
297 }
298
299 fn capture_stderr(&self) -> bool {
300 true
301 }
302
303 fn map_output(
304 self: Box<Self>,
305 _stdout: Option<Vec<u8>>,
306 stderr: Option<Vec<u8>>,
307 _exit_status: ExitStatus,
308 ) -> Result<Self::Output, Self::Error> {
309 Ok(output_to_string(stderr.unwrap())?)
310 }
311}
312
313#[derive(Debug)]
315pub struct ReturnStdoutAndErrStrings;
316
317impl OutputMapping for ReturnStdoutAndErrStrings {
318 type Output = CapturedStdoutAndErrStrings;
319 type Error = CommandExecutionWithStringOutputError;
320
321 fn capture_stdout(&self) -> bool {
322 true
323 }
324
325 fn capture_stderr(&self) -> bool {
326 true
327 }
328
329 fn map_output(
330 self: Box<Self>,
331 stdout: Option<Vec<u8>>,
332 stderr: Option<Vec<u8>>,
333 _exit_status: ExitStatus,
334 ) -> Result<Self::Output, Self::Error> {
335 Ok(CapturedStdoutAndErrStrings {
336 stdout: output_to_string(stdout.unwrap())?,
337 stderr: output_to_string(stderr.unwrap())?,
338 })
339 }
340}
341
342#[derive(Debug)]
344pub struct CapturedStdoutAndErrStrings {
345 pub stdout: String,
346 pub stderr: String,
347}
348
349#[derive(Debug)]
351pub struct MapStdoutString<O, E, F>(pub F)
352where
353 F: FnMut(String) -> Result<O, E> + 'static,
354 E: From<CommandExecutionWithStringOutputError> + 'static,
355 O: 'static;
356
357impl<O, E, F> OutputMapping for MapStdoutString<O, E, F>
358where
359 F: FnMut(String) -> Result<O, E>,
360 E: From<CommandExecutionWithStringOutputError>,
361{
362 type Output = O;
363 type Error = E;
364
365 fn capture_stdout(&self) -> bool {
366 true
367 }
368
369 fn capture_stderr(&self) -> bool {
370 false
371 }
372
373 fn map_output(
374 mut self: Box<Self>,
375 stdout: Option<Vec<u8>>,
376 _stderr: Option<Vec<u8>>,
377 _exit_status: ExitStatus,
378 ) -> Result<Self::Output, Self::Error> {
379 (self.0)(output_to_string(stdout.unwrap())?)
380 }
381}
382
383#[derive(Debug)]
385pub struct MapStderrString<O, E, F>(pub F)
386where
387 F: FnMut(String) -> Result<O, E> + 'static,
388 E: From<CommandExecutionWithStringOutputError> + 'static,
389 O: 'static;
390
391impl<O, E, F> OutputMapping for MapStderrString<O, E, F>
392where
393 F: FnMut(String) -> Result<O, E>,
394 E: From<CommandExecutionWithStringOutputError>,
395{
396 type Output = O;
397 type Error = E;
398
399 fn capture_stdout(&self) -> bool {
400 false
401 }
402
403 fn capture_stderr(&self) -> bool {
404 true
405 }
406
407 fn map_output(
408 mut self: Box<Self>,
409 _stdout: Option<Vec<u8>>,
410 stderr: Option<Vec<u8>>,
411 _exit_status: ExitStatus,
412 ) -> Result<Self::Output, Self::Error> {
413 (self.0)(output_to_string(stderr.unwrap())?)
414 }
415}
416
417#[derive(Debug)]
419pub struct MapStdoutAndErrStrings<O, E, F>(pub F)
420where
421 F: FnMut(CapturedStdoutAndErrStrings) -> Result<O, E> + 'static,
422 E: From<CommandExecutionWithStringOutputError> + 'static,
423 O: 'static;
424
425impl<O, E, F> OutputMapping for MapStdoutAndErrStrings<O, E, F>
426where
427 F: FnMut(CapturedStdoutAndErrStrings) -> Result<O, E>,
428 E: From<CommandExecutionWithStringOutputError>,
429{
430 type Output = O;
431 type Error = E;
432
433 fn capture_stdout(&self) -> bool {
434 true
435 }
436
437 fn capture_stderr(&self) -> bool {
438 true
439 }
440
441 fn map_output(
442 mut self: Box<Self>,
443 stdout: Option<Vec<u8>>,
444 stderr: Option<Vec<u8>>,
445 _exit_status: ExitStatus,
446 ) -> Result<Self::Output, Self::Error> {
447 (self.0)(CapturedStdoutAndErrStrings {
448 stdout: output_to_string(stdout.unwrap())?,
449 stderr: output_to_string(stderr.unwrap())?,
450 })
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 #![allow(non_snake_case)]
457
458 use super::{output_to_string, CommandExecutionWithStringOutputError};
459
460 mod ReturnNothing {
461 use super::super::*;
462 use crate::{Command, ExecResult};
463
464 #[test]
465 fn captures_stdout_returns_true() {
466 assert_eq!(ReturnNothing.capture_stdout(), false);
467 }
468
469 #[test]
470 fn captures_stderr_returns_false() {
471 assert_eq!(ReturnNothing.capture_stderr(), false);
472 }
473
474 #[test]
475 fn returns_nothing() {
476 let _: () = Command::new("foo", ReturnNothing)
477 .with_exec_replacement_callback(move |_, _| {
478 Ok(ExecResult {
479 exit_status: 0.into(),
480 stdout: None,
481 stderr: None,
482 })
483 })
484 .run()
485 .unwrap();
486 }
487 }
488
489 mod ReturnStdout {
490 use super::super::*;
491 use crate::{Command, ExecResult};
492 use proptest::prelude::*;
493
494 #[test]
495 fn captures_stdout_returns_true() {
496 assert_eq!(ReturnStdout.capture_stdout(), true);
497 }
498
499 #[test]
500 fn captures_stderr_returns_false() {
501 assert_eq!(ReturnStdout.capture_stderr(), false);
502 }
503
504 proptest! {
505 #[test]
506 fn returns_only_captured_std_out_but_not_err(
507 stdout in any::<Vec<u8>>(),
508 ) {
509 let stdout_ = stdout.clone();
510 let out = Command::new("foo", ReturnStdout)
511 .with_exec_replacement_callback(move |_,_| {
512 Ok(ExecResult {
513 exit_status: 0.into(),
514 stdout: Some(stdout_),
515 stderr: None
516 })
517 })
518 .run()
519 .unwrap();
520
521 assert_eq!(out, stdout);
522 }
523 }
524 }
525
526 mod ReturnStderr {
527 use super::super::*;
528 use crate::{Command, ExecResult};
529 use proptest::prelude::*;
530
531 #[test]
532 fn captures_stdout_returns_true() {
533 assert_eq!(ReturnStderr.capture_stdout(), false);
534 }
535
536 #[test]
537 fn captures_stderr_returns_false() {
538 assert_eq!(ReturnStderr.capture_stderr(), true);
539 }
540
541 proptest! {
542 #[test]
543 fn returns_only_captured_std_err_but_not_out(
544 stderr in any::<Vec<u8>>()
545 ) {
546 let stderr_ = stderr.clone();
547 let out = Command::new("foo", ReturnStderr)
548 .with_exec_replacement_callback(move |_,_| {
549 Ok(ExecResult {
550 exit_status: 0.into(),
551 stdout: None,
552 stderr: Some(stderr_)
553 })
554 })
555 .run()
556 .unwrap();
557
558 assert_eq!(out, stderr);
559 }
560 }
561 }
562
563 mod ReturnStdoutAndErr {
564 use super::super::*;
565 use crate::{Command, ExecResult};
566 use proptest::prelude::*;
567
568 #[test]
569 fn captures_stdout_returns_true() {
570 assert_eq!(ReturnStdoutAndErr.capture_stdout(), true);
571 }
572
573 #[test]
574 fn captures_stderr_returns_false() {
575 assert_eq!(ReturnStdoutAndErr.capture_stderr(), true);
576 }
577
578 proptest! {
579 #[test]
580 fn returns_captured_std_out_and_err(
581 stdout in any::<Vec<u8>>(),
582 stderr in any::<Vec<u8>>()
583 ) {
584 let stdout_ = stdout.clone();
585 let stderr_ = stderr.clone();
586 let out: CapturedStdoutAndErr = Command::new("foo", ReturnStdoutAndErr)
587 .with_exec_replacement_callback(move |_,_| {
588 Ok(ExecResult {
589 exit_status: 0.into(),
590 stdout: Some(stdout_),
591 stderr: Some(stderr_)
592 })
593 })
594 .run()
595 .unwrap();
596
597 assert_eq!(out.stdout, stdout);
598 assert_eq!(out.stderr, stderr);
599 }
600 }
601 }
602
603 mod MapStdout {
604 use super::super::*;
605 use crate::{Command, ExecResult};
606
607 #[test]
608 fn maps_stdout_to_a_result() {
609 let res = Command::new(
610 "foo",
611 MapStdout(|out| -> Result<u32, Box<dyn std::error::Error>> {
612 Ok(String::from_utf8(out)?.parse()?)
613 }),
614 )
615 .with_exec_replacement_callback(|_, _| {
616 Ok(ExecResult {
617 exit_status: 0.into(),
618 stdout: Some("3241".into()),
619 stderr: None,
620 })
621 })
622 .run()
623 .unwrap();
624
625 assert_eq!(res, 3241u32);
626 }
627
628 #[test]
629 fn mapping_stdout_to_a_result_can_fail() {
630 Command::new(
631 "foo",
632 MapStdout(|out| -> Result<u32, Box<dyn std::error::Error>> {
633 Ok(String::from_utf8(out)?.parse()?)
634 }),
635 )
636 .with_exec_replacement_callback(|_, _| {
637 Ok(ExecResult {
638 exit_status: 0.into(),
639 stdout: Some("abcd".into()),
640 stderr: None,
641 })
642 })
643 .run()
644 .unwrap_err();
645 }
646 }
647
648 mod MapStderr {
649 use super::super::*;
650 use crate::{Command, ExecResult};
651
652 #[test]
653 fn maps_stderr_to_a_result() {
654 let res = Command::new(
655 "foo",
656 MapStderr(|err| -> Result<u32, Box<dyn std::error::Error>> {
657 Ok(String::from_utf8(err)?.parse()?)
658 }),
659 )
660 .with_exec_replacement_callback(|_, _| {
661 Ok(ExecResult {
662 exit_status: 0.into(),
663 stderr: Some("3241".into()),
664 stdout: None,
665 })
666 })
667 .run()
668 .unwrap();
669
670 assert_eq!(res, 3241u32);
671 }
672
673 #[test]
674 fn mapping_stderr_to_a_result_can_fail() {
675 Command::new(
676 "foo",
677 MapStderr(|err| -> Result<u32, Box<dyn std::error::Error>> {
678 Ok(String::from_utf8(err)?.parse()?)
679 }),
680 )
681 .with_exec_replacement_callback(|_, _| {
682 Ok(ExecResult {
683 exit_status: 0.into(),
684 stdout: None,
685 stderr: Some("abcd".into()),
686 })
687 })
688 .run()
689 .unwrap_err();
690 }
691 }
692
693 mod MapStdoutAndErr {
694 use super::super::*;
695 use crate::{Command, ExecResult};
696
697 #[test]
698 fn maps_stdout_to_a_result() {
699 let res = Command::new(
700 "foo",
701 MapStdoutAndErr(|cap| -> Result<(u32, u32), Box<dyn std::error::Error>> {
702 let out_res = String::from_utf8(cap.stdout)?.parse()?;
703 let err_res = String::from_utf8(cap.stderr)?.parse()?;
704 Ok((out_res, err_res))
705 }),
706 )
707 .with_exec_replacement_callback(|_, _| {
708 Ok(ExecResult {
709 exit_status: 0.into(),
710 stdout: Some("3241".into()),
711 stderr: Some("1242".into()),
712 })
713 })
714 .run()
715 .unwrap();
716
717 assert_eq!(res, (3241u32, 1242u32));
718 }
719
720 #[test]
721 fn mapping_stdout_to_a_result_can_fail() {
722 Command::new(
723 "foo",
724 MapStdoutAndErr(|_| -> Result<u32, Box<dyn std::error::Error>> {
725 Err("yes this fails")?
726 }),
727 )
728 .with_exec_replacement_callback(|_, _| {
729 Ok(ExecResult {
730 exit_status: 0.into(),
731 stdout: Some(Vec::new()),
732 stderr: Some(Vec::new()),
733 })
734 })
735 .run()
736 .unwrap_err();
737 }
738 }
739
740 fn is_utf8_error(err: &CommandExecutionWithStringOutputError) -> bool {
741 if let CommandExecutionWithStringOutputError::Utf8Error(_) = err {
742 true
743 } else {
744 false
745 }
746 }
747
748 #[test]
749 fn output_to_string_fails_on_bad_strings() {
750 let err = output_to_string(vec![0xFF, 0xFF, 0xFF]).unwrap_err();
751
752 if !is_utf8_error(&err) {
753 panic!("unexpected error: {:?}", err);
754 }
755 }
756
757 #[test]
758 fn output_to_string_converts_utf8_bytes_to_string() {
759 let out = output_to_string("hy".to_owned().into_bytes()).unwrap();
760 assert_eq!(out, "hy");
761 }
762
763 mod ReturnStdoutString {
764 use super::super::*;
765 use crate::{Command, ExecResult};
766 use proptest::prelude::*;
767 use tests::is_utf8_error;
768
769 #[test]
770 fn captures_stdout_returns_true() {
771 assert_eq!(ReturnStdoutString.capture_stdout(), true);
772 }
773
774 #[test]
775 fn captures_stderr_returns_false() {
776 assert_eq!(ReturnStdoutString.capture_stderr(), false);
777 }
778
779 proptest! {
780 #[test]
781 fn returns_only_captured_std_out_but_not_err(
782 stdout in any::<Vec<u8>>(),
783 ) {
784 let stdout_ = stdout.clone();
785 let res = Command::new("foo", ReturnStdoutString)
786 .with_exec_replacement_callback(move |_,_| {
787 Ok(ExecResult {
788 exit_status: 0.into(),
789 stdout: Some(stdout_),
790 stderr: None
791 })
792 })
793 .run();
794
795 let expected = output_to_string(stdout);
796
797 match expected {
798 Ok(expected) => {
799 let got = res.unwrap();
800 assert_eq!(expected, got);
801 },
802 Err(error) => {
803 assert!(is_utf8_error(&error));
804 }
805 }
806 }
807 }
808 }
809
810 mod ReturnStderrString {
811 use super::{super::*, is_utf8_error};
812 use crate::{Command, ExecResult};
813 use proptest::prelude::*;
814
815 #[test]
816 fn captures_stdout_returns_true() {
817 assert_eq!(ReturnStderrString.capture_stdout(), false);
818 }
819
820 #[test]
821 fn captures_stderr_returns_false() {
822 assert_eq!(ReturnStderrString.capture_stderr(), true);
823 }
824
825 proptest! {
826 #[test]
827 fn returns_only_captured_std_err_but_not_out(
828 stderr in any::<Vec<u8>>()
829 ) {
830 let stderr_ = stderr.clone();
831 let res = Command::new("foo", ReturnStderrString)
832 .with_exec_replacement_callback(move |_,_| {
833 Ok(ExecResult {
834 exit_status: 0.into(),
835 stdout: None,
836 stderr: Some(stderr_)
837 })
838 })
839 .run();
840
841 let expected = output_to_string(stderr);
842
843 match expected {
844 Ok(expected) => {
845 let got = res.unwrap();
846 assert_eq!(expected, got);
847 },
848 Err(error) => {
849 assert!(is_utf8_error(&error));
850 }
851 }
852 }
853 }
854 }
855
856 mod ReturnStdoutAndErrStrings {
857 use super::{super::*, is_utf8_error};
858 use crate::{Command, ExecResult};
859 use proptest::prelude::*;
860
861 #[test]
862 fn captures_stdout_returns_true() {
863 assert_eq!(ReturnStdoutAndErrStrings.capture_stdout(), true);
864 }
865
866 #[test]
867 fn captures_stderr_returns_false() {
868 assert_eq!(ReturnStdoutAndErrStrings.capture_stderr(), true);
869 }
870
871 proptest! {
872 #[test]
873 fn returns_captured_std_out_and_err(
874 stdout in any::<Vec<u8>>(),
875 stderr in any::<Vec<u8>>()
876 ) {
877 let stdout_ = stdout.clone();
878 let stderr_ = stderr.clone();
879 let res = Command::new("foo", ReturnStdoutAndErrStrings)
880 .with_exec_replacement_callback(move |_,_| {
881 Ok(ExecResult {
882 exit_status: 0.into(),
883 stdout: Some(stdout_),
884 stderr: Some(stderr_)
885 })
886 })
887 .run();
888
889 let expected = output_to_string(stdout)
890 .and_then(|stdout| Ok((stdout, output_to_string(stderr)?)));
891
892 match expected {
893 Ok((expected_stdout, expected_stderr)) => {
894 let got = res.unwrap();
895 assert_eq!(expected_stdout, got.stdout);
896 assert_eq!(expected_stderr, got.stderr);
897 },
898 Err(error) => {
899 assert!(is_utf8_error(&error));
900 }
901 }
902 }
903 }
904 }
905
906 mod MapStdoutStrings {
907 use super::super::*;
908 use crate::{Command, ExecResult};
909
910 #[test]
911 fn maps_stdout_to_a_result() {
912 let res = Command::new(
913 "foo",
914 MapStdoutString(|out| -> Result<u32, Box<dyn std::error::Error>> {
915 Ok(out.parse()?)
916 }),
917 )
918 .with_exec_replacement_callback(|_, _| {
919 Ok(ExecResult {
920 exit_status: 0.into(),
921 stdout: Some("3241".into()),
922 stderr: None,
923 })
924 })
925 .run()
926 .unwrap();
927
928 assert_eq!(res, 3241u32);
929 }
930
931 #[test]
932 fn mapping_stdout_to_a_result_can_fail() {
933 Command::new(
934 "foo",
935 MapStdoutString(|out| -> Result<u32, Box<dyn std::error::Error>> {
936 Ok(out.parse()?)
937 }),
938 )
939 .with_exec_replacement_callback(|_, _| {
940 Ok(ExecResult {
941 exit_status: 0.into(),
942 stdout: Some("abcd".into()),
943 stderr: None,
944 })
945 })
946 .run()
947 .unwrap_err();
948 }
949 }
950
951 mod MapStderrString {
952 use super::super::*;
953 use crate::{Command, ExecResult};
954
955 #[test]
956 fn maps_stderr_to_a_result() {
957 let res = Command::new(
958 "foo",
959 MapStderrString(|err| -> Result<u32, Box<dyn std::error::Error>> {
960 Ok(err.parse()?)
961 }),
962 )
963 .with_exec_replacement_callback(|_, _| {
964 Ok(ExecResult {
965 exit_status: 0.into(),
966 stderr: Some("3241".into()),
967 stdout: None,
968 })
969 })
970 .run()
971 .unwrap();
972
973 assert_eq!(res, 3241u32);
974 }
975
976 #[test]
977 fn mapping_stderr_to_a_result_can_fail() {
978 Command::new(
979 "foo",
980 MapStderrString(|err| -> Result<u32, Box<dyn std::error::Error>> {
981 Ok(err.parse()?)
982 }),
983 )
984 .with_exec_replacement_callback(|_, _| {
985 Ok(ExecResult {
986 exit_status: 0.into(),
987 stdout: None,
988 stderr: Some("abcd".into()),
989 })
990 })
991 .run()
992 .unwrap_err();
993 }
994 }
995
996 mod MapStdoutAndErrStrings {
997 use super::super::*;
998 use crate::{Command, ExecResult};
999
1000 #[test]
1001 fn maps_stdout_to_a_result() {
1002 let res = Command::new(
1003 "foo",
1004 MapStdoutAndErrStrings(|cap| -> Result<(u32, u32), Box<dyn std::error::Error>> {
1005 let out_res = cap.stdout.parse()?;
1006 let err_res = cap.stderr.parse()?;
1007 Ok((out_res, err_res))
1008 }),
1009 )
1010 .with_exec_replacement_callback(|_, _| {
1011 Ok(ExecResult {
1012 exit_status: 0.into(),
1013 stdout: Some("3241".into()),
1014 stderr: Some("1242".into()),
1015 })
1016 })
1017 .run()
1018 .unwrap();
1019
1020 assert_eq!(res, (3241u32, 1242u32));
1021 }
1022
1023 #[test]
1024 fn mapping_stdout_to_a_result_can_fail() {
1025 Command::new(
1026 "foo",
1027 MapStdoutAndErrStrings(|_| -> Result<u32, Box<dyn std::error::Error>> {
1028 Err("yes this fails")?
1029 }),
1030 )
1031 .with_exec_replacement_callback(|_, _| {
1032 Ok(ExecResult {
1033 exit_status: 0.into(),
1034 stdout: Some(Vec::new()),
1035 stderr: Some(Vec::new()),
1036 })
1037 })
1038 .run()
1039 .unwrap_err();
1040 }
1041 }
1042
1043 }