398 lines
7.8 KiB
Go
398 lines
7.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
type ValidationError struct {
|
|
Model string
|
|
Field string
|
|
Err error
|
|
}
|
|
|
|
var _ ErrorWithParams = ValidationError{}
|
|
var _ ErrorWithPath = ValidationError{}
|
|
|
|
func (e ValidationError) Error() string {
|
|
prefix := e.Path().String()
|
|
|
|
if prefix != "" {
|
|
prefix += ": "
|
|
}
|
|
|
|
return prefix + e.Err.Error()
|
|
}
|
|
|
|
func (e ValidationError) Path() ErrorPathSegment {
|
|
return ErrorPathSegment{
|
|
Model: e.Model,
|
|
Field: e.Field,
|
|
Index: -1,
|
|
}
|
|
}
|
|
|
|
func (e ValidationError) Type() ErrorType {
|
|
var domainErr Error
|
|
if errors.As(e.Err, &domainErr) {
|
|
return domainErr.Type()
|
|
}
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
// errorcode:ignore
|
|
const errorCodeValidationFailed = "validation-failed"
|
|
|
|
func (e ValidationError) Code() string {
|
|
var domainErr Error
|
|
if errors.As(e.Err, &domainErr) {
|
|
return domainErr.Code()
|
|
}
|
|
return errorCodeValidationFailed
|
|
}
|
|
|
|
func (e ValidationError) Params() map[string]any {
|
|
var withParams ErrorWithParams
|
|
if ok := errors.As(e.Err, &withParams); !ok {
|
|
return nil
|
|
}
|
|
return withParams.Params()
|
|
}
|
|
|
|
func (e ValidationError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
type SliceElementValidationError struct {
|
|
Model string
|
|
Field string
|
|
Index int
|
|
Err error
|
|
}
|
|
|
|
var _ ErrorWithParams = SliceElementValidationError{}
|
|
var _ ErrorWithPath = SliceElementValidationError{}
|
|
|
|
func (e SliceElementValidationError) Error() string {
|
|
prefix := e.Path().String()
|
|
|
|
if prefix != "" {
|
|
prefix += ": "
|
|
}
|
|
|
|
return prefix + e.Err.Error()
|
|
}
|
|
|
|
func (e SliceElementValidationError) Path() ErrorPathSegment {
|
|
return ErrorPathSegment{
|
|
Model: e.Model,
|
|
Field: e.Field,
|
|
Index: e.Index,
|
|
}
|
|
}
|
|
|
|
func (e SliceElementValidationError) Type() ErrorType {
|
|
var domainErr Error
|
|
if errors.As(e.Err, &domainErr) {
|
|
return domainErr.Type()
|
|
}
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
// errorcode:ignore
|
|
const errorCodeSliceElementValidationFailed = "slice-element-validation-failed"
|
|
|
|
func (e SliceElementValidationError) Code() string {
|
|
var domainErr Error
|
|
if errors.As(e.Err, &domainErr) {
|
|
return domainErr.Code()
|
|
}
|
|
return errorCodeSliceElementValidationFailed
|
|
}
|
|
|
|
func (e SliceElementValidationError) Params() map[string]any {
|
|
var withParams ErrorWithParams
|
|
if ok := errors.As(e.Err, &withParams); !ok {
|
|
return nil
|
|
}
|
|
return withParams.Params()
|
|
}
|
|
|
|
func (e SliceElementValidationError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
type MinGreaterEqualError struct {
|
|
Min int
|
|
Current int
|
|
}
|
|
|
|
var _ ErrorWithParams = MinGreaterEqualError{}
|
|
|
|
func (e MinGreaterEqualError) Error() string {
|
|
return fmt.Sprintf("must be no less than %d (current: %d)", e.Min, e.Current)
|
|
}
|
|
|
|
func (e MinGreaterEqualError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeMinGreaterEqual = "min-greater-equal"
|
|
|
|
func (e MinGreaterEqualError) Code() string {
|
|
return errorCodeMinGreaterEqual
|
|
}
|
|
|
|
func (e MinGreaterEqualError) Params() map[string]any {
|
|
return map[string]any{
|
|
"Min": e.Min,
|
|
"Current": e.Current,
|
|
}
|
|
}
|
|
|
|
type MaxLessEqualError struct {
|
|
Max int
|
|
Current int
|
|
}
|
|
|
|
var _ ErrorWithParams = MaxLessEqualError{}
|
|
|
|
func (e MaxLessEqualError) Error() string {
|
|
return fmt.Sprintf("must be no greater than %d (current: %d)", e.Max, e.Current)
|
|
}
|
|
|
|
func (e MaxLessEqualError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeMaxLessEqual = "max-less-equal"
|
|
|
|
func (e MaxLessEqualError) Code() string {
|
|
return errorCodeMaxLessEqual
|
|
}
|
|
|
|
func (e MaxLessEqualError) Params() map[string]any {
|
|
return map[string]any{
|
|
"Max": e.Max,
|
|
"Current": e.Current,
|
|
}
|
|
}
|
|
|
|
type LenOutOfRangeError struct {
|
|
Min int
|
|
Max int
|
|
Current int
|
|
}
|
|
|
|
var _ ErrorWithParams = LenOutOfRangeError{}
|
|
|
|
func (e LenOutOfRangeError) Error() string {
|
|
return fmt.Sprintf("length must be between %d and %d (current length: %d)", e.Min, e.Max, e.Current)
|
|
}
|
|
|
|
func (e LenOutOfRangeError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeLenOutOfRange = "length-out-of-range"
|
|
|
|
func (e LenOutOfRangeError) Code() string {
|
|
return errorCodeLenOutOfRange
|
|
}
|
|
|
|
func (e LenOutOfRangeError) Params() map[string]any {
|
|
return map[string]any{
|
|
"Min": e.Min,
|
|
"Max": e.Max,
|
|
"Current": e.Current,
|
|
}
|
|
}
|
|
|
|
type InvalidURLError struct {
|
|
URL string
|
|
}
|
|
|
|
var _ ErrorWithParams = InvalidURLError{}
|
|
|
|
func (e InvalidURLError) Error() string {
|
|
return fmt.Sprintf("%s: invalid URL", e.URL)
|
|
}
|
|
|
|
func (e InvalidURLError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeInvalidURL = "invalid-url"
|
|
|
|
func (e InvalidURLError) Code() string {
|
|
return errorCodeInvalidURL
|
|
}
|
|
|
|
func (e InvalidURLError) Params() map[string]any {
|
|
return map[string]any{
|
|
"URL": e.URL,
|
|
}
|
|
}
|
|
|
|
type UnsupportedSortStringError struct {
|
|
Sort string
|
|
}
|
|
|
|
var _ ErrorWithParams = UnsupportedSortStringError{}
|
|
|
|
func (e UnsupportedSortStringError) Error() string {
|
|
return fmt.Sprintf("sort %s is unsupported", e.Sort)
|
|
}
|
|
|
|
func (e UnsupportedSortStringError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeUnsupportedSortString = "unsupported-sort-string"
|
|
|
|
func (e UnsupportedSortStringError) Code() string {
|
|
return errorCodeUnsupportedSortString
|
|
}
|
|
|
|
func (e UnsupportedSortStringError) Params() map[string]any {
|
|
return map[string]any{
|
|
"Sort": e.Sort,
|
|
}
|
|
}
|
|
|
|
type SortConflictError struct {
|
|
Sort [2]string
|
|
}
|
|
|
|
var _ ErrorWithParams = SortConflictError{}
|
|
|
|
func (e SortConflictError) Error() string {
|
|
return fmt.Sprintf("sort values %s are in conflict and can't be used together", e.Sort[0]+" and "+e.Sort[1])
|
|
}
|
|
|
|
func (e SortConflictError) Type() ErrorType {
|
|
return ErrorTypeIncorrectInput
|
|
}
|
|
|
|
const errorCodeSortConflict = "sort-conflict"
|
|
|
|
func (e SortConflictError) Code() string {
|
|
return errorCodeSortConflict
|
|
}
|
|
|
|
func (e SortConflictError) Params() map[string]any {
|
|
return map[string]any{
|
|
"Sort": e.Sort,
|
|
}
|
|
}
|
|
|
|
// This error code is returned when a value can't be blank.
|
|
const errorCodeRequired = "required"
|
|
|
|
var ErrRequired error = simpleError{
|
|
msg: "can't be blank",
|
|
typ: ErrorTypeIncorrectInput,
|
|
code: errorCodeRequired,
|
|
}
|
|
|
|
// This error code is returned when a value can't be nil.
|
|
const errorCodeNil = "nil"
|
|
|
|
var ErrNil error = simpleError{
|
|
msg: "must not be nil",
|
|
typ: ErrorTypeIncorrectInput,
|
|
code: errorCodeNil,
|
|
}
|
|
|
|
// This error code is returned when a cursor can't be decoded (e.g. is malformed).
|
|
const errorCodeInvalidCursor = "invalid-cursor"
|
|
|
|
// ErrInvalidCursor is an error that is returned when a cursor can't be decoded (e.g. is malformed).
|
|
var ErrInvalidCursor error = simpleError{
|
|
msg: "invalid cursor",
|
|
typ: ErrorTypeIncorrectInput,
|
|
code: errorCodeInvalidCursor,
|
|
}
|
|
|
|
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
|
if l := len(s); l > max || l < min {
|
|
return LenOutOfRangeError{
|
|
Min: min,
|
|
Max: max,
|
|
Current: l,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateStringLen(s string, min, max int) error {
|
|
if l := len(s); l > max || l < min {
|
|
return LenOutOfRangeError{
|
|
Min: min,
|
|
Max: max,
|
|
Current: l,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
versionCodeMinLength = 2
|
|
versionCodeMaxLength = 2
|
|
)
|
|
|
|
func validateVersionCode(code string) error {
|
|
return validateStringLen(code, versionCodeMinLength, versionCodeMaxLength)
|
|
}
|
|
|
|
const (
|
|
serverKeyMinLength = 1
|
|
serverKeyMaxLength = 10
|
|
)
|
|
|
|
func validateServerKey(key string) error {
|
|
return validateStringLen(key, serverKeyMinLength, serverKeyMaxLength)
|
|
}
|
|
|
|
func validateIntInRange(current, min, max int) error {
|
|
if current < min {
|
|
return MinGreaterEqualError{
|
|
Min: min,
|
|
Current: current,
|
|
}
|
|
}
|
|
|
|
if current > max {
|
|
return MaxLessEqualError{
|
|
Max: max,
|
|
Current: current,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateSort[S ~[]E, E interface {
|
|
IsInConflict(s E) bool
|
|
String() string
|
|
}](sort S, min, max int) error {
|
|
if err := validateSliceLen(sort, min, max); err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, s1 := range sort {
|
|
for j := i + 1; j < len(sort); j++ {
|
|
s2 := sort[j]
|
|
if s1.IsInConflict(s2) {
|
|
return SortConflictError{
|
|
Sort: [2]string{s1.String(), s2.String()},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|