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 }