@@ -37,9 +37,14 @@ enum CommandState<'a> {
3737        stdout :  OutputMode , 
3838        stderr :  OutputMode , 
3939        executed_at :  & ' a  Location < ' a > , 
40+         cache_key :  Option < CommandCacheKey > , 
4041    } , 
4142} 
4243
44+ pub  struct  DeferredCommand < ' a >  { 
45+     state :  CommandState < ' a > , 
46+ } 
47+ 
4348impl  CommandCache  { 
4449    pub  fn  new ( )  -> Self  { 
4550        Self  {  cache :  Mutex :: new ( HashMap :: new ( ) )  } 
@@ -122,13 +127,33 @@ impl ExecutionContext {
122127        stdout :  OutputMode , 
123128        stderr :  OutputMode , 
124129    )  -> DeferredCommand < ' a >  { 
130+         let  cache_key = command. cache_key ( ) ; 
131+ 
132+         if  let  Some ( cached_output)  = cache_key. as_ref ( ) . and_then ( |key| self . command_cache . get ( key) ) 
133+         { 
134+             command. mark_as_executed ( ) ; 
135+ 
136+             self . verbose ( || println ! ( "Cache hit: {:?}" ,  command) ) ; 
137+ 
138+             return  DeferredCommand  {  state :  CommandState :: Cached ( cached_output)  } ; 
139+         } 
140+ 
125141        command. mark_as_executed ( ) ; 
126142
127143        let  created_at = command. get_created_location ( ) ; 
128144        let  executed_at = std:: panic:: Location :: caller ( ) ; 
129145
130146        if  self . dry_run ( )  && !command. run_always  { 
131-             return  DeferredCommand  {  process :  None ,  stdout,  stderr,  command,  executed_at } ; 
147+             return  DeferredCommand  { 
148+                 state :  CommandState :: Deferred  { 
149+                     process :  None , 
150+                     command, 
151+                     stdout, 
152+                     stderr, 
153+                     executed_at, 
154+                     cache_key, 
155+                 } , 
156+             } ; 
132157        } 
133158
134159        #[ cfg( feature = "tracing" ) ]  
@@ -144,7 +169,16 @@ impl ExecutionContext {
144169
145170        let  child = cmd. spawn ( ) ; 
146171
147-         DeferredCommand  {  process :  Some ( child) ,  stdout,  stderr,  command,  executed_at } 
172+         DeferredCommand  { 
173+             state :  CommandState :: Deferred  { 
174+                 process :  Some ( child) , 
175+                 command, 
176+                 stdout, 
177+                 stderr, 
178+                 executed_at, 
179+                 cache_key, 
180+             } , 
181+         } 
148182    } 
149183
150184    /// Execute a command and return its output. 
@@ -212,33 +246,53 @@ impl AsRef<ExecutionContext> for ExecutionContext {
212246    } 
213247} 
214248
215- pub  struct  DeferredCommand < ' a >  { 
216-     process :  Option < Result < Child ,  std:: io:: Error > > , 
217-     command :  & ' a  mut  BootstrapCommand , 
218-     stdout :  OutputMode , 
219-     stderr :  OutputMode , 
220-     executed_at :  & ' a  Location < ' a > , 
221- } 
222- 
223249impl < ' a >  DeferredCommand < ' a >  { 
224250    pub  fn  wait_for_output ( mut  self ,  exec_ctx :  impl  AsRef < ExecutionContext > )  -> CommandOutput  { 
251+         match  self . state  { 
252+             CommandState :: Cached ( output)  => output, 
253+             CommandState :: Deferred  {  process,  command,  stdout,  stderr,  executed_at,  cache_key }  => { 
254+                 let  exec_ctx = exec_ctx. as_ref ( ) ; 
255+ 
256+                 let  output =
257+                     Self :: execute_process ( process,  command,  stdout,  stderr,  executed_at,  exec_ctx) ; 
258+ 
259+                 if  ( !exec_ctx. dry_run ( )  || command. run_always ) 
260+                     && cache_key. is_some ( ) 
261+                     && output. status ( ) . is_some ( ) 
262+                 { 
263+                     exec_ctx. command_cache . insert ( cache_key. unwrap ( ) ,  output. clone ( ) ) ; 
264+                 } 
265+ 
266+                 output
267+             } 
268+         } 
269+     } 
270+ 
271+     pub  fn  execute_process ( 
272+         mut  process :  Option < Result < Child ,  std:: io:: Error > > , 
273+         command :  & mut  BootstrapCommand , 
274+         stdout :  OutputMode , 
275+         stderr :  OutputMode , 
276+         executed_at :  & ' a  std:: panic:: Location < ' a > , 
277+         exec_ctx :  & ExecutionContext , 
278+     )  -> CommandOutput  { 
225279        let  exec_ctx = exec_ctx. as_ref ( ) ; 
226280
227-         let  process = match  self . process . take ( )  { 
281+         let  process = match  process. take ( )  { 
228282            Some ( p)  => p, 
229283            None  => return  CommandOutput :: default ( ) , 
230284        } ; 
231285
232-         let  created_at = self . command . get_created_location ( ) ; 
233-         let  executed_at = self . executed_at ; 
286+         let  created_at = command. get_created_location ( ) ; 
287+         let  executed_at = executed_at; 
234288
235289        let  mut  message = String :: new ( ) ; 
236290
237291        let  output = match  process { 
238292            Ok ( child)  => match  child. wait_with_output ( )  { 
239293                Ok ( result)  if  result. status . success ( )  => { 
240294                    // Successful execution 
241-                     CommandOutput :: from_output ( result,  self . stdout ,  self . stderr ) 
295+                     CommandOutput :: from_output ( result,  stdout,  stderr) 
242296                } 
243297                Ok ( result)  => { 
244298                    // Command ran but failed 
@@ -251,16 +305,16 @@ Command {:?} did not execute successfully.
251305Expected success, got {} 
252306Created at: {created_at} 
253307Executed at: {executed_at}"# , 
254-                         self . command,  result. status, 
308+                         command,  result. status, 
255309                    ) 
256310                    . unwrap ( ) ; 
257311
258-                     let  output = CommandOutput :: from_output ( result,  self . stdout ,  self . stderr ) ; 
312+                     let  output = CommandOutput :: from_output ( result,  stdout,  stderr) ; 
259313
260-                     if  self . stdout . captures ( )  { 
314+                     if  stdout. captures ( )  { 
261315                        writeln ! ( message,  "\n STDOUT ----\n {}" ,  output. stdout( ) . trim( ) ) . unwrap ( ) ; 
262316                    } 
263-                     if  self . stderr . captures ( )  { 
317+                     if  stderr. captures ( )  { 
264318                        writeln ! ( message,  "\n STDERR ----\n {}" ,  output. stderr( ) . trim( ) ) . unwrap ( ) ; 
265319                    } 
266320
@@ -274,11 +328,11 @@ Executed at: {executed_at}"#,
274328                        message, 
275329                        "\n \n Command {:?} did not execute successfully.\  
276330                         \n It was not possible to execute the command: {e:?}", 
277-                         self . command
331+                         command
278332                    ) 
279333                    . unwrap ( ) ; 
280334
281-                     CommandOutput :: did_not_start ( self . stdout ,  self . stderr ) 
335+                     CommandOutput :: did_not_start ( stdout,  stderr) 
282336                } 
283337            } , 
284338            Err ( e)  => { 
@@ -289,16 +343,16 @@ Executed at: {executed_at}"#,
289343                    message, 
290344                    "\n \n Command {:?} did not execute successfully.\  
291345                     \n It was not possible to execute the command: {e:?}", 
292-                     self . command
346+                     command
293347                ) 
294348                . unwrap ( ) ; 
295349
296-                 CommandOutput :: did_not_start ( self . stdout ,  self . stderr ) 
350+                 CommandOutput :: did_not_start ( stdout,  stderr) 
297351            } 
298352        } ; 
299353
300354        if  !output. is_success ( )  { 
301-             match  self . command . failure_behavior  { 
355+             match  command. failure_behavior  { 
302356                BehaviorOnFailure :: DelayFail  => { 
303357                    if  exec_ctx. fail_fast  { 
304358                        exec_ctx. fail ( & message,  output) ; 
0 commit comments