| 
					
				 | 
			
			
				@@ -0,0 +1,100 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package fsm 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"context" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"time" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2Hook[T any] func(ctx context.Context, event M2Event[T]) error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2State[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Name() string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	SetAfterEnter([]M2Hook[T]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	AfterEnter() []M2Hook[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	SetBeforeLeave([]M2Hook[T]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	BeforeLeave() []M2Hook[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2Event[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Name() string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Data() T 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type RetryableError interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	RetryableError() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2Transition[T any] struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	guards, actions []M2Hook[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	next            State 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (mt *M2Transition[T]) SetGuards([]M2Hook[T])  {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (mt *M2Transition[T]) Guards() []M2Hook[T]    { return mt.guards } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (mt *M2Transition[T]) SetActions([]M2Hook[T]) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (mt *M2Transition[T]) Actions() []M2Hook[T]   { return mt.actions } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (mt *M2Transition[T]) Next() State            { return mt.next } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2Matrix[T any] map[M2State[T]]map[M2Event[T]]M2Transition[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// M2BackoffFunc returning <0 means the retry limit has been reached. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2BackoffFunc func() time.Duration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2FSM[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Name() string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	SetBackoff(fn M2BackoffFunc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	SetMatrix(mx M2Matrix[T]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Matrix() M2Matrix[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	StartState() M2State[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	EndState() M2State[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ErrorState() M2State[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2PushMachine[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2FSM[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Handle(context.Context, M2Event[T]) error // If ctx == nil, handling uses the machine context. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2PullMachine[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2FSM[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Start(ctx context.Context, events chan<- M2Event[T]) M2State[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type M2VerifiableMachine[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2FSM[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	IsReachable(s1, s2 M2State[T]) bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	IsStronglyConnected() bool // Minimal Complexity O(V+E) using DFS (Tarjan or Kosaraju) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m2BackoffInitialDelay    = time.Millisecond 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m2BackoffMaxAttempts     = 10 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m2DefaultBackoffAttempts = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// M2YoloBackoff retries indefinitely without delay. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2YoloBackoff M2BackoffFunc = func() time.Duration { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// M2NoBackoff retries immediately but only up to the maximum number of retries. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2NoBackoff M2BackoffFunc = func() time.Duration { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		m2DefaultBackoffAttempts++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if m2DefaultBackoffAttempts > m2BackoffMaxAttempts { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return -1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return M2YoloBackoff() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// M2ExponentialBackoff retries with an exponential delay and maximum number of retries. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	M2ExponentialBackoff M2BackoffFunc = func() time.Duration { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		d := M2NoBackoff() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if d < 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return d 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return (1 << m2DefaultBackoffAttempts) * m2BackoffInitialDelay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 |