feat: game lobby service
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package invite
|
||||
|
||||
// Status identifies one lifecycle state of a Game Lobby invite record.
|
||||
type Status string
|
||||
|
||||
const (
|
||||
// StatusCreated reports that the invite was created by the private-game
|
||||
// owner and awaits invitee action.
|
||||
StatusCreated Status = "created"
|
||||
|
||||
// StatusRedeemed reports that the invitee redeemed the invite; a
|
||||
// membership record was created as part of the same operation.
|
||||
StatusRedeemed Status = "redeemed"
|
||||
|
||||
// StatusDeclined reports that the invitee declined the invite.
|
||||
StatusDeclined Status = "declined"
|
||||
|
||||
// StatusRevoked reports that the owner revoked the invite before the
|
||||
// invitee acted on it.
|
||||
StatusRevoked Status = "revoked"
|
||||
|
||||
// StatusExpired reports that the invite expired because the game
|
||||
// transitioned out of enrollment_open.
|
||||
StatusExpired Status = "expired"
|
||||
)
|
||||
|
||||
// IsKnown reports whether status belongs to the frozen invite status
|
||||
// vocabulary.
|
||||
func (status Status) IsKnown() bool {
|
||||
switch status {
|
||||
case StatusCreated, StatusRedeemed, StatusDeclined, StatusRevoked, StatusExpired:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsTerminal reports whether status can no longer accept lifecycle
|
||||
// transitions.
|
||||
func (status Status) IsTerminal() bool {
|
||||
switch status {
|
||||
case StatusRedeemed, StatusDeclined, StatusRevoked, StatusExpired:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// transitionKey stores one `(from, to)` pair in the allowed-transitions
|
||||
// table.
|
||||
type transitionKey struct {
|
||||
from Status
|
||||
to Status
|
||||
}
|
||||
|
||||
// allowedTransitions stores the set of permitted `(from, to)` status pairs.
|
||||
// It mirrors the state machine frozen in lobby/README.md Invite Lifecycle
|
||||
// section.
|
||||
var allowedTransitions = map[transitionKey]struct{}{
|
||||
{StatusCreated, StatusRedeemed}: {},
|
||||
{StatusCreated, StatusDeclined}: {},
|
||||
{StatusCreated, StatusRevoked}: {},
|
||||
{StatusCreated, StatusExpired}: {},
|
||||
}
|
||||
|
||||
// AllowedTransitions returns a copy of the `(from, to)` allowed-transitions
|
||||
// table used by Transition. The returned map is safe to mutate.
|
||||
func AllowedTransitions() map[Status][]Status {
|
||||
result := make(map[Status][]Status)
|
||||
for key := range allowedTransitions {
|
||||
result[key.from] = append(result[key.from], key.to)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Transition reports whether from may transition to next. The function
|
||||
// returns nil when the pair is permitted, and an *InvalidTransitionError
|
||||
// wrapping ErrInvalidTransition otherwise. It does not touch any store and
|
||||
// is safe to call from any layer.
|
||||
func Transition(from Status, next Status) error {
|
||||
if !from.IsKnown() || !next.IsKnown() {
|
||||
return &InvalidTransitionError{From: from, To: next}
|
||||
}
|
||||
if _, ok := allowedTransitions[transitionKey{from: from, to: next}]; !ok {
|
||||
return &InvalidTransitionError{From: from, To: next}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user