package notify import ( "errors" "fmt" ) // Sentinel errors for programmatic handling. var ( ErrInvalidConfig = errors.New("invalid configuration") ErrUnauthorized = errors.New("unauthorized") ErrForbidden = errors.New("forbidden") ErrSuppressed = errors.New("recipient suppressed") ErrHostNotFound = errors.New("host not found") ErrFromNotFound = errors.New("from address not registered") ErrValidation = errors.New("validation error") ErrServerError = errors.New("server error") ErrRateLimit = errors.New("rate limit exceeded") ) // APIError represents an error returned by the notify API. type APIError struct { StatusCode int Message string Code string err error } func (e *APIError) Error() string { if e.Code != "" { return fmt.Sprintf("notify api error (status %d): [%s] %s", e.StatusCode, e.Code, e.Message) } return fmt.Sprintf("notify api error (status %d): %s", e.StatusCode, e.Message) } func (e *APIError) Unwrap() error { return e.err } // classifyError maps an HTTP status and optional error code to a sentinel error. func classifyError(statusCode int, code string) error { // Check specific error codes first. switch code { case "suppressed": return ErrSuppressed case "host_not_found": return ErrHostNotFound case "from_not_found": return ErrFromNotFound } // Fall back to HTTP status. switch { case statusCode == 401: return ErrUnauthorized case statusCode == 403: return ErrForbidden case statusCode == 422: return ErrValidation case statusCode == 429: return ErrRateLimit case statusCode >= 400 && statusCode < 500: return ErrValidation case statusCode >= 500: return ErrServerError default: return nil } }