core/internal/domain/village.go

359 lines
6.8 KiB
Go

package domain
import (
"cmp"
"fmt"
"math"
"net/url"
"slices"
"time"
)
const (
villageNameMinLength = 1
villageNameMaxLength = 150
villageContinentMinLength = 1
villageContinentMaxLength = 5
)
type Village struct {
id int
serverKey string
name string
points int
x int
y int
continent string
bonus int
playerID int
profileURL *url.URL
createdAt time.Time
}
const villageModelName = "Village"
// UnmarshalVillageFromDatabase unmarshals Village from the database.
//
// It should be used only for unmarshalling from the database!
// You can't use UnmarshalVillageFromDatabase as constructor - It may put domain into the invalid state!
func UnmarshalVillageFromDatabase(
id int,
serverKey string,
name string,
points int,
x int,
y int,
continent string,
bonus int,
playerID int,
rawProfileURL string,
createdAt time.Time,
) (Village, error) {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return Village{}, ValidationError{
Model: villageModelName,
Field: "id",
Err: err,
}
}
if err := validateServerKey(serverKey); err != nil {
return Village{}, ValidationError{
Model: villageModelName,
Field: "serverKey",
Err: err,
}
}
profileURL, err := parseURL(rawProfileURL)
if err != nil {
return Village{}, ValidationError{
Model: villageModelName,
Field: "profileURL",
Err: err,
}
}
return Village{
id: id,
serverKey: serverKey,
name: name,
points: points,
x: x,
y: y,
continent: continent,
bonus: bonus,
playerID: playerID,
profileURL: profileURL,
createdAt: createdAt,
}, nil
}
func (v Village) ID() int {
return v.id
}
func (v Village) ServerKey() string {
return v.serverKey
}
func (v Village) Name() string {
return v.name
}
func (v Village) Points() int {
return v.points
}
func (v Village) X() int {
return v.x
}
func (v Village) Y() int {
return v.y
}
func (v Village) Continent() string {
return v.continent
}
func (v Village) Bonus() int {
return v.bonus
}
func (v Village) PlayerID() int {
return v.playerID
}
func (v Village) ProfileURL() *url.URL {
return v.profileURL
}
func (v Village) CreatedAt() time.Time {
return v.createdAt
}
func (v Village) Base() BaseVillage {
return BaseVillage{
id: v.id,
name: v.name,
points: v.points,
x: v.x,
y: v.y,
continent: v.continent,
bonus: v.bonus,
playerID: v.playerID,
profileURL: v.profileURL,
}
}
type Villages []Village
// Delete finds all villages with the given serverKey that are not in the given slice with active villages
// and returns their ids. Both slices must be sorted in ascending order by ID
// + if vs contains villages from different servers. they must be sorted in ascending order by server key.
func (vs Villages) Delete(serverKey string, active BaseVillages) []int {
// villages are deleted now and then, there is no point in prereallocating these slices
//nolint:prealloc
var toDelete []int
for _, v := range vs {
if v.ServerKey() != serverKey {
continue
}
_, found := slices.BinarySearchFunc(active, v, func(a BaseVillage, b Village) int {
return cmp.Compare(a.ID(), b.ID())
})
if found {
continue
}
toDelete = append(toDelete, v.ID())
}
return toDelete
}
type CreateVillageParams struct {
base BaseVillage
serverKey string
}
const createVillageParamsModelName = "CreateVillageParams"
func NewCreateVillageParams(serverKey string, villages BaseVillages) ([]CreateVillageParams, error) {
if err := validateServerKey(serverKey); err != nil {
return nil, ValidationError{
Model: createVillageParamsModelName,
Field: "serverKey",
Err: err,
}
}
params := make([]CreateVillageParams, 0, len(villages))
for i, v := range villages {
if v.IsZero() {
return nil, fmt.Errorf("villages[%d] is an empty struct", i)
}
params = append(params, CreateVillageParams{
base: v,
serverKey: serverKey,
})
}
return params, nil
}
func (params CreateVillageParams) Base() BaseVillage {
return params.base
}
func (params CreateVillageParams) ServerKey() string {
return params.serverKey
}
type VillageSort uint8
const (
VillageSortIDASC VillageSort = iota + 1
VillageSortIDDESC
VillageSortServerKeyASC
VillageSortServerKeyDESC
)
type ListVillagesParams struct {
ids []int
idGT NullInt
serverKeys []string
sort []VillageSort
limit int
offset int
}
const (
VillageListMaxLimit = 500
listVillagesParamsModelName = "ListVillagesParams"
)
func NewListVillagesParams() ListVillagesParams {
return ListVillagesParams{
sort: []VillageSort{
VillageSortServerKeyASC,
VillageSortIDASC,
},
limit: VillageListMaxLimit,
}
}
func (params *ListVillagesParams) IDs() []int {
return params.ids
}
func (params *ListVillagesParams) SetIDs(ids []int) error {
for i, id := range ids {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return SliceElementValidationError{
Model: listVillagesParamsModelName,
Field: "ids",
Index: i,
Err: err,
}
}
}
params.ids = ids
return nil
}
func (params *ListVillagesParams) IDGT() NullInt {
return params.idGT
}
func (params *ListVillagesParams) SetIDGT(idGT NullInt) error {
if idGT.Valid {
if err := validateIntInRange(idGT.V, 0, math.MaxInt); err != nil {
return ValidationError{
Model: listVillagesParamsModelName,
Field: "idGT",
Err: err,
}
}
}
params.idGT = idGT
return nil
}
func (params *ListVillagesParams) ServerKeys() []string {
return params.serverKeys
}
func (params *ListVillagesParams) SetServerKeys(serverKeys []string) error {
params.serverKeys = serverKeys
return nil
}
func (params *ListVillagesParams) Sort() []VillageSort {
return params.sort
}
const (
villageSortMinLength = 1
villageSortMaxLength = 2
)
func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
if err := validateSliceLen(sort, villageSortMinLength, villageSortMaxLength); err != nil {
return ValidationError{
Model: listVillagesParamsModelName,
Field: "sort",
Err: err,
}
}
params.sort = sort
return nil
}
func (params *ListVillagesParams) Limit() int {
return params.limit
}
func (params *ListVillagesParams) SetLimit(limit int) error {
if err := validateIntInRange(limit, 1, VillageListMaxLimit); err != nil {
return ValidationError{
Model: listVillagesParamsModelName,
Field: "limit",
Err: err,
}
}
params.limit = limit
return nil
}
func (params *ListVillagesParams) Offset() int {
return params.offset
}
func (params *ListVillagesParams) SetOffset(offset int) error {
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
return ValidationError{
Model: listVillagesParamsModelName,
Field: "offset",
Err: err,
}
}
params.offset = offset
return nil
}