feat: add command domain invariants
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
||||
)
|
||||
|
||||
// CanTransition reports whether a public lifecycle change is legal. Duplicate
|
||||
// events are handled by sequence validation and are not state transitions.
|
||||
func CanTransition(from, to rvboxv1.CommandLifecycle) bool {
|
||||
switch from {
|
||||
case rvboxv1.CommandLifecycle_COMMAND_QUEUED:
|
||||
return to == rvboxv1.CommandLifecycle_COMMAND_DISPATCHED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_CANCELLED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_EXPIRED
|
||||
case rvboxv1.CommandLifecycle_COMMAND_DISPATCHED:
|
||||
return to == rvboxv1.CommandLifecycle_COMMAND_QUEUED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_ACCEPTED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_REJECTED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_CANCELLED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED
|
||||
case rvboxv1.CommandLifecycle_COMMAND_ACCEPTED:
|
||||
return to == rvboxv1.CommandLifecycle_COMMAND_RUNNING ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_REJECTED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_CANCELLED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED
|
||||
case rvboxv1.CommandLifecycle_COMMAND_RUNNING:
|
||||
return to == rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_FAILED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_TERMINATED ||
|
||||
to == rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateTransition rejects unspecified, repeated, terminal-origin, and
|
||||
// otherwise impossible public lifecycle changes.
|
||||
func ValidateTransition(from, to rvboxv1.CommandLifecycle) error {
|
||||
if CanTransition(from, to) {
|
||||
return nil
|
||||
}
|
||||
return NewError(
|
||||
rvboxv1.ControlError_PROTOCOL_ERROR,
|
||||
fmt.Sprintf("illegal command lifecycle transition %s -> %s", from, to),
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// IsTerminal reports whether no further public lifecycle transition is legal.
|
||||
func IsTerminal(state rvboxv1.CommandLifecycle) bool {
|
||||
switch state {
|
||||
case rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_FAILED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_TERMINATED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_CANCELLED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_EXPIRED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_REJECTED:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// LaunchPhase is durable client-only state between accepted and running.
|
||||
type LaunchPhase uint8
|
||||
|
||||
const (
|
||||
LaunchPhaseNone LaunchPhase = iota
|
||||
LaunchPhasePrepared
|
||||
LaunchPhaseAuthorized
|
||||
)
|
||||
|
||||
// CanAdvanceLaunchPhase permits only the one-way launch barrier.
|
||||
func CanAdvanceLaunchPhase(from, to LaunchPhase) bool {
|
||||
return (from == LaunchPhaseNone && to == LaunchPhasePrepared) ||
|
||||
(from == LaunchPhasePrepared && to == LaunchPhaseAuthorized)
|
||||
}
|
||||
|
||||
// MayRedispatch is false once command code may have been released.
|
||||
func MayRedispatch(phase LaunchPhase) bool {
|
||||
return phase == LaunchPhaseNone || phase == LaunchPhasePrepared
|
||||
}
|
||||
Reference in New Issue
Block a user