@@ -12,7 +12,7 @@ macro_rules! Wrap {
1212 #[ derive( Debug ) ]
1313 pub struct $name {
1414 command: $command,
15- wrappers: :: indexmap:: IndexMap <:: std:: any:: TypeId , Box <dyn $wrapper>>,
15+ wrappers: :: indexmap:: IndexMap <:: std:: any:: TypeId , :: std :: cell :: RefCell < :: std :: option :: Option < Box <dyn $wrapper>> >>,
1616 }
1717
1818 impl $name {
@@ -62,70 +62,72 @@ macro_rules! Wrap {
6262 /// Returns `&mut self` for chaining.
6363 pub fn wrap<W : $wrapper + ' static >( & mut self , wrapper: W ) -> & mut Self {
6464 let typeid = :: std:: any:: TypeId :: of:: <W >( ) ;
65- let mut wrapper = Some ( Box :: new( wrapper) ) ;
65+ let boxed: Box <( dyn $wrapper + ' static ) > = Box :: new( wrapper) ;
66+ let mut wrapper = Some ( :: std:: cell:: RefCell :: new( Some ( boxed) ) ) ;
6667 let extant = self
6768 . wrappers
6869 . entry( typeid)
6970 . or_insert_with( || wrapper. take( ) . unwrap( ) ) ;
7071 if let Some ( wrapper) = wrapper {
71- extant. extend( wrapper) ;
72+ // UNWRAPs: we've just created those so we know they're Somes
73+ extant. get_mut( ) . as_mut( ) . unwrap( ) . extend( wrapper. into_inner( ) . unwrap( ) ) ;
7274 }
7375
7476 self
7577 }
7678
77- // poor man's try..finally block
78- #[ inline]
79- fn spawn_inner(
80- & self ,
81- command: & mut $command,
82- wrappers: & mut :: indexmap:: IndexMap <:: std:: any:: TypeId , Box <dyn $wrapper>>,
83- ) -> :: std:: io:: Result <Box <dyn $childer>> {
84- for ( id, wrapper) in wrappers. iter_mut( ) {
79+ /// Spawn the command, returning a `Child` that can be interacted with.
80+ ///
81+ /// In order, this runs all the `pre_spawn` hooks, then spawns the command, then runs
82+ /// all the `post_spawn` hooks, then stacks all the `wrap_child`s. As it returns a boxed
83+ /// trait object, only the methods from the trait are available directly; however you
84+ /// may downcast to the concrete type of the last applied wrapper if you need to.
85+ pub fn spawn( & mut self ) -> :: std:: io:: Result <Box <dyn $childer>> {
86+ // for each loop, we extract the active wrapper from its cell
87+ // so we can use it mutably independently from the self borrow
88+ // then we re-insert it; this happens regardless of the result
89+
90+ for ( id, cell) in & self . wrappers {
8591 #[ cfg( feature = "tracing" ) ]
8692 :: tracing:: debug!( ?id, "pre_spawn" ) ;
87- wrapper. pre_spawn( command, self ) ?;
93+ if let Some ( mut wrapper) = cell. take( ) {
94+ let mut command = :: std:: mem:: replace( & mut self . command, <$command>:: new( "" ) ) ;
95+ let ret = wrapper. pre_spawn( & mut command, self ) ;
96+ self . command = command;
97+ cell. replace( Some ( wrapper) ) ;
98+ ret?;
99+ }
88100 }
89101
90- let mut child = command. spawn( ) ?;
91- for ( id, wrapper ) in wrappers . iter_mut ( ) {
102+ let mut child = self . command. spawn( ) ?;
103+ for ( id, cell ) in & self . wrappers {
92104 #[ cfg( feature = "tracing" ) ]
93105 :: tracing:: debug!( ?id, "post_spawn" ) ;
94- wrapper. post_spawn( & mut child, self ) ?;
106+ if let Some ( mut wrapper) = cell. take( ) {
107+ let ret = wrapper. post_spawn( & mut child, self ) ;
108+ cell. replace( Some ( wrapper) ) ;
109+ ret?;
110+ }
95111 }
96112
97113 let mut child = Box :: new(
98114 #[ allow( clippy:: redundant_closure_call) ]
99115 $first_child_wrapper( child) ,
100116 ) as Box <dyn $childer>;
101117
102- for ( id, wrapper ) in wrappers . iter_mut ( ) {
118+ for ( id, cell ) in & self . wrappers {
103119 #[ cfg( feature = "tracing" ) ]
104120 :: tracing:: debug!( ?id, "wrap_child" ) ;
105- child = wrapper. wrap_child( child, self ) ?;
121+ if let Some ( mut wrapper) = cell. take( ) {
122+ let ret = wrapper. wrap_child( child, self ) ;
123+ cell. replace( Some ( wrapper) ) ;
124+ child = ret?;
125+ }
106126 }
107127
108128 Ok ( child)
109129 }
110130
111- /// Spawn the command, returning a `Child` that can be interacted with.
112- ///
113- /// In order, this runs all the `pre_spawn` hooks, then spawns the command, then runs
114- /// all the `post_spawn` hooks, then stacks all the `wrap_child`s. As it returns a boxed
115- /// trait object, only the methods from the trait are available directly; however you
116- /// may downcast to the concrete type of the last applied wrapper if you need to.
117- pub fn spawn( & mut self ) -> :: std:: io:: Result <Box <dyn $childer>> {
118- let mut command = :: std:: mem:: replace( & mut self . command, <$command>:: new( "" ) ) ;
119- let mut wrappers = :: std:: mem:: take( & mut self . wrappers) ;
120-
121- let res = self . spawn_inner( & mut command, & mut wrappers) ;
122-
123- self . command = command;
124- self . wrappers = wrappers;
125-
126- res
127- }
128-
129131 /// Check if a wrapper of a given type is present.
130132 pub fn has_wrap<W : $wrapper + ' static >( & self ) -> bool {
131133 let typeid = :: std:: any:: TypeId :: of:: <W >( ) ;
@@ -139,14 +141,23 @@ macro_rules! Wrap {
139141 ///
140142 /// Returns `None` if the wrapper is not present. To merely check if a wrapper is
141143 /// present, use `has_wrap` instead.
142- pub fn get_wrap<W : $wrapper + ' static >( & self ) -> Option <& W > {
144+ ///
145+ /// Note that calling `.get_wrap()` to retrieve the current wrapper while within that
146+ /// wrapper's hooks will return `None`. As this is useless (you should trivially have
147+ /// access to the current wrapper), this is not considered a bug.
148+ pub fn get_wrap<W : $wrapper + ' static >( & self ) -> Option <:: std:: cell:: Ref <W >> {
143149 let typeid = :: std:: any:: TypeId :: of:: <W >( ) ;
144- self . wrappers. get( & typeid) . map( |w| {
145- let w_any = w as & dyn :: std:: any:: Any ;
146- w_any
147- . downcast_ref( )
148- . expect( "downcasting is guaranteed to succeed due to wrap()'s internals" )
149- } )
150+ self . wrappers. get( & typeid)
151+ . and_then( |cell| cell. try_borrow( ) . ok( )
152+ . and_then( |borrow| :: std:: cell:: Ref :: filter_map(
153+ borrow, |opt| opt. as_ref( ) . map( |w| {
154+ let w_any = w as & dyn :: std:: any:: Any ;
155+ w_any
156+ . downcast_ref( )
157+ . expect( "downcasting is guaranteed to succeed due to wrap()'s internals" )
158+ } )
159+ ) . ok( ) )
160+ )
150161 }
151162 }
152163
0 commit comments