Skip to main content

perforce_cli/
spawn.rs

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