package application // Status identifies one lifecycle state of a Game Lobby application record. type Status string const ( // StatusSubmitted reports that the application was created by the // applicant and awaits admin decision. StatusSubmitted Status = "submitted" // StatusApproved reports that the admin accepted the application and // a membership record was created for the applicant. StatusApproved Status = "approved" // StatusRejected reports that the admin declined the application. // The applicant may submit a new application while enrollment is open. StatusRejected Status = "rejected" ) // IsKnown reports whether status belongs to the frozen application status // vocabulary. func (status Status) IsKnown() bool { switch status { case StatusSubmitted, StatusApproved, StatusRejected: return true default: return false } } // IsTerminal reports whether status can no longer accept lifecycle // transitions. func (status Status) IsTerminal() bool { switch status { case StatusApproved, StatusRejected: 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 Application // Lifecycle section. var allowedTransitions = map[transitionKey]struct{}{ {StatusSubmitted, StatusApproved}: {}, {StatusSubmitted, StatusRejected}: {}, } // 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 }