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 } func (e ValidationError) Code() string { var domainErr Error if errors.As(e.Err, &domainErr) { return domainErr.Code() } return "validation-failed" } 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 } func (e SliceElementValidationError) Code() string { var domainErr Error if errors.As(e.Err, &domainErr) { return domainErr.Code() } return "slice-element-validation-failed" } 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 } func (e MinGreaterEqualError) Code() string { return "min-greater-equal" } 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 } func (e MaxLessEqualError) Code() string { return "max-less-equal" } 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 } func (e LenOutOfRangeError) Code() string { return "length-out-of-range" } 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 } func (e InvalidURLError) Code() string { return "invalid-url" } 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 } func (e UnsupportedSortStringError) Code() string { return "unsupported-sort-string" } func (e UnsupportedSortStringError) Params() map[string]any { return map[string]any{ "Sort": e.Sort, } } var ErrRequired error = simpleError{ msg: "can't be blank", typ: ErrorTypeIncorrectInput, code: "required", } var ErrNil error = simpleError{ msg: "must not be nil", typ: ErrorTypeIncorrectInput, code: "nil", } // 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: "invalid-cursor", } 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 }