package errors import ( "fmt" "net/http" ) // ErrorCode represents application-specific error codes type ErrorCode string const ( // Authentication errors ErrCodeInvalidCredentials ErrorCode = "INVALID_CREDENTIALS" ErrCodeTokenExpired ErrorCode = "TOKEN_EXPIRED" ErrCodeTokenInvalid ErrorCode = "TOKEN_INVALID" ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED" ErrCodeForbidden ErrorCode = "FORBIDDEN" // Validation errors ErrCodeValidationFailed ErrorCode = "VALIDATION_FAILED" ErrCodeInvalidInput ErrorCode = "INVALID_INPUT" ErrCodeMissingField ErrorCode = "MISSING_FIELD" // Resource errors ErrCodeNotFound ErrorCode = "NOT_FOUND" ErrCodeAlreadyExists ErrorCode = "ALREADY_EXISTS" ErrCodeConflict ErrorCode = "CONFLICT" // Business logic errors ErrCodeSlotUnavailable ErrorCode = "SLOT_UNAVAILABLE" ErrCodeBookingNotFound ErrorCode = "BOOKING_NOT_FOUND" ErrCodePaymentFailed ErrorCode = "PAYMENT_FAILED" ErrCodePaymentRequired ErrorCode = "PAYMENT_REQUIRED" ErrCodeMeetingCreateFailed ErrorCode = "MEETING_CREATE_FAILED" // System errors ErrCodeInternalServer ErrorCode = "INTERNAL_SERVER_ERROR" ErrCodeDatabaseError ErrorCode = "DATABASE_ERROR" ErrCodeExternalAPI ErrorCode = "EXTERNAL_API_ERROR" ErrCodeServiceUnavailable ErrorCode = "SERVICE_UNAVAILABLE" // Rate limiting errors ErrCodeRateLimitExceeded ErrorCode = "RATE_LIMIT_EXCEEDED" ErrCodeTooManyRequests ErrorCode = "TOO_MANY_REQUESTS" ) // AppError represents an application error with structured information type AppError struct { Code ErrorCode `json:"code"` Message string `json:"message"` Details string `json:"details,omitempty"` HTTPStatus int `json:"-"` Fields map[string]interface{} `json:"fields,omitempty"` Cause error `json:"-"` } // Error implements the error interface func (e *AppError) Error() string { if e.Details != "" { return fmt.Sprintf("%s: %s - %s", e.Code, e.Message, e.Details) } return fmt.Sprintf("%s: %s", e.Code, e.Message) } // Unwrap returns the underlying cause error func (e *AppError) Unwrap() error { return e.Cause } // WithDetails adds details to the error func (e *AppError) WithDetails(details string) *AppError { e.Details = details return e } // WithField adds a field to the error func (e *AppError) WithField(key string, value interface{}) *AppError { if e.Fields == nil { e.Fields = make(map[string]interface{}) } e.Fields[key] = value return e } // WithFields adds multiple fields to the error func (e *AppError) WithFields(fields map[string]interface{}) *AppError { if e.Fields == nil { e.Fields = make(map[string]interface{}) } for k, v := range fields { e.Fields[k] = v } return e } // WithCause adds a cause error func (e *AppError) WithCause(cause error) *AppError { e.Cause = cause return e } // New creates a new AppError func New(code ErrorCode, message string, httpStatus int) *AppError { return &AppError{ Code: code, Message: message, HTTPStatus: httpStatus, } } // Wrap wraps an existing error with application error information func Wrap(err error, code ErrorCode, message string, httpStatus int) *AppError { return &AppError{ Code: code, Message: message, HTTPStatus: httpStatus, Cause: err, } } // Predefined common errors var ( // Authentication errors ErrInvalidCredentials = New(ErrCodeInvalidCredentials, "Invalid email or password", http.StatusUnauthorized) ErrTokenExpired = New(ErrCodeTokenExpired, "Authentication token has expired", http.StatusUnauthorized) ErrTokenInvalid = New(ErrCodeTokenInvalid, "Invalid authentication token", http.StatusUnauthorized) ErrUnauthorized = New(ErrCodeUnauthorized, "Authentication required", http.StatusUnauthorized) ErrForbidden = New(ErrCodeForbidden, "Access denied", http.StatusForbidden) // Validation errors ErrValidationFailed = New(ErrCodeValidationFailed, "Request validation failed", http.StatusBadRequest) ErrInvalidInput = New(ErrCodeInvalidInput, "Invalid input provided", http.StatusBadRequest) ErrMissingField = New(ErrCodeMissingField, "Required field is missing", http.StatusBadRequest) // Resource errors ErrNotFound = New(ErrCodeNotFound, "Resource not found", http.StatusNotFound) ErrAlreadyExists = New(ErrCodeAlreadyExists, "Resource already exists", http.StatusConflict) ErrConflict = New(ErrCodeConflict, "Resource conflict", http.StatusConflict) // Business logic errors ErrSlotUnavailable = New(ErrCodeSlotUnavailable, "Selected time slot is not available", http.StatusConflict) ErrBookingNotFound = New(ErrCodeBookingNotFound, "Booking not found", http.StatusNotFound) ErrPaymentFailed = New(ErrCodePaymentFailed, "Payment processing failed", http.StatusPaymentRequired) ErrPaymentRequired = New(ErrCodePaymentRequired, "Payment is required", http.StatusPaymentRequired) ErrMeetingCreateFailed = New(ErrCodeMeetingCreateFailed, "Failed to create meeting room", http.StatusInternalServerError) // System errors ErrInternalServer = New(ErrCodeInternalServer, "Internal server error", http.StatusInternalServerError) ErrDatabaseError = New(ErrCodeDatabaseError, "Database operation failed", http.StatusInternalServerError) ErrExternalAPI = New(ErrCodeExternalAPI, "External API error", http.StatusBadGateway) ErrServiceUnavailable = New(ErrCodeServiceUnavailable, "Service temporarily unavailable", http.StatusServiceUnavailable) // Rate limiting errors ErrRateLimitExceeded = New(ErrCodeRateLimitExceeded, "Rate limit exceeded", http.StatusTooManyRequests) ErrTooManyRequests = New(ErrCodeTooManyRequests, "Too many requests", http.StatusTooManyRequests) ) // IsAppError checks if an error is an AppError func IsAppError(err error) bool { _, ok := err.(*AppError) return ok } // GetAppError extracts AppError from error, returns nil if not an AppError func GetAppError(err error) *AppError { if appErr, ok := err.(*AppError); ok { return appErr } return nil } // ErrorResponse represents the JSON error response structure type ErrorResponse struct { Error ErrorInfo `json:"error"` } // ErrorInfo contains error information for API responses type ErrorInfo struct { Code ErrorCode `json:"code"` Message string `json:"message"` Details string `json:"details,omitempty"` Fields map[string]interface{} `json:"fields,omitempty"` } // ToErrorResponse converts an AppError to an ErrorResponse func (e *AppError) ToErrorResponse() ErrorResponse { return ErrorResponse{ Error: ErrorInfo{ Code: e.Code, Message: e.Message, Details: e.Details, Fields: e.Fields, }, } } // ValidationError represents a field validation error type ValidationError struct { Field string `json:"field"` Message string `json:"message"` Value interface{} `json:"value,omitempty"` } // ValidationErrors represents multiple validation errors type ValidationErrors []ValidationError // Error implements the error interface for ValidationErrors func (ve ValidationErrors) Error() string { if len(ve) == 0 { return "validation failed" } if len(ve) == 1 { return fmt.Sprintf("validation failed: %s %s", ve[0].Field, ve[0].Message) } return fmt.Sprintf("validation failed: %d errors", len(ve)) } // ToAppError converts ValidationErrors to AppError func (ve ValidationErrors) ToAppError() *AppError { fields := make(map[string]interface{}) for _, err := range ve { fields[err.Field] = err.Message } return ErrValidationFailed.WithFields(fields).WithDetails(ve.Error()) } // NewValidationError creates a new validation error func NewValidationError(field, message string, value interface{}) ValidationError { return ValidationError{ Field: field, Message: message, Value: value, } }