@@ -40,12 +40,30 @@ type StateMachine[D any] struct {
40
40
behavior StateMachineBehavior [D ]
41
41
mailbox gen.ProcessMailbox
42
42
43
+ // The specification for the StateMachine
43
44
spec StateMachineSpec [D ]
44
45
45
- currentState gen.Atom
46
- data D
46
+ // The state the StateMachine is currently in
47
+ currentState gen.Atom
48
+
49
+ // The data associated with the StateMachine
50
+ data D
51
+
52
+ // stateMessageHandlers maps states to the (asynchronous) handlers for the state.
53
+ // Key: State (gen.Atom) - The state for which the handler is registered.
54
+ // Value: Map of message type to the handler for that message.
55
+ // Key: The type of the message received (String).
56
+ // Value: The message handler (any). There is a compile-time guarantee
57
+ // that the handler is of type StateMessageHandler[D, M].
47
58
stateMessageHandlers map [gen.Atom ]map [string ]any
48
- stateCallHandlers map [gen.Atom ]map [string ]any
59
+
60
+ // stateCallHandlers maps states to the (synchronous) handlers for the state.
61
+ // Key: State (gen.Atom) - The state for which the handler is registered.
62
+ // Value: Map of message type to the handler for that message.
63
+ // Key: The type of the message received (String).
64
+ // Value: The message handler (any). There is a compile-time guarantee
65
+ // that the handler is of type StateCallHandler[D, M, R].
66
+ stateCallHandlers map [gen.Atom ]map [string ]any
49
67
}
50
68
51
69
type StateMessageHandler [D any , M any ] func (* StateMachine [D ], M ) error
@@ -67,8 +85,8 @@ func NewStateMachineSpec[D any](initialState gen.Atom, options ...Option[D]) Sta
67
85
stateMessageHandlers : make (map [gen.Atom ]map [string ]any ),
68
86
stateCallHandlers : make (map [gen.Atom ]map [string ]any ),
69
87
}
70
- for _ , cb := range options {
71
- cb (& spec )
88
+ for _ , opt := range options {
89
+ opt (& spec )
72
90
}
73
91
return spec
74
92
}
@@ -79,23 +97,23 @@ func WithData[D any](data D) Option[D] {
79
97
}
80
98
}
81
99
82
- func WithStateMessageHandler [D any , M any ](state gen.Atom , callback StateMessageHandler [D , M ]) Option [D ] {
83
- typeName := reflect .TypeOf ((* M )(nil )).Elem ().String ()
100
+ func WithStateMessageHandler [D any , M any ](state gen.Atom , handler StateMessageHandler [D , M ]) Option [D ] {
101
+ messageType := reflect .TypeOf ((* M )(nil )).Elem ().String ()
84
102
return func (s * StateMachineSpec [D ]) {
85
103
if _ , exists := s .stateMessageHandlers [state ]; exists == false {
86
104
s .stateMessageHandlers [state ] = make (map [string ]any )
87
105
}
88
- s.stateMessageHandlers [state ][typeName ] = callback
106
+ s.stateMessageHandlers [state ][messageType ] = handler
89
107
}
90
108
}
91
109
92
- func WithStateCallHandler [D any , M any , R any ](state gen.Atom , callback StateCallHandler [D , M , R ]) Option [D ] {
93
- typeName := reflect .TypeOf ((* M )(nil )).Elem ().String ()
110
+ func WithStateCallHandler [D any , M any , R any ](state gen.Atom , handler StateCallHandler [D , M , R ]) Option [D ] {
111
+ messageType := reflect .TypeOf ((* M )(nil )).Elem ().String ()
94
112
return func (s * StateMachineSpec [D ]) {
95
113
if _ , exists := s .stateCallHandlers [state ]; exists == false {
96
114
s .stateCallHandlers [state ] = make (map [string ]any )
97
115
}
98
- s.stateCallHandlers [state ][typeName ] = callback
116
+ s.stateCallHandlers [state ][messageType ] = handler
99
117
}
100
118
}
101
119
@@ -146,9 +164,10 @@ func (sm *StateMachine[D]) ProcessInit(process gen.Process, args ...any) (rr err
146
164
return err
147
165
}
148
166
149
- // set up callbacks
150
167
sm .currentState = spec .initialState
168
+ sm .data = spec .data
151
169
sm .stateMessageHandlers = spec .stateMessageHandlers
170
+ sm .stateCallHandlers = spec .stateCallHandlers
152
171
153
172
return nil
154
173
}
@@ -212,46 +231,24 @@ func (sm *StateMachine[D]) ProcessRun() (rr error) {
212
231
switch message .Type {
213
232
case gen .MailboxMessageTypeRegular :
214
233
// check if there is a handler for the message in the current state
215
- typeName := typeName (message )
216
- if callbackInterface , ok := sm .lookupMessageHandler (typeName ); ok == true {
217
- callbackValue := reflect .ValueOf (callbackInterface )
218
- smValue := reflect .ValueOf (sm )
219
- msgValue := reflect .ValueOf (message .Message )
220
-
221
- results := callbackValue .Call ([]reflect.Value {smValue , msgValue })
222
-
223
- if len (results ) > 0 && ! results [0 ].IsNil () {
224
- return results [0 ].Interface ().(error )
225
- }
226
- return nil
234
+ messageType := typeName (message )
235
+ handler , ok := sm .lookupMessageHandler (messageType )
236
+ if ok == false {
237
+ return fmt .Errorf ("No handler for message %s in state %s" , messageType , sm .currentState )
227
238
}
228
- return fmt . Errorf ( "Unsupported message %s for state %s" , typeName , sm . currentState )
239
+ return sm . invokeMessageHandler ( handler , message )
229
240
230
241
case gen .MailboxMessageTypeRequest :
231
242
var reason error
232
243
var result any
233
244
234
- sm .Log ().Info ("got request" )
235
-
236
245
// check if there is a handler for the call in the current state
237
- typeName := typeName (message )
238
- if callbackInterface , ok := sm .lookupMessageHandler (typeName ); ok == true {
239
- sm .Log ().Info ("found handler" )
240
-
241
- callbackValue := reflect .ValueOf (callbackInterface )
242
- smValue := reflect .ValueOf (sm )
243
- msgValue := reflect .ValueOf (message .Message )
244
-
245
- results := callbackValue .Call ([]reflect.Value {smValue , msgValue })
246
- if ! results [0 ].IsZero () {
247
- result = results [0 ].Interface ()
248
- }
249
- if ! results [1 ].IsNil () {
250
- reason = results [1 ].Interface ().(error )
251
- }
252
- } else {
253
- reason = fmt .Errorf ("Unsupported call %s for state %s" , typeName , sm .currentState )
246
+ messageType := typeName (message )
247
+ handler , ok := sm .lookupCallHandler (messageType )
248
+ if ok == false {
249
+ return fmt .Errorf ("No handler for message %s in state %s" , messageType , sm .currentState )
254
250
}
251
+ result , reason = sm .invokeCallHandler (handler , message )
255
252
256
253
if reason != nil {
257
254
// if reason is "normal" and we got response - send it before termination
@@ -260,9 +257,7 @@ func (sm *StateMachine[D]) ProcessRun() (rr error) {
260
257
}
261
258
return reason
262
259
}
263
-
264
260
// Note: we do not support async handling of sync request at the moment
265
-
266
261
sm .SendResponse (message .From , message .Ref , result )
267
262
268
263
case gen .MailboxMessageTypeEvent :
@@ -343,11 +338,45 @@ func (sm *StateMachine[D]) lookupMessageHandler(messageType string) (any, bool)
343
338
return nil , false
344
339
}
345
340
341
+ func (sm * StateMachine [D ]) invokeMessageHandler (handler any , message * gen.MailboxMessage ) error {
342
+ callbackValue := reflect .ValueOf (handler )
343
+ smValue := reflect .ValueOf (sm )
344
+ msgValue := reflect .ValueOf (message .Message )
345
+
346
+ results := callbackValue .Call ([]reflect.Value {smValue , msgValue })
347
+
348
+ if len (results ) > 0 && ! results [0 ].IsNil () {
349
+ return results [0 ].Interface ().(error )
350
+ }
351
+ return nil
352
+ }
353
+
346
354
func (sm * StateMachine [D ]) lookupCallHandler (messageType string ) (any , bool ) {
347
- if stateCallHandlers , exists := sm .stateMessageHandlers [sm .currentState ]; exists == true {
355
+ if stateCallHandlers , exists := sm .stateCallHandlers [sm .currentState ]; exists == true {
348
356
if callback , exists := stateCallHandlers [messageType ]; exists == true {
349
357
return callback , true
350
358
}
351
359
}
352
360
return nil , false
353
361
}
362
+
363
+ func (sm * StateMachine [D ]) invokeCallHandler (handler any , message * gen.MailboxMessage ) (any , error ) {
364
+ callbackValue := reflect .ValueOf (handler )
365
+ smValue := reflect .ValueOf (sm )
366
+ msgValue := reflect .ValueOf (message .Message )
367
+
368
+ results := callbackValue .Call ([]reflect.Value {smValue , msgValue })
369
+
370
+ if len (results ) != 2 {
371
+ sm .Log ().Panic ("StateMachine terminated. Panic reason: unexpected " +
372
+ "error when invoking call handler for %v" , typeName (message ))
373
+ }
374
+
375
+ if ! results [1 ].IsNil () {
376
+ err := results [1 ].Interface ().(error )
377
+ return nil , err
378
+ }
379
+
380
+ result := results [0 ].Interface ()
381
+ return result , nil
382
+ }
0 commit comments