package apperrors import ( "fmt" "net/http" ) // AppError represents an application error with HTTP status and i18n key type AppError struct { Code int // HTTP status code Message string // Default message (fallback if i18n key not found) MessageKey string // i18n key for localization Err error // Wrapped error (for internal errors) } func (e *AppError) Error() string { if e.Err != nil { return fmt.Sprintf("%s: %v", e.Message, e.Err) } if e.Message != "" { return e.Message } return e.MessageKey } func (e *AppError) Unwrap() error { return e.Err } // NotFound creates a 404 Not Found error func NotFound(messageKey string) *AppError { return &AppError{ Code: http.StatusNotFound, MessageKey: messageKey, } } // Forbidden creates a 403 Forbidden error func Forbidden(messageKey string) *AppError { return &AppError{ Code: http.StatusForbidden, MessageKey: messageKey, } } // BadRequest creates a 400 Bad Request error func BadRequest(messageKey string) *AppError { return &AppError{ Code: http.StatusBadRequest, MessageKey: messageKey, } } // Unauthorized creates a 401 Unauthorized error func Unauthorized(messageKey string) *AppError { return &AppError{ Code: http.StatusUnauthorized, MessageKey: messageKey, } } // Conflict creates a 409 Conflict error func Conflict(messageKey string) *AppError { return &AppError{ Code: http.StatusConflict, MessageKey: messageKey, } } // TooManyRequests creates a 429 Too Many Requests error func TooManyRequests(messageKey string) *AppError { return &AppError{ Code: http.StatusTooManyRequests, MessageKey: messageKey, } } // Internal creates a 500 Internal Server Error, wrapping the original error func Internal(err error) *AppError { return &AppError{ Code: http.StatusInternalServerError, MessageKey: "error.internal", Err: err, } } // WithMessage adds a default message to the error (used when i18n key not found) func (e *AppError) WithMessage(msg string) *AppError { e.Message = msg return e } // Wrap wraps an underlying error func (e *AppError) Wrap(err error) *AppError { e.Err = err return e }