Skip to main content

perforce_cli/
spawn.rs

1use std::process::Child;
2use std::process::Output;
3
4pub trait ParameterizedSpawn<T> {
5    type Output;
6    type Error;
7
8    fn spawn_with(&mut self, input: T) -> Result<Self::Output, Self::Error>;
9}
10
11pub trait SpawnExt: ParameterizedSpawn<()> {
12    fn spawn(&mut self) -> Result<Self::Output, Self::Error>;
13}
14
15impl<T> SpawnExt for T
16where
17    T: ParameterizedSpawn<()>,
18{
19    fn spawn(&mut self) -> Result<Self::Output, Self::Error> {
20        self.spawn_with(())
21    }
22}
23
24pub trait ParameterizedOutput<T> {
25    type Error;
26
27    fn output_with(&mut self, input: T) -> Result<Output, Self::Error>;
28}
29
30impl<T, I> ParameterizedOutput<I> for T
31where
32    T: ParameterizedSpawn<I, Output = Child, Error = std::io::Error>,
33{
34    type Error = std::io::Error;
35
36    fn output_with(&mut self, input: I) -> Result<Output, Self::Error> {
37        self.spawn_with(input)
38            .and_then(|child| child.wait_with_output())
39    }
40}
41
42pub trait OutputExt: SpawnExt {
43    fn output(&mut self) -> Result<Output, Self::Error>;
44}
45
46impl<T> OutputExt for T
47where
48    T: SpawnExt<Output = Child, Error = std::io::Error>,
49{
50    fn output(&mut self) -> Result<Output, Self::Error> {
51        self.spawn().and_then(|child| child.wait_with_output())
52    }
53}
54
55/// Generates `SpawnExtN<T1, …, TN>` + `OutputExtN<T1, …, TN>` traits and
56/// their blanket impls for a given arity N ≥ 1.
57///
58/// For N args, the input type must be `(T1, …, TN)`. The generated `spawn`
59/// method takes N separate parameters and passes them as a tuple to
60/// `spawn_with`; `output` does the same via `output_with`.
61macro_rules! spawnext_def {
62    (
63        $spawn_trait:ident,
64        $output_trait:ident,
65        $($param:ident : $T:ident),+
66    ) => {
67        #[allow(clippy::too_many_arguments)]
68        #[allow(unused_parens)]
69        pub trait $spawn_trait<$($T),+>: ParameterizedSpawn<($($T),+)> {
70            fn spawn(
71                &mut self,
72                $($param: $T),+
73            ) -> Result<Self::Output, Self::Error>;
74        }
75
76        #[allow(clippy::too_many_arguments)]
77        #[allow(unused_parens)]
78        impl<T, $($T),+> $spawn_trait<$($T),+> for T
79        where
80            T: ParameterizedSpawn<($($T),+)>,
81        {
82            fn spawn(
83                &mut self,
84                $($param: $T),+
85            ) -> Result<Self::Output, Self::Error> {
86                self.spawn_with(($($param),+))
87            }
88        }
89
90        #[allow(clippy::too_many_arguments)]
91        #[allow(unused_parens)]
92        pub trait $output_trait<$($T),+>: ParameterizedOutput<($($T),+)> {
93            fn output(
94                &mut self,
95                $($param: $T),+
96            ) -> Result<Output, Self::Error>;
97        }
98
99        #[allow(clippy::too_many_arguments)]
100        #[allow(unused_parens)]
101        impl<T, $($T),+> $output_trait<$($T),+> for T
102        where
103            T: ParameterizedOutput<($($T),+)>,
104        {
105            fn output(
106                &mut self,
107                $($param: $T),+
108            ) -> Result<Output, Self::Error> {
109                self.output_with(($($param),+))
110            }
111        }
112    };
113}
114
115spawnext_def!(SpawnExt1, OutputExt1, a: T1);
116spawnext_def!(SpawnExt2, OutputExt2, a: T1, b: T2);
117spawnext_def!(SpawnExt3, OutputExt3, a: T1, b: T2, c: T3);
118spawnext_def!(SpawnExt4, OutputExt4, a: T1, b: T2, c: T3, d: T4);
119spawnext_def!(SpawnExt5, OutputExt5, a: T1, b: T2, c: T3, d: T4, e: T5);
120spawnext_def!(SpawnExt6, OutputExt6, a: T1, b: T2, c: T3, d: T4, e: T5, f: T6);
121spawnext_def!(SpawnExt7, OutputExt7, a: T1, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7);
122spawnext_def!(SpawnExt8, OutputExt8, a: T1, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8);