@@ -15,14 +15,20 @@ use crate::{
15
15
/// This is a custom command wrapper that simplifies working with commands and makes it easier to
16
16
/// ensure that we check the exit status of executed processes.
17
17
///
18
- /// # A [`Command`] must be executed
18
+ /// # A [`Command`] must be executed exactly once
19
19
///
20
20
/// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If
21
21
/// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test
22
22
/// to panic. Execution methods [`run`] and [`run_fail`] will defuse the drop bomb. A test
23
23
/// containing constructed but never executed commands is dangerous because it can give a false
24
24
/// sense of confidence.
25
25
///
26
+ /// Each [`Command`] invocation can also only be executed once, because we want to enforce
27
+ /// `std{in,out,err}` config via [`std::process::Stdio`] but [`std::process::Stdio`] is not
28
+ /// cloneable.
29
+ ///
30
+ /// In this sense, [`Command`] exhibits linear type semantics but enforced at run-time.
31
+ ///
26
32
/// [`run`]: Self::run
27
33
/// [`run_fail`]: Self::run_fail
28
34
/// [`run_unchecked`]: Self::run_unchecked
@@ -37,7 +43,9 @@ pub struct Command {
37
43
stdout : Option < Stdio > ,
38
44
stderr : Option < Stdio > ,
39
45
46
+ // Emulate linear type semantics.
40
47
drop_bomb : DropBomb ,
48
+ already_executed : bool ,
41
49
}
42
50
43
51
impl Command {
@@ -51,6 +59,7 @@ impl Command {
51
59
stdin : None ,
52
60
stdout : None ,
53
61
stderr : None ,
62
+ already_executed : false ,
54
63
}
55
64
}
56
65
@@ -177,6 +186,12 @@ impl Command {
177
186
178
187
#[ track_caller]
179
188
fn command_output ( & mut self ) -> CompletedProcess {
189
+ if self . already_executed {
190
+ panic ! ( "command was already executed" ) ;
191
+ } else {
192
+ self . already_executed = true ;
193
+ }
194
+
180
195
self . drop_bomb . defuse ( ) ;
181
196
// let's make sure we piped all the input and outputs
182
197
self . cmd . stdin ( self . stdin . take ( ) . unwrap_or ( Stdio :: piped ( ) ) ) ;
0 commit comments