package domain import ( "crypto/sha256" "encoding/hex" "fmt" "math" "regexp" "strings" ) type Village struct { ID int64 FullName string ProfileURL string Points int64 X int64 Y int64 Player NullPlayerMeta } func (v Village) Coords() string { return fmt.Sprintf("%d|%d", v.X, v.Y) } type VillageMeta struct { ID int64 FullName string ProfileURL string Player NullPlayerMeta } type VillageCoordsStringTooLongError struct { Str string Max int } var _ TranslatableError = VillageCoordsStringTooLongError{} func (e VillageCoordsStringTooLongError) Error() string { return fmt.Sprintf("coords string is too long (%d/%d)", len(e.Str), e.Max) } func (e VillageCoordsStringTooLongError) Slug() string { return "village-coords-string-too-long" } func (e VillageCoordsStringTooLongError) Params() map[string]any { return map[string]any{ "Str": e.Str, "Max": e.Max, } } type VillageCoordsNotFoundInStringError struct { Str string } var _ TranslatableError = VillageCoordsNotFoundInStringError{} func (e VillageCoordsNotFoundInStringError) Error() string { return fmt.Sprintf("no coords found in string: '%s'", e.Str) } func (e VillageCoordsNotFoundInStringError) Slug() string { return "village-coords-not-found-in-string" } func (e VillageCoordsNotFoundInStringError) Params() map[string]any { return map[string]any{ "Str": e.Str, } } type TranslateVillageCoordsParams struct { versionCode string serverKey string coords []string perPage int32 maxPage int32 sha256 string } var villageCoordsRegex = regexp.MustCompile(`(\d+)\|(\d+)`) const ( VillageCoordsStringMaxLength = 1000 translateVillageCoordsMinPerPage = 1 ) func NewTranslateVillageCoordsParams(versionCode, serverKey, coordsStr string, perPage int32) (TranslateVillageCoordsParams, error) { if versionCode == "" { return TranslateVillageCoordsParams{}, RequiredError{Field: "VersionCode"} } if serverKey == "" { return TranslateVillageCoordsParams{}, RequiredError{Field: "ServerKey"} } if perPage < translateVillageCoordsMinPerPage { return TranslateVillageCoordsParams{}, GreaterEqualThanError{ Field: "PerPage", Threshold: translateVillageCoordsMinPerPage, } } if len(coordsStr) > VillageCoordsStringMaxLength { return TranslateVillageCoordsParams{}, VillageCoordsStringTooLongError{ Max: VillageCoordsStringMaxLength, Str: coordsStr, } } coords := uniq(villageCoordsRegex.FindAllString(coordsStr, -1)) if len(coords) == 0 { return TranslateVillageCoordsParams{}, VillageCoordsNotFoundInStringError{ Str: coordsStr, } } sum256 := sha256.Sum256(fmt.Appendf(nil, "%s-%s-%s-%d", versionCode, serverKey, strings.Join(coords, ","), perPage)) return TranslateVillageCoordsParams{ versionCode: versionCode, serverKey: serverKey, coords: coords, perPage: perPage, maxPage: int32(math.Ceil(float64(len(coords)) / float64(perPage))), sha256: hex.EncodeToString(sum256[:]), }, nil } // UnmarshalTranslateVillageCoordsParamsFromDatabase unmarshals TranslateVillageCoordsParams from the database. // // It should be used only for unmarshalling from the database! // You can't use UnmarshalTranslateVillageCoordsParamsFromDatabase as constructor - It may put domain into the invalid state! func UnmarshalTranslateVillageCoordsParamsFromDatabase( versionCode string, serverKey string, coords []string, perPage int32, maxPage int32, sha256Hash string, ) (TranslateVillageCoordsParams, error) { if versionCode == "" { return TranslateVillageCoordsParams{}, RequiredError{Field: "VersionCode"} } if serverKey == "" { return TranslateVillageCoordsParams{}, RequiredError{Field: "ServerKey"} } if len(coords) == 0 { return TranslateVillageCoordsParams{}, LengthTooShortError{ Field: "Coords", Threshold: 1, } } if perPage < translateVillageCoordsMinPerPage { return TranslateVillageCoordsParams{}, GreaterEqualThanError{ Field: "PerPage", Threshold: translateVillageCoordsMinPerPage, } } if sha256Hash == "" { return TranslateVillageCoordsParams{}, RequiredError{Field: "SHA256"} } return TranslateVillageCoordsParams{ versionCode: versionCode, serverKey: serverKey, coords: coords, perPage: perPage, maxPage: maxPage, sha256: sha256Hash, }, nil } func (t TranslateVillageCoordsParams) VersionCode() string { return t.versionCode } func (t TranslateVillageCoordsParams) ServerKey() string { return t.serverKey } func (t TranslateVillageCoordsParams) Coords() []string { return t.coords } func (t TranslateVillageCoordsParams) PerPage() int32 { return t.perPage } func (t TranslateVillageCoordsParams) MaxPage() int32 { return t.maxPage } func (t TranslateVillageCoordsParams) SHA256() string { return t.sha256 } type TranslateVillageCoordsResult struct { Villages []Village NotFound []string HasNext bool HasPrev bool Page int32 MaxPage int32 ParamsSHA256 string } type TranslateVillageCoordsParamsNotFoundError struct { SHA256 string } var _ TranslatableError = TranslateVillageCoordsParamsNotFoundError{} func (e TranslateVillageCoordsParamsNotFoundError) Error() string { return fmt.Sprintf("params (SHA256=%s) not found", e.SHA256) } func (e TranslateVillageCoordsParamsNotFoundError) Slug() string { return "translate-village-coords-params-not-found" } func (e TranslateVillageCoordsParamsNotFoundError) Params() map[string]any { return map[string]any{ "SHA256": e.SHA256, } }