@@ -60,6 +60,56 @@ impl<V: Value> Persister<V> for NoopPersister {
6060 fn load ( & self , token : Self :: Token ) -> Result < V , Self :: Error > { Ok ( token. 0 ) }
6161}
6262
63+ /// Handles cases where the transition either succeeds with a final result that ends the session, or hits a static condition and stays in the same state.
64+ /// State transition may also be a fatal error or transient error.
65+ pub struct MaybeSuccessTransitionWithNoResults < Event , SuccessValue , CurrentState , Err > (
66+ Result < AcceptOptionalTransition < Event , SuccessValue , CurrentState > , Rejection < Event , Err > > ,
67+ ) ;
68+
69+ impl < Event , SuccessValue , CurrentState , Err >
70+ MaybeSuccessTransitionWithNoResults < Event , SuccessValue , CurrentState , Err >
71+ {
72+ #[ allow( dead_code) ]
73+ #[ inline]
74+ pub ( crate ) fn fatal ( event : Event , error : Err ) -> Self {
75+ MaybeSuccessTransitionWithNoResults ( Err ( Rejection :: fatal ( event, error) ) )
76+ }
77+
78+ #[ allow( dead_code) ]
79+ #[ inline]
80+ pub ( crate ) fn transient ( error : Err ) -> Self {
81+ MaybeSuccessTransitionWithNoResults ( Err ( Rejection :: transient ( error) ) )
82+ }
83+
84+ #[ allow( dead_code) ]
85+ #[ inline]
86+ pub ( crate ) fn no_results ( current_state : CurrentState ) -> Self {
87+ MaybeSuccessTransitionWithNoResults ( Ok ( AcceptOptionalTransition :: NoResults ( current_state) ) )
88+ }
89+
90+ #[ allow( dead_code) ]
91+ #[ inline]
92+ pub ( crate ) fn success ( success_value : SuccessValue , event : Event ) -> Self {
93+ MaybeSuccessTransitionWithNoResults ( Ok ( AcceptOptionalTransition :: Success ( AcceptNextState (
94+ event,
95+ success_value,
96+ ) ) ) )
97+ }
98+
99+ pub fn save < P > (
100+ self ,
101+ persister : & P ,
102+ ) -> Result <
103+ OptionalTransitionOutcome < SuccessValue , CurrentState > ,
104+ PersistedError < Err , P :: InternalStorageError > ,
105+ >
106+ where
107+ P : SessionPersister < SessionEvent = Event > ,
108+ Err : std:: error:: Error ,
109+ {
110+ persister. save_maybe_no_results_success_transition ( self )
111+ }
112+ }
63113/// A transition that can result in a state transition, fatal error, transient error, or successfully have no results.
64114pub struct MaybeFatalTransitionWithNoResults < Event , NextState , CurrentState , Err > (
65115 Result < AcceptOptionalTransition < Event , NextState , CurrentState > , Rejection < Event , Err > > ,
@@ -465,6 +515,42 @@ trait InternalSessionPersister: SessionPersister {
465515 }
466516 }
467517
518+ /// Persists the outcome of a state transition that may result in one of the following:
519+ /// - A successful state transition, in which case the success value is returned and the session is closed.
520+ /// - No state change (stasis), where the current state is retained and nothing is persisted.
521+ /// - A transient error, which does not affect persistent storage and is returned to the caller.
522+ /// - A fatal error, which is persisted and returned to the caller.
523+ fn save_maybe_no_results_success_transition < SuccessValue , CurrentState , Err > (
524+ & self ,
525+ state_transition : MaybeSuccessTransitionWithNoResults <
526+ Self :: SessionEvent ,
527+ SuccessValue ,
528+ CurrentState ,
529+ Err ,
530+ > ,
531+ ) -> Result <
532+ OptionalTransitionOutcome < SuccessValue , CurrentState > ,
533+ PersistedError < Err , Self :: InternalStorageError > ,
534+ >
535+ where
536+ Err : std:: error:: Error ,
537+ {
538+ match state_transition. 0 {
539+ Ok ( AcceptOptionalTransition :: Success ( AcceptNextState ( event, success_value) ) ) => {
540+ self . save_event ( & event) . map_err ( InternalPersistedError :: Storage ) ?;
541+ self . close ( ) . map_err ( InternalPersistedError :: Storage ) ?;
542+ Ok ( OptionalTransitionOutcome :: Progress ( success_value) )
543+ }
544+ Ok ( AcceptOptionalTransition :: NoResults ( current_state) ) =>
545+ Ok ( OptionalTransitionOutcome :: Stasis ( current_state) ) ,
546+ Err ( Rejection :: Fatal ( fatal_rejection) ) => {
547+ self . handle_fatal_reject ( & fatal_rejection) ?;
548+ Err ( InternalPersistedError :: Fatal ( fatal_rejection. 1 ) . into ( ) )
549+ }
550+ Err ( Rejection :: Transient ( RejectTransient ( err) ) ) =>
551+ Err ( InternalPersistedError :: Transient ( err) . into ( ) ) ,
552+ }
553+ }
468554 /// Save a transition that can result in:
469555 /// - A successful state transition
470556 /// - No state change (no results)
@@ -925,6 +1011,84 @@ mod tests {
9251011 }
9261012 }
9271013
1014+ #[ test]
1015+ fn test_maybe_success_transition_with_no_results ( ) {
1016+ let event = InMemoryTestEvent ( "foo" . to_string ( ) ) ;
1017+ let error_event = InMemoryTestEvent ( "error event" . to_string ( ) ) ;
1018+ let current_state = "Current state" . to_string ( ) ;
1019+ let success_value = "Success value" . to_string ( ) ;
1020+ let test_cases: Vec <
1021+ TestCase <
1022+ OptionalTransitionOutcome < InMemoryTestState , InMemoryTestState > ,
1023+ PersistedError < InMemoryTestError , std:: convert:: Infallible > ,
1024+ > ,
1025+ > = vec ! [
1026+ // Success
1027+ TestCase {
1028+ expected_result: ExpectedResult {
1029+ events: vec![ event. clone( ) ] ,
1030+ is_closed: true ,
1031+ error: None ,
1032+ success: Some ( OptionalTransitionOutcome :: Progress ( success_value. clone( ) ) ) ,
1033+ } ,
1034+ test: Box :: new( move |persister| {
1035+ MaybeSuccessTransitionWithNoResults :: success(
1036+ success_value. clone( ) ,
1037+ event. clone( ) ,
1038+ )
1039+ . save( persister)
1040+ } ) ,
1041+ } ,
1042+ // No results
1043+ TestCase {
1044+ expected_result: ExpectedResult {
1045+ events: vec![ ] ,
1046+ is_closed: false ,
1047+ error: None ,
1048+ success: Some ( OptionalTransitionOutcome :: Stasis ( current_state. clone( ) ) ) ,
1049+ } ,
1050+ test: Box :: new( move |persister| {
1051+ MaybeSuccessTransitionWithNoResults :: no_results( current_state. clone( ) )
1052+ . save( persister)
1053+ } ) ,
1054+ } ,
1055+ // Transient error
1056+ TestCase {
1057+ expected_result: ExpectedResult {
1058+ events: vec![ ] ,
1059+ is_closed: false ,
1060+ error: Some ( InternalPersistedError :: Transient ( InMemoryTestError { } ) . into( ) ) ,
1061+ success: None ,
1062+ } ,
1063+ test: Box :: new( move |persister| {
1064+ MaybeSuccessTransitionWithNoResults :: transient( InMemoryTestError { } )
1065+ . save( persister)
1066+ } ) ,
1067+ } ,
1068+ // Fatal error
1069+ TestCase {
1070+ expected_result: ExpectedResult {
1071+ events: vec![ error_event. clone( ) ] ,
1072+ is_closed: true ,
1073+ error: Some ( InternalPersistedError :: Fatal ( InMemoryTestError { } ) . into( ) ) ,
1074+ success: None ,
1075+ } ,
1076+ test: Box :: new( move |persister| {
1077+ MaybeSuccessTransitionWithNoResults :: fatal(
1078+ error_event. clone( ) ,
1079+ InMemoryTestError { } ,
1080+ )
1081+ . save( persister)
1082+ } ) ,
1083+ } ,
1084+ ] ;
1085+
1086+ for test in test_cases {
1087+ let persister = InMemoryTestPersister :: default ( ) ;
1088+ do_test ( & persister, & test) ;
1089+ }
1090+ }
1091+
9281092 #[ test]
9291093 fn test_maybe_fatal_transition_with_no_results ( ) {
9301094 let event = InMemoryTestEvent ( "foo" . to_string ( ) ) ;
0 commit comments