2025-10-16 12:55:36 +03:00
|
|
|
|
package errors
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// ErrorType представляет тип ошибки
|
|
|
|
|
type ErrorType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
ErrorTypeInvalid ErrorType = "INVALID"
|
|
|
|
|
ErrorTypeNotFound ErrorType = "NOT_FOUND"
|
|
|
|
|
ErrorTypeConflict ErrorType = "CONFLICT"
|
|
|
|
|
ErrorTypeInternal ErrorType = "INTERNAL"
|
|
|
|
|
ErrorTypeUnauthorized ErrorType = "UNAUTHORIZED"
|
|
|
|
|
ErrorTypeForbidden ErrorType = "FORBIDDEN"
|
2025-10-16 13:23:45 +03:00
|
|
|
|
ErrorTypeNetwork ErrorType = "NETWORK"
|
2025-10-16 12:55:36 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AppError - кастомная ошибка приложения
|
|
|
|
|
type AppError struct {
|
|
|
|
|
Type ErrorType `json:"type"`
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
Code int `json:"code,omitempty"`
|
|
|
|
|
Err error `json:"-"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error реализует интерфейс error
|
|
|
|
|
func (e *AppError) Error() string {
|
|
|
|
|
if e.Err != nil {
|
|
|
|
|
return fmt.Sprintf("%s: %v", e.Message, e.Err)
|
|
|
|
|
}
|
|
|
|
|
return e.Message
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unwrap для поддержки errors.Unwrap
|
|
|
|
|
func (e *AppError) Unwrap() error {
|
|
|
|
|
return e.Err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithCode добавляет HTTP код к ошибке
|
|
|
|
|
func (e *AppError) WithCode(code int) *AppError {
|
|
|
|
|
e.Code = code
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrap оборачивает существующую ошибку
|
|
|
|
|
func (e *AppError) Wrap(err error) *AppError {
|
|
|
|
|
e.Err = err
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Конструкторы ошибок
|
|
|
|
|
func NewInvalidError(message string) *AppError {
|
|
|
|
|
return &AppError{
|
|
|
|
|
Type: ErrorTypeInvalid,
|
|
|
|
|
Message: message,
|
|
|
|
|
Code: 400,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewNotFoundError(message string) *AppError {
|
|
|
|
|
return &AppError{
|
|
|
|
|
Type: ErrorTypeNotFound,
|
|
|
|
|
Message: message,
|
|
|
|
|
Code: 404,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 13:23:45 +03:00
|
|
|
|
func NewNetworkError(message string) *AppError {
|
|
|
|
|
return &AppError{
|
|
|
|
|
Type: ErrorTypeNetwork,
|
|
|
|
|
Message: message,
|
|
|
|
|
Code: 0, // Network errors don't have HTTP codes
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:55:36 +03:00
|
|
|
|
func NewInternalError(message string) *AppError {
|
|
|
|
|
return &AppError{
|
|
|
|
|
Type: ErrorTypeInternal,
|
|
|
|
|
Message: message,
|
|
|
|
|
Code: 500,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewConflictError(message string) *AppError {
|
|
|
|
|
return &AppError{
|
|
|
|
|
Type: ErrorTypeConflict,
|
|
|
|
|
Message: message,
|
|
|
|
|
Code: 409,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Вспомогательные функции для проверки типов ошибок
|
|
|
|
|
func IsNotFound(err error) bool {
|
|
|
|
|
var appErr *AppError
|
|
|
|
|
if errors.As(err, &appErr) {
|
|
|
|
|
return appErr.Type == ErrorTypeNotFound
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func IsInvalid(err error) bool {
|
|
|
|
|
var appErr *AppError
|
|
|
|
|
if errors.As(err, &appErr) {
|
|
|
|
|
return appErr.Type == ErrorTypeInvalid
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func IsConflict(err error) bool {
|
|
|
|
|
var appErr *AppError
|
|
|
|
|
if errors.As(err, &appErr) {
|
|
|
|
|
return appErr.Type == ErrorTypeConflict
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 14:19:39 +03:00
|
|
|
|
func IsNetworkError(err error) bool {
|
|
|
|
|
var appErr *AppError
|
|
|
|
|
if errors.As(err, &appErr) {
|
|
|
|
|
return appErr.Type == ErrorTypeNetwork
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:55:36 +03:00
|
|
|
|
// Wrap оборачивает ошибку с сообщением
|
|
|
|
|
func Wrap(err error, message string) error {
|
|
|
|
|
return fmt.Errorf("%s: %w", message, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Wrapf(err error, format string, args ...interface{}) error {
|
|
|
|
|
return fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)
|
|
|
|
|
}
|