package domain import ( "fmt" "math" "slices" "time" ) type PlayerSnapshot struct { id int playerID int serverKey string numVillages int points int rank int tribeID int od OpponentsDefeated date time.Time createdAt time.Time } const playerSnapshotModelName = "PlayerSnapshot" // UnmarshalPlayerSnapshotFromDatabase unmarshals PlayerSnapshot from the database. // // It should be used only for unmarshalling from the database! // You can't use UnmarshalPlayerSnapshotFromDatabase as constructor - It may put domain into the invalid state! func UnmarshalPlayerSnapshotFromDatabase( id int, playerID int, serverKey string, numVillages int, points int, rank int, tribeID int, od OpponentsDefeated, date time.Time, createdAt time.Time, ) (PlayerSnapshot, error) { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return PlayerSnapshot{}, ValidationError{ Model: playerSnapshotModelName, Field: "id", Err: err, } } if err := validateIntInRange(playerID, 1, math.MaxInt); err != nil { return PlayerSnapshot{}, ValidationError{ Model: playerSnapshotModelName, Field: "playerID", Err: err, } } if err := validateServerKey(serverKey); err != nil { return PlayerSnapshot{}, ValidationError{ Model: playerSnapshotModelName, Field: "serverKey", Err: err, } } return PlayerSnapshot{ id: id, playerID: playerID, serverKey: serverKey, numVillages: numVillages, points: points, rank: rank, tribeID: tribeID, od: od, date: date, createdAt: createdAt, }, nil } func (ps PlayerSnapshot) ID() int { return ps.id } func (ps PlayerSnapshot) PlayerID() int { return ps.playerID } func (ps PlayerSnapshot) ServerKey() string { return ps.serverKey } func (ps PlayerSnapshot) NumVillages() int { return ps.numVillages } func (ps PlayerSnapshot) Points() int { return ps.points } func (ps PlayerSnapshot) Rank() int { return ps.rank } func (ps PlayerSnapshot) TribeID() int { return ps.tribeID } func (ps PlayerSnapshot) OD() OpponentsDefeated { return ps.od } func (ps PlayerSnapshot) Date() time.Time { return ps.date } func (ps PlayerSnapshot) CreatedAt() time.Time { return ps.createdAt } type PlayerSnapshots []PlayerSnapshot type CreatePlayerSnapshotParams struct { playerID int serverKey string numVillages int points int rank int tribeID int od OpponentsDefeated date time.Time } func NewCreatePlayerSnapshotParams(players Players, date time.Time) ([]CreatePlayerSnapshotParams, error) { params := make([]CreatePlayerSnapshotParams, 0, len(players)) for i, p := range players { if p.IsZero() { return nil, fmt.Errorf("players[%d] is an empty struct", i) } if p.IsDeleted() { continue } params = append(params, CreatePlayerSnapshotParams{ playerID: p.ID(), serverKey: p.ServerKey(), numVillages: p.NumVillages(), points: p.Points(), rank: p.Rank(), tribeID: p.TribeID(), od: p.OD(), date: date, }) } return params, nil } func (params CreatePlayerSnapshotParams) PlayerID() int { return params.playerID } func (params CreatePlayerSnapshotParams) ServerKey() string { return params.serverKey } func (params CreatePlayerSnapshotParams) NumVillages() int { return params.numVillages } func (params CreatePlayerSnapshotParams) Points() int { return params.points } func (params CreatePlayerSnapshotParams) Rank() int { return params.rank } func (params CreatePlayerSnapshotParams) TribeID() int { return params.tribeID } func (params CreatePlayerSnapshotParams) OD() OpponentsDefeated { return params.od } func (params CreatePlayerSnapshotParams) Date() time.Time { return params.date } type PlayerSnapshotSort uint8 const ( PlayerSnapshotSortDateASC PlayerSnapshotSort = iota + 1 PlayerSnapshotSortDateDESC PlayerSnapshotSortIDASC PlayerSnapshotSortIDDESC PlayerSnapshotSortServerKeyASC PlayerSnapshotSortServerKeyDESC ) // IsInConflict returns true if two sorts can't be used together // (e.g. PlayerSnapshotSortIDASC and PlayerSnapshotSortIDDESC). func (s PlayerSnapshotSort) IsInConflict(s2 PlayerSnapshotSort) bool { ss := []PlayerSnapshotSort{s, s2} slices.Sort(ss) // ASC is always an odd number, DESC is always an even number return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1] } //nolint:gocyclo func (s PlayerSnapshotSort) String() string { switch s { case PlayerSnapshotSortDateASC: return "date:ASC" case PlayerSnapshotSortDateDESC: return "date:DESC" case PlayerSnapshotSortIDASC: return "id:ASC" case PlayerSnapshotSortIDDESC: return "id:DESC" case PlayerSnapshotSortServerKeyASC: return "serverKey:ASC" case PlayerSnapshotSortServerKeyDESC: return "serverKey:DESC" default: return "unknown player snapshot sort" } } const PlayerSnapshotListMaxLimit = 500 type ListPlayerSnapshotsParams struct { serverKeys []string sort []PlayerSnapshotSort limit int offset int } const listPlayerSnapshotsParamsModelName = "ListPlayerSnapshotsParams" func NewListPlayerSnapshotsParams() ListPlayerSnapshotsParams { return ListPlayerSnapshotsParams{ sort: []PlayerSnapshotSort{ PlayerSnapshotSortServerKeyASC, PlayerSnapshotSortDateASC, PlayerSnapshotSortIDASC, }, limit: PlayerSnapshotListMaxLimit, } } func (params *ListPlayerSnapshotsParams) ServerKeys() []string { return params.serverKeys } func (params *ListPlayerSnapshotsParams) SetServerKeys(serverKeys []string) error { for i, sk := range serverKeys { if err := validateServerKey(sk); err != nil { return SliceElementValidationError{ Model: listPlayerSnapshotsParamsModelName, Field: "serverKeys", Index: i, Err: err, } } } params.serverKeys = serverKeys return nil } func (params *ListPlayerSnapshotsParams) Sort() []PlayerSnapshotSort { return params.sort } const ( playerSnapshotSortMinLength = 1 playerSnapshotSortMaxLength = 3 ) func (params *ListPlayerSnapshotsParams) SetSort(sort []PlayerSnapshotSort) error { if err := validateSort(sort, playerSnapshotSortMinLength, playerSnapshotSortMaxLength); err != nil { return ValidationError{ Model: listPlayerSnapshotsParamsModelName, Field: "sort", Err: err, } } params.sort = sort return nil } func (params *ListPlayerSnapshotsParams) Limit() int { return params.limit } func (params *ListPlayerSnapshotsParams) SetLimit(limit int) error { if err := validateIntInRange(limit, 1, PlayerSnapshotListMaxLimit); err != nil { return ValidationError{ Model: listPlayerSnapshotsParamsModelName, Field: "limit", Err: err, } } params.limit = limit return nil } func (params *ListPlayerSnapshotsParams) Offset() int { return params.offset } func (params *ListPlayerSnapshotsParams) SetOffset(offset int) error { if err := validateIntInRange(offset, 0, math.MaxInt); err != nil { return ValidationError{ Model: listPlayerSnapshotsParamsModelName, Field: "offset", Err: err, } } params.offset = offset return nil }