package domain import ( "fmt" "math" "slices" "time" ) type Ennoblement struct { id int serverKey string villageID int newOwnerID int newTribeID int oldOwnerID int oldTribeID int points int createdAt time.Time } const ennoblementModelName = "Ennoblement" // UnmarshalEnnoblementFromDatabase unmarshals Ennoblement from the database. // // It should be used only for unmarshalling from the database! // You can't use UnmarshalEnnoblementFromDatabase as constructor - It may put domain into the invalid state! func UnmarshalEnnoblementFromDatabase( id int, serverKey string, villageID int, newOwnerID int, newTribeID int, oldOwnerID int, oldTribeID int, points int, createdAt time.Time, ) (Ennoblement, error) { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return Ennoblement{}, ValidationError{ Model: ennoblementModelName, Field: "id", Err: err, } } if err := validateServerKey(serverKey); err != nil { return Ennoblement{}, ValidationError{ Model: ennoblementModelName, Field: "serverKey", Err: err, } } return Ennoblement{ id: id, serverKey: serverKey, villageID: villageID, newOwnerID: newOwnerID, newTribeID: newTribeID, oldOwnerID: oldOwnerID, oldTribeID: oldTribeID, points: points, createdAt: createdAt, }, nil } func (e Ennoblement) ID() int { return e.id } func (e Ennoblement) ServerKey() string { return e.serverKey } func (e Ennoblement) VillageID() int { return e.villageID } func (e Ennoblement) NewOwnerID() int { return e.newOwnerID } func (e Ennoblement) NewTribeID() int { return e.newTribeID } func (e Ennoblement) OldOwnerID() int { return e.oldOwnerID } func (e Ennoblement) OldTribeID() int { return e.oldTribeID } func (e Ennoblement) Points() int { return e.points } func (e Ennoblement) CreatedAt() time.Time { return e.createdAt } func (e Ennoblement) WithRelations( village VillageMeta, newOwner NullPlayerMeta, newTribe NullTribeMeta, oldOwner NullPlayerMeta, oldTribe NullTribeMeta, ) EnnoblementWithRelations { return EnnoblementWithRelations{ ennoblement: e, village: village, newOwner: newOwner, newTribe: newTribe, oldOwner: oldOwner, oldTribe: oldTribe, } } func (e Ennoblement) ToCursor() (EnnoblementCursor, error) { return NewEnnoblementCursor(e.id, e.serverKey, e.createdAt) } func (e Ennoblement) Base() BaseEnnoblement { return BaseEnnoblement{ villageID: e.villageID, newOwnerID: e.newOwnerID, newTribeID: e.newTribeID, oldOwnerID: e.oldOwnerID, oldTribeID: e.oldTribeID, points: e.points, createdAt: e.createdAt, } } func (e Ennoblement) IsZero() bool { return e == Ennoblement{} } type Ennoblements []Ennoblement type EnnoblementWithRelations struct { ennoblement Ennoblement village VillageMeta newOwner NullPlayerMeta newTribe NullTribeMeta oldOwner NullPlayerMeta oldTribe NullTribeMeta } func (e EnnoblementWithRelations) Ennoblement() Ennoblement { return e.ennoblement } func (e EnnoblementWithRelations) Village() VillageMeta { return e.village } func (e EnnoblementWithRelations) NewOwner() NullPlayerMeta { return e.newOwner } func (e EnnoblementWithRelations) NewTribe() NullTribeMeta { return e.newTribe } func (e EnnoblementWithRelations) OldOwner() NullPlayerMeta { return e.oldOwner } func (e EnnoblementWithRelations) OldTribe() NullTribeMeta { return e.oldTribe } func (e EnnoblementWithRelations) IsZero() bool { return e.ennoblement.IsZero() } type EnnoblementsWithRelations []EnnoblementWithRelations type CreateEnnoblementParams struct { base BaseEnnoblement serverKey string } const createEnnoblementParamsModelName = "CreateEnnoblementParams" func NewCreateEnnoblementParams(serverKey string, ennoblements BaseEnnoblements) ([]CreateEnnoblementParams, error) { if err := validateServerKey(serverKey); err != nil { return nil, ValidationError{ Model: createEnnoblementParamsModelName, Field: "serverKey", Err: err, } } params := make([]CreateEnnoblementParams, 0, len(ennoblements)) for i, e := range ennoblements { if e.IsZero() { return nil, fmt.Errorf("ennoblements[%d] is an empty struct", i) } params = append(params, CreateEnnoblementParams{ base: e, serverKey: serverKey, }) } return params, nil } func (params CreateEnnoblementParams) Base() BaseEnnoblement { return params.base } func (params CreateEnnoblementParams) ServerKey() string { return params.serverKey } type EnnoblementSort uint8 const ( EnnoblementSortCreatedAtASC EnnoblementSort = iota + 1 EnnoblementSortCreatedAtDESC EnnoblementSortIDASC EnnoblementSortIDDESC EnnoblementSortServerKeyASC EnnoblementSortServerKeyDESC ) // IsInConflict returns true if two sorts can't be used together (e.g. EnnoblementSortIDASC and EnnoblementSortIDDESC). func (s EnnoblementSort) IsInConflict(s2 EnnoblementSort) bool { ss := []EnnoblementSort{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 EnnoblementSort) String() string { switch s { case EnnoblementSortCreatedAtASC: return "createdAt:ASC" case EnnoblementSortCreatedAtDESC: return "createdAt:DESC" case EnnoblementSortIDASC: return "id:ASC" case EnnoblementSortIDDESC: return "id:DESC" case EnnoblementSortServerKeyASC: return "serverKey:ASC" case EnnoblementSortServerKeyDESC: return "serverKey:DESC" default: return "unknown ennoblement sort" } } type EnnoblementCursor struct { id int serverKey string createdAt time.Time } const ennoblementCursorModelName = "EnnoblementCursor" func NewEnnoblementCursor(id int, serverKey string, createdAt time.Time) (EnnoblementCursor, error) { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return EnnoblementCursor{}, ValidationError{ Model: ennoblementCursorModelName, Field: "id", Err: err, } } if err := validateServerKey(serverKey); err != nil { return EnnoblementCursor{}, ValidationError{ Model: ennoblementCursorModelName, Field: "serverKey", Err: err, } } return EnnoblementCursor{ id: id, serverKey: serverKey, createdAt: createdAt, }, nil } //nolint:gocyclo func decodeEnnoblementCursor(encoded string) (EnnoblementCursor, error) { m, err := decodeCursor(encoded) if err != nil { return EnnoblementCursor{}, err } id, err := m.int("id") if err != nil { return EnnoblementCursor{}, ErrInvalidCursor } serverKey, err := m.string("serverKey") if err != nil { return EnnoblementCursor{}, ErrInvalidCursor } createdAt, err := m.time("createdAt") if err != nil { return EnnoblementCursor{}, ErrInvalidCursor } ec, err := NewEnnoblementCursor( id, serverKey, createdAt, ) if err != nil { return EnnoblementCursor{}, ErrInvalidCursor } return ec, nil } func (ec EnnoblementCursor) ID() int { return ec.id } func (ec EnnoblementCursor) ServerKey() string { return ec.serverKey } func (ec EnnoblementCursor) CreatedAt() time.Time { return ec.createdAt } func (ec EnnoblementCursor) IsZero() bool { return ec == EnnoblementCursor{} } func (ec EnnoblementCursor) Encode() string { if ec.IsZero() { return "" } return encodeCursor([]keyValuePair{ {"id", ec.id}, {"serverKey", ec.serverKey}, {"createdAt", ec.createdAt}, }) } type ListEnnoblementsParams struct { serverKeys []string villageIDs []int playerIDs []int // Ennoblement.NewOwnerID or Ennoblement.OldOwnerID tribeIDs []int // Ennoblement.NewTribeID or Ennoblement.OldTribeID since NullTime before NullTime sort []EnnoblementSort cursor EnnoblementCursor limit int } const ( EnnoblementListMaxLimit = 500 listEnnoblementsParamsModelName = "ListEnnoblementsParams" ) func NewListEnnoblementsParams() ListEnnoblementsParams { return ListEnnoblementsParams{ sort: []EnnoblementSort{ EnnoblementSortServerKeyASC, EnnoblementSortCreatedAtASC, EnnoblementSortIDASC, }, limit: EnnoblementListMaxLimit, } } func (params *ListEnnoblementsParams) ServerKeys() []string { return params.serverKeys } func (params *ListEnnoblementsParams) SetServerKeys(serverKeys []string) error { for i, sk := range serverKeys { if err := validateServerKey(sk); err != nil { return SliceElementValidationError{ Model: listEnnoblementsParamsModelName, Field: "serverKeys", Index: i, Err: err, } } } params.serverKeys = serverKeys return nil } func (params *ListEnnoblementsParams) VillageIDs() []int { return params.villageIDs } func (params *ListEnnoblementsParams) SetVillageIDs(villageIDs []int) error { for i, id := range villageIDs { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return SliceElementValidationError{ Model: listEnnoblementsParamsModelName, Field: "villageIDs", Index: i, Err: err, } } } params.villageIDs = villageIDs return nil } func (params *ListEnnoblementsParams) PlayerIDs() []int { return params.playerIDs } func (params *ListEnnoblementsParams) SetPlayerIDs(playerIDs []int) error { for i, id := range playerIDs { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return SliceElementValidationError{ Model: listEnnoblementsParamsModelName, Field: "playerIDs", Index: i, Err: err, } } } params.playerIDs = playerIDs return nil } func (params *ListEnnoblementsParams) TribeIDs() []int { return params.tribeIDs } func (params *ListEnnoblementsParams) SetTribeIDs(tribeIDs []int) error { for i, id := range tribeIDs { if err := validateIntInRange(id, 1, math.MaxInt); err != nil { return SliceElementValidationError{ Model: listEnnoblementsParamsModelName, Field: "tribeIDs", Index: i, Err: err, } } } params.tribeIDs = tribeIDs return nil } func (params *ListEnnoblementsParams) Since() NullTime { return params.since } func (params *ListEnnoblementsParams) SetSince(since NullTime) error { params.since = since return nil } func (params *ListEnnoblementsParams) Before() NullTime { return params.before } func (params *ListEnnoblementsParams) SetBefore(before NullTime) error { params.before = before return nil } func (params *ListEnnoblementsParams) Sort() []EnnoblementSort { return params.sort } const ( ennoblementSortMinLength = 1 ennoblementSortMaxLength = 3 ) func (params *ListEnnoblementsParams) SetSort(sort []EnnoblementSort) error { if err := validateSort(sort, ennoblementSortMinLength, ennoblementSortMaxLength); err != nil { return ValidationError{ Model: listEnnoblementsParamsModelName, Field: "sort", Err: err, } } params.sort = sort return nil } func (params *ListEnnoblementsParams) PrependSortString(sort []string) error { if err := validateSliceLen( sort, ennoblementSortMinLength, max(ennoblementSortMaxLength-len(params.sort), 0), ); err != nil { return ValidationError{ Model: listEnnoblementsParamsModelName, Field: "sort", Err: err, } } toPrepend := make([]EnnoblementSort, 0, len(sort)) for i, s := range sort { converted, err := newSortFromString( s, EnnoblementSortCreatedAtASC, EnnoblementSortCreatedAtDESC, ) if err != nil { return SliceElementValidationError{ Model: listEnnoblementsParamsModelName, Field: "sort", Index: i, Err: err, } } toPrepend = append(toPrepend, converted) } return params.SetSort(append(toPrepend, params.sort...)) } func (params *ListEnnoblementsParams) Cursor() EnnoblementCursor { return params.cursor } func (params *ListEnnoblementsParams) SetCursor(cursor EnnoblementCursor) error { params.cursor = cursor return nil } func (params *ListEnnoblementsParams) SetEncodedCursor(encoded string) error { decoded, err := decodeEnnoblementCursor(encoded) if err != nil { return ValidationError{ Model: listEnnoblementsParamsModelName, Field: "cursor", Err: err, } } params.cursor = decoded return nil } func (params *ListEnnoblementsParams) Limit() int { return params.limit } func (params *ListEnnoblementsParams) SetLimit(limit int) error { if err := validateIntInRange(limit, 1, EnnoblementListMaxLimit); err != nil { return ValidationError{ Model: listEnnoblementsParamsModelName, Field: "limit", Err: err, } } params.limit = limit return nil } type ListEnnoblementsResult struct { ennoblements Ennoblements self EnnoblementCursor next EnnoblementCursor } const listEnnoblementsResultModelName = "ListEnnoblementsResult" func NewListEnnoblementsResult(ennoblements Ennoblements, next Ennoblement) (ListEnnoblementsResult, error) { var err error res := ListEnnoblementsResult{ ennoblements: ennoblements, } if len(ennoblements) > 0 { res.self, err = ennoblements[0].ToCursor() if err != nil { return ListEnnoblementsResult{}, ValidationError{ Model: listEnnoblementsResultModelName, Field: "self", Err: err, } } } if !next.IsZero() { res.next, err = next.ToCursor() if err != nil { return ListEnnoblementsResult{}, ValidationError{ Model: listEnnoblementsResultModelName, Field: "next", Err: err, } } } return res, nil } func (res ListEnnoblementsResult) Ennoblements() Ennoblements { return res.ennoblements } func (res ListEnnoblementsResult) Self() EnnoblementCursor { return res.self } func (res ListEnnoblementsResult) Next() EnnoblementCursor { return res.next } type ListEnnoblementsWithRelationsResult struct { ennoblements EnnoblementsWithRelations self EnnoblementCursor next EnnoblementCursor } const listEnnoblementsWithRelationsResultModelName = "ListEnnoblementsWithRelationsResult" func NewListEnnoblementsWithRelationsResult( ennoblements EnnoblementsWithRelations, next EnnoblementWithRelations, ) (ListEnnoblementsWithRelationsResult, error) { var err error res := ListEnnoblementsWithRelationsResult{ ennoblements: ennoblements, } if len(ennoblements) > 0 { res.self, err = ennoblements[0].Ennoblement().ToCursor() if err != nil { return ListEnnoblementsWithRelationsResult{}, ValidationError{ Model: listEnnoblementsWithRelationsResultModelName, Field: "self", Err: err, } } } if !next.IsZero() { res.next, err = next.Ennoblement().ToCursor() if err != nil { return ListEnnoblementsWithRelationsResult{}, ValidationError{ Model: listEnnoblementsWithRelationsResultModelName, Field: "next", Err: err, } } } return res, nil } func (res ListEnnoblementsWithRelationsResult) Ennoblements() EnnoblementsWithRelations { return res.ennoblements } func (res ListEnnoblementsWithRelationsResult) Self() EnnoblementCursor { return res.self } func (res ListEnnoblementsWithRelationsResult) Next() EnnoblementCursor { return res.next }