mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 01:53:02 +00:00
390 lines
9.0 KiB
Go
390 lines
9.0 KiB
Go
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
// Common sentinel errors for quick checks
|
|
var (
|
|
// ErrNotFound is returned when a resource is not found.
|
|
ErrNotFound = errors.New("not found")
|
|
|
|
// ErrUnauthorized is returned when authentication fails or is missing.
|
|
ErrUnauthorized = errors.New("unauthorized")
|
|
|
|
// ErrForbidden is returned when the user lacks permission for an action.
|
|
ErrForbidden = errors.New("forbidden")
|
|
|
|
// ErrConflict is returned when a resource already exists.
|
|
ErrConflict = errors.New("resource already exists")
|
|
|
|
// ErrInvalidInput is returned when request input is invalid.
|
|
ErrInvalidInput = errors.New("invalid input")
|
|
|
|
// ErrTimeout is returned when an operation times out.
|
|
ErrTimeout = errors.New("operation timeout")
|
|
|
|
// ErrServiceUnavailable is returned when a required service is unavailable.
|
|
ErrServiceUnavailable = errors.New("service unavailable")
|
|
|
|
// ErrInternal is returned when an internal error occurs.
|
|
ErrInternal = errors.New("internal error")
|
|
|
|
// ErrTooManyRequests is returned when rate limit is exceeded.
|
|
ErrTooManyRequests = errors.New("too many requests")
|
|
)
|
|
|
|
// Error is the base interface for all custom errors in the system.
|
|
// It extends the standard error interface with additional context.
|
|
type Error interface {
|
|
error
|
|
// Code returns the error code
|
|
Code() string
|
|
// Message returns the human-readable error message
|
|
Message() string
|
|
// Unwrap returns the underlying cause
|
|
Unwrap() error
|
|
}
|
|
|
|
// BaseError provides a foundation for all typed errors.
|
|
type BaseError struct {
|
|
code string
|
|
message string
|
|
cause error
|
|
stack []uintptr
|
|
}
|
|
|
|
// Error implements the error interface.
|
|
func (e *BaseError) Error() string {
|
|
if e.cause != nil {
|
|
return fmt.Sprintf("%s: %v", e.message, e.cause)
|
|
}
|
|
return e.message
|
|
}
|
|
|
|
// Code returns the error code.
|
|
func (e *BaseError) Code() string {
|
|
return e.code
|
|
}
|
|
|
|
// Message returns the error message.
|
|
func (e *BaseError) Message() string {
|
|
return e.message
|
|
}
|
|
|
|
// Unwrap returns the underlying cause.
|
|
func (e *BaseError) Unwrap() error {
|
|
return e.cause
|
|
}
|
|
|
|
// Stack returns the captured stack trace.
|
|
func (e *BaseError) Stack() []uintptr {
|
|
return e.stack
|
|
}
|
|
|
|
// captureStack captures the current stack trace.
|
|
func captureStack(skip int) []uintptr {
|
|
const maxDepth = 32
|
|
stack := make([]uintptr, maxDepth)
|
|
n := runtime.Callers(skip+2, stack)
|
|
return stack[:n]
|
|
}
|
|
|
|
// StackTrace returns a formatted stack trace string.
|
|
func (e *BaseError) StackTrace() string {
|
|
if len(e.stack) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var buf strings.Builder
|
|
frames := runtime.CallersFrames(e.stack)
|
|
for {
|
|
frame, more := frames.Next()
|
|
if !strings.Contains(frame.File, "runtime/") {
|
|
fmt.Fprintf(&buf, "%s\n\t%s:%d\n", frame.Function, frame.File, frame.Line)
|
|
}
|
|
if !more {
|
|
break
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// ValidationError represents an input validation error.
|
|
type ValidationError struct {
|
|
*BaseError
|
|
Field string
|
|
Value interface{}
|
|
}
|
|
|
|
// NewValidationError creates a new validation error.
|
|
func NewValidationError(field, message string, value interface{}) *ValidationError {
|
|
return &ValidationError{
|
|
BaseError: &BaseError{
|
|
code: CodeValidation,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
},
|
|
Field: field,
|
|
Value: value,
|
|
}
|
|
}
|
|
|
|
// Error implements the error interface.
|
|
func (e *ValidationError) Error() string {
|
|
if e.Field != "" {
|
|
return fmt.Sprintf("validation error: %s: %s", e.Field, e.message)
|
|
}
|
|
return fmt.Sprintf("validation error: %s", e.message)
|
|
}
|
|
|
|
// NotFoundError represents a resource not found error.
|
|
type NotFoundError struct {
|
|
*BaseError
|
|
Resource string
|
|
ID string
|
|
}
|
|
|
|
// NewNotFoundError creates a new not found error.
|
|
func NewNotFoundError(resource, id string) *NotFoundError {
|
|
return &NotFoundError{
|
|
BaseError: &BaseError{
|
|
code: CodeNotFound,
|
|
message: fmt.Sprintf("%s not found", resource),
|
|
stack: captureStack(1),
|
|
},
|
|
Resource: resource,
|
|
ID: id,
|
|
}
|
|
}
|
|
|
|
// Error implements the error interface.
|
|
func (e *NotFoundError) Error() string {
|
|
if e.ID != "" {
|
|
return fmt.Sprintf("%s with ID '%s' not found", e.Resource, e.ID)
|
|
}
|
|
return fmt.Sprintf("%s not found", e.Resource)
|
|
}
|
|
|
|
// UnauthorizedError represents an authentication error.
|
|
type UnauthorizedError struct {
|
|
*BaseError
|
|
Realm string
|
|
}
|
|
|
|
// NewUnauthorizedError creates a new unauthorized error.
|
|
func NewUnauthorizedError(message string) *UnauthorizedError {
|
|
if message == "" {
|
|
message = "authentication required"
|
|
}
|
|
return &UnauthorizedError{
|
|
BaseError: &BaseError{
|
|
code: CodeUnauthorized,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
},
|
|
}
|
|
}
|
|
|
|
// WithRealm sets the authentication realm.
|
|
func (e *UnauthorizedError) WithRealm(realm string) *UnauthorizedError {
|
|
e.Realm = realm
|
|
return e
|
|
}
|
|
|
|
// ForbiddenError represents an authorization error.
|
|
type ForbiddenError struct {
|
|
*BaseError
|
|
Resource string
|
|
Action string
|
|
}
|
|
|
|
// NewForbiddenError creates a new forbidden error.
|
|
func NewForbiddenError(resource, action string) *ForbiddenError {
|
|
message := "forbidden"
|
|
if resource != "" && action != "" {
|
|
message = fmt.Sprintf("forbidden: cannot %s %s", action, resource)
|
|
}
|
|
return &ForbiddenError{
|
|
BaseError: &BaseError{
|
|
code: CodeForbidden,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
},
|
|
Resource: resource,
|
|
Action: action,
|
|
}
|
|
}
|
|
|
|
// ConflictError represents a resource conflict error.
|
|
type ConflictError struct {
|
|
*BaseError
|
|
Resource string
|
|
Field string
|
|
Value string
|
|
}
|
|
|
|
// NewConflictError creates a new conflict error.
|
|
func NewConflictError(resource, field, value string) *ConflictError {
|
|
message := fmt.Sprintf("%s already exists", resource)
|
|
if field != "" {
|
|
message = fmt.Sprintf("%s with %s='%s' already exists", resource, field, value)
|
|
}
|
|
return &ConflictError{
|
|
BaseError: &BaseError{
|
|
code: CodeConflict,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
},
|
|
Resource: resource,
|
|
Field: field,
|
|
Value: value,
|
|
}
|
|
}
|
|
|
|
// InternalError represents an internal server error.
|
|
type InternalError struct {
|
|
*BaseError
|
|
Operation string
|
|
}
|
|
|
|
// NewInternalError creates a new internal error.
|
|
func NewInternalError(message string, cause error) *InternalError {
|
|
if message == "" {
|
|
message = "internal error"
|
|
}
|
|
return &InternalError{
|
|
BaseError: &BaseError{
|
|
code: CodeInternal,
|
|
message: message,
|
|
cause: cause,
|
|
stack: captureStack(1),
|
|
},
|
|
}
|
|
}
|
|
|
|
// WithOperation sets the operation context.
|
|
func (e *InternalError) WithOperation(op string) *InternalError {
|
|
e.Operation = op
|
|
return e
|
|
}
|
|
|
|
// ServiceError represents a downstream service error.
|
|
type ServiceError struct {
|
|
*BaseError
|
|
Service string
|
|
StatusCode int
|
|
}
|
|
|
|
// NewServiceError creates a new service error.
|
|
func NewServiceError(service, message string, statusCode int, cause error) *ServiceError {
|
|
if message == "" {
|
|
message = fmt.Sprintf("%s service error", service)
|
|
}
|
|
return &ServiceError{
|
|
BaseError: &BaseError{
|
|
code: CodeServiceUnavailable,
|
|
message: message,
|
|
cause: cause,
|
|
stack: captureStack(1),
|
|
},
|
|
Service: service,
|
|
StatusCode: statusCode,
|
|
}
|
|
}
|
|
|
|
// TimeoutError represents a timeout error.
|
|
type TimeoutError struct {
|
|
*BaseError
|
|
Operation string
|
|
Duration string
|
|
}
|
|
|
|
// NewTimeoutError creates a new timeout error.
|
|
func NewTimeoutError(operation, duration string) *TimeoutError {
|
|
message := "operation timeout"
|
|
if operation != "" {
|
|
message = fmt.Sprintf("%s timeout", operation)
|
|
}
|
|
return &TimeoutError{
|
|
BaseError: &BaseError{
|
|
code: CodeTimeout,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
},
|
|
Operation: operation,
|
|
Duration: duration,
|
|
}
|
|
}
|
|
|
|
// RateLimitError represents a rate limiting error.
|
|
type RateLimitError struct {
|
|
*BaseError
|
|
Limit int
|
|
RetryAfter int // seconds
|
|
}
|
|
|
|
// NewRateLimitError creates a new rate limit error.
|
|
func NewRateLimitError(limit, retryAfter int) *RateLimitError {
|
|
return &RateLimitError{
|
|
BaseError: &BaseError{
|
|
code: CodeRateLimit,
|
|
message: "rate limit exceeded",
|
|
stack: captureStack(1),
|
|
},
|
|
Limit: limit,
|
|
RetryAfter: retryAfter,
|
|
}
|
|
}
|
|
|
|
// Wrap wraps an error with additional context.
|
|
// If the error is already one of our custom types, it preserves the type
|
|
// and adds the cause chain. Otherwise, it creates an InternalError.
|
|
func Wrap(err error, message string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// If it's already our error type, wrap it
|
|
if e, ok := err.(Error); ok {
|
|
return &BaseError{
|
|
code: e.Code(),
|
|
message: message,
|
|
cause: err,
|
|
stack: captureStack(1),
|
|
}
|
|
}
|
|
|
|
// Otherwise create an internal error
|
|
return &InternalError{
|
|
BaseError: &BaseError{
|
|
code: CodeInternal,
|
|
message: message,
|
|
cause: err,
|
|
stack: captureStack(1),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Wrapf wraps an error with a formatted message.
|
|
func Wrapf(err error, format string, args ...interface{}) error {
|
|
return Wrap(err, fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// New creates a new error with a message.
|
|
func New(message string) error {
|
|
return &BaseError{
|
|
code: CodeInternal,
|
|
message: message,
|
|
stack: captureStack(1),
|
|
}
|
|
}
|
|
|
|
// Newf creates a new error with a formatted message.
|
|
func Newf(format string, args ...interface{}) error {
|
|
return New(fmt.Sprintf(format, args...))
|
|
}
|