@@ -152,6 +152,149 @@ pub fn effect_impl(args: Option<Ident>, input: ItemEnum) -> TokenStream {
152152 }
153153 } ) ;
154154
155+ let test_ext_trait_ident = format_ident ! ( "{}TestExt" , enum_ident) ;
156+
157+ let test_ext_trait_methods = effects. clone ( ) . map ( |effect| {
158+ let effect_ident_str = effect. ident . to_string ( ) ;
159+ let effect_ident_snake = effect_ident_str. to_snake_case ( ) ;
160+ let operation = & effect. operation ;
161+ let expect_fn = Ident :: new ( & format ! ( "expect_{effect_ident_snake}" ) , Span :: call_site ( ) ) ;
162+ let expect_with_fn = Ident :: new (
163+ & format ! ( "expect_{effect_ident_snake}_with" ) ,
164+ Span :: call_site ( ) ,
165+ ) ;
166+ let expect_only_fn = Ident :: new (
167+ & format ! ( "expect_only_{effect_ident_snake}" ) ,
168+ Span :: call_site ( ) ,
169+ ) ;
170+ let expect_only_with_fn = Ident :: new (
171+ & format ! ( "expect_only_{effect_ident_snake}_with" ) ,
172+ Span :: call_site ( ) ,
173+ ) ;
174+ let resolve_fn = Ident :: new ( & format ! ( "resolve_{effect_ident_snake}" ) , Span :: call_site ( ) ) ;
175+ quote ! {
176+ fn #expect_fn( & mut self ) -> & mut Self ;
177+ fn #expect_with_fn<F >( & mut self , f: F ) -> & mut Self
178+ where
179+ F : :: core:: ops:: FnOnce ( & #operation) ;
180+ fn #expect_only_fn( & mut self ) ;
181+ fn #expect_only_with_fn<F >( & mut self , f: F )
182+ where
183+ F : :: core:: ops:: FnOnce ( & #operation) ;
184+ fn #resolve_fn<F >( & mut self , f: F ) -> & mut Self
185+ where
186+ F : :: core:: ops:: FnOnce ( & #operation)
187+ -> <#operation as :: crux_core:: capability:: Operation >:: Output ;
188+ }
189+ } ) ;
190+
191+ let test_ext_impl_methods = effects. clone ( ) . map ( |effect| {
192+ let effect_ident_str = effect. ident . to_string ( ) ;
193+ let effect_ident_snake = effect_ident_str. to_snake_case ( ) ;
194+ let operation = & effect. operation ;
195+ let expect_fn = Ident :: new ( & format ! ( "expect_{effect_ident_snake}" ) , Span :: call_site ( ) ) ;
196+ let expect_with_fn = Ident :: new (
197+ & format ! ( "expect_{effect_ident_snake}_with" ) ,
198+ Span :: call_site ( ) ,
199+ ) ;
200+ let expect_only_fn = Ident :: new (
201+ & format ! ( "expect_only_{effect_ident_snake}" ) ,
202+ Span :: call_site ( ) ,
203+ ) ;
204+ let expect_only_with_fn = Ident :: new (
205+ & format ! ( "expect_only_{effect_ident_snake}_with" ) ,
206+ Span :: call_site ( ) ,
207+ ) ;
208+ let resolve_fn = Ident :: new ( & format ! ( "resolve_{effect_ident_snake}" ) , Span :: call_site ( ) ) ;
209+ let no_more_msg = format ! ( "expected {effect_ident_str} effect but no more effects remain" ) ;
210+ quote ! {
211+ #[ track_caller]
212+ fn #expect_fn( & mut self ) -> & mut Self {
213+ let effect = self . effects( ) . next( )
214+ . unwrap_or_else( || panic!( #no_more_msg) ) ;
215+ let _ = effect. #expect_fn( ) ;
216+ self
217+ }
218+
219+ #[ track_caller]
220+ fn #expect_with_fn<F >( & mut self , f: F ) -> & mut Self
221+ where
222+ F : :: core:: ops:: FnOnce ( & #operation) ,
223+ {
224+ let effect = self . effects( ) . next( )
225+ . unwrap_or_else( || panic!( #no_more_msg) ) ;
226+ let req = effect. #expect_fn( ) ;
227+ f( & req. operation) ;
228+ self
229+ }
230+
231+ #[ track_caller]
232+ fn #expect_only_fn( & mut self ) {
233+ let effect = self . effects( ) . next( )
234+ . unwrap_or_else( || panic!( #no_more_msg) ) ;
235+ let _ = effect. #expect_fn( ) ;
236+ self . expect_no_effect_or_events( ) ;
237+ }
238+
239+ #[ track_caller]
240+ fn #expect_only_with_fn<F >( & mut self , f: F )
241+ where
242+ F : :: core:: ops:: FnOnce ( & #operation) ,
243+ {
244+ let effect = self . effects( ) . next( )
245+ . unwrap_or_else( || panic!( #no_more_msg) ) ;
246+ let req = effect. #expect_fn( ) ;
247+ f( & req. operation) ;
248+ self . expect_no_effect_or_events( ) ;
249+ }
250+
251+ #[ track_caller]
252+ fn #resolve_fn<F >( & mut self , f: F ) -> & mut Self
253+ where
254+ F : :: core:: ops:: FnOnce ( & #operation)
255+ -> <#operation as :: crux_core:: capability:: Operation >:: Output ,
256+ {
257+ let effect = self . effects( ) . next( )
258+ . unwrap_or_else( || panic!( #no_more_msg) ) ;
259+ let mut req = effect. #expect_fn( ) ;
260+ let output = f( & req. operation) ;
261+ req. resolve( output) . expect( "resolve failed" ) ;
262+ self
263+ }
264+ }
265+ } ) ;
266+
267+ let test_ext = quote ! {
268+ pub trait #test_ext_trait_ident<Event >
269+ where
270+ Event : :: core:: marker:: Send + ' static ,
271+ {
272+ #( #test_ext_trait_methods) *
273+
274+ fn then_event<F >( & mut self , f: F ) -> & mut Self
275+ where
276+ F : :: core:: ops:: FnOnce ( & Event ) ;
277+ }
278+
279+ impl <Event > #test_ext_trait_ident<Event > for :: crux_core:: Command <#enum_ident, Event >
280+ where
281+ Event : :: core:: marker:: Send + ' static ,
282+ {
283+ #( #test_ext_impl_methods) *
284+
285+ #[ track_caller]
286+ fn then_event<F >( & mut self , f: F ) -> & mut Self
287+ where
288+ F : :: core:: ops:: FnOnce ( & Event ) ,
289+ {
290+ let ev = self . events( ) . next( )
291+ . unwrap_or_else( || panic!( "expected an event but got none" ) ) ;
292+ f( & ev) ;
293+ self
294+ }
295+ }
296+ } ;
297+
155298 let type_gen = match typegen_kind {
156299 TypegenKind :: Serde => {
157300 let effect_gen = effects. map ( |effect| {
@@ -252,6 +395,8 @@ pub fn effect_impl(args: Option<Ident>, input: ItemEnum) -> TokenStream {
252395
253396 #( #filters) *
254397
398+ #test_ext
399+
255400 #type_gen
256401
257402 }
0 commit comments