mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 17:48:52 -05:00
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 1.0.8 to 1.0.9. - [Commits](https://github.com/olekukonko/tablewriter/compare/v1.0.8...v1.0.9) --- updated-dependencies: - dependency-name: github.com/olekukonko/tablewriter dependency-version: 1.0.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
433 lines
11 KiB
Go
433 lines
11 KiB
Go
package errors
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// As wraps errors.As, using custom type assertion for *Error types.
|
||
// Falls back to standard errors.As for non-*Error types.
|
||
// Returns false if either err or target is nil.
|
||
func As(err error, target interface{}) bool {
|
||
if err == nil || target == nil {
|
||
return false
|
||
}
|
||
|
||
// First try our custom *Error handling
|
||
if e, ok := err.(*Error); ok {
|
||
return e.As(target)
|
||
}
|
||
|
||
// Fall back to standard errors.As
|
||
return errors.As(err, target)
|
||
}
|
||
|
||
// Code returns the status code of an error, if it is an *Error.
|
||
// Returns 500 as a default for non-*Error types to indicate an internal error.
|
||
func Code(err error) int {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Code()
|
||
}
|
||
return DefaultCode
|
||
}
|
||
|
||
// Context extracts the context map from an error, if it is an *Error.
|
||
// Returns nil for non-*Error types or if no context is present.
|
||
func Context(err error) map[string]interface{} {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Context()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Convert transforms any error into an *Error, preserving its message and wrapping it if needed.
|
||
// Returns nil if the input is nil; returns the original if already an *Error.
|
||
// Uses multiple strategies: direct assertion, errors.As, manual unwrapping, and fallback creation.
|
||
func Convert(err error) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
|
||
// First try direct type assertion (fast path)
|
||
if e, ok := err.(*Error); ok {
|
||
return e
|
||
}
|
||
|
||
// Try using errors.As (more flexible)
|
||
var e *Error
|
||
if errors.As(err, &e) {
|
||
return e
|
||
}
|
||
|
||
// Manual unwrapping as fallback
|
||
visited := make(map[error]bool)
|
||
for unwrapped := err; unwrapped != nil; {
|
||
if visited[unwrapped] {
|
||
break // Cycle detected
|
||
}
|
||
visited[unwrapped] = true
|
||
if e, ok := unwrapped.(*Error); ok {
|
||
return e
|
||
}
|
||
unwrapped = errors.Unwrap(unwrapped)
|
||
}
|
||
|
||
// Final fallback: create new error with original message and wrap it
|
||
return New(err.Error()).Wrap(err)
|
||
}
|
||
|
||
// Count returns the occurrence count of an error, if it is an *Error.
|
||
// Returns 0 for non-*Error types.
|
||
func Count(err error) uint64 {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Count()
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// Find searches the error chain for the first error matching pred.
|
||
// Returns nil if no match is found or pred is nil; traverses both Unwrap() and Cause() chains.
|
||
func Find(err error, pred func(error) bool) error {
|
||
for current := err; current != nil; {
|
||
if pred(current) {
|
||
return current
|
||
}
|
||
|
||
// Attempt to unwrap using Unwrap() or Cause()
|
||
switch v := current.(type) {
|
||
case interface{ Unwrap() error }:
|
||
current = v.Unwrap()
|
||
case interface{ Cause() error }:
|
||
current = v.Cause()
|
||
default:
|
||
return nil
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// From transforms any error into an *Error, preserving its message and wrapping it if needed.
|
||
// Alias of Convert; returns nil if input is nil, original if already an *Error.
|
||
func From(err error) *Error {
|
||
return Convert(err)
|
||
}
|
||
|
||
// FromContext creates an *Error from a context and an existing error.
|
||
// Enhances the error with context info: timeout status, deadline, or cancellation.
|
||
// Returns nil if input error is nil; does not store context values directly.
|
||
func FromContext(ctx context.Context, err error) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
|
||
e := New(err.Error())
|
||
|
||
// Handle context errors
|
||
switch ctx.Err() {
|
||
case context.DeadlineExceeded:
|
||
e.WithTimeout()
|
||
if deadline, ok := ctx.Deadline(); ok {
|
||
e.With("deadline", deadline.Format(time.RFC3339))
|
||
}
|
||
case context.Canceled:
|
||
e.With("cancelled", true)
|
||
}
|
||
|
||
return e
|
||
}
|
||
|
||
// Category returns the category of an error, if it is an *Error.
|
||
// Returns an empty string for non-*Error types or unset categories.
|
||
func Category(err error) string {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.category
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// Has checks if an error contains meaningful content.
|
||
// Returns true for non-nil standard errors or *Error with content (msg, name, template, or cause).
|
||
func Has(err error) bool {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Has()
|
||
}
|
||
return err != nil
|
||
}
|
||
|
||
// HasContextKey checks if the error's context contains the specified key.
|
||
// Returns false for non-*Error types or if the key is not present in the context.
|
||
func HasContextKey(err error, key string) bool {
|
||
if e, ok := err.(*Error); ok {
|
||
ctx := e.Context()
|
||
if ctx != nil {
|
||
_, exists := ctx[key]
|
||
return exists
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// Is wraps errors.Is, using custom matching for *Error types.
|
||
// Falls back to standard errors.Is for non-*Error types; returns true if err equals target.
|
||
func Is(err, target error) bool {
|
||
if err == nil || target == nil {
|
||
return err == target
|
||
}
|
||
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Is(target)
|
||
}
|
||
|
||
// Use standard errors.Is for non-Error types
|
||
return errors.Is(err, target)
|
||
}
|
||
|
||
// IsError checks if an error is an instance of *Error.
|
||
// Returns true only for this package's custom error type; false for nil or other types.
|
||
func IsError(err error) bool {
|
||
_, ok := err.(*Error)
|
||
return ok
|
||
}
|
||
|
||
// IsEmpty checks if an error has no meaningful content.
|
||
// Returns true for nil errors, empty *Error instances, or standard errors with whitespace-only messages.
|
||
func IsEmpty(err error) bool {
|
||
if err == nil {
|
||
return true
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
return e.IsEmpty()
|
||
}
|
||
return strings.TrimSpace(err.Error()) == ""
|
||
}
|
||
|
||
// IsNull checks if an error is nil or represents a NULL value.
|
||
// Delegates to *Error’s IsNull for custom errors; uses sqlNull for others.
|
||
func IsNull(err error) bool {
|
||
if err == nil {
|
||
return true
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
return e.IsNull()
|
||
}
|
||
return sqlNull(err)
|
||
}
|
||
|
||
// IsRetryable checks if an error is retryable.
|
||
// For *Error, checks context for retry flag; for others, looks for "retry" or timeout in message.
|
||
// Returns false for nil errors; thread-safe for *Error types.
|
||
func IsRetryable(err error) bool {
|
||
if err == nil {
|
||
return false
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
e.mu.RLock()
|
||
defer e.mu.RUnlock()
|
||
// Check smallContext directly if context map isn’t populated
|
||
for i := int32(0); i < e.smallCount; i++ {
|
||
if e.smallContext[i].key == ctxRetry {
|
||
if val, ok := e.smallContext[i].value.(bool); ok {
|
||
return val
|
||
}
|
||
}
|
||
}
|
||
// Check regular context
|
||
if e.context != nil {
|
||
if val, ok := e.context[ctxRetry].(bool); ok {
|
||
return val
|
||
}
|
||
}
|
||
// Check cause recursively
|
||
if e.cause != nil {
|
||
return IsRetryable(e.cause)
|
||
}
|
||
}
|
||
lowerMsg := strings.ToLower(err.Error())
|
||
return IsTimeout(err) || strings.Contains(lowerMsg, "retry")
|
||
}
|
||
|
||
// IsTimeout checks if an error indicates a timeout.
|
||
// For *Error, checks context for timeout flag; for others, looks for "timeout" in message.
|
||
// Returns false for nil errors.
|
||
func IsTimeout(err error) bool {
|
||
if err == nil {
|
||
return false
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
if val, ok := e.Context()[ctxTimeout].(bool); ok {
|
||
return val
|
||
}
|
||
}
|
||
return strings.Contains(strings.ToLower(err.Error()), "timeout")
|
||
}
|
||
|
||
// Merge combines multiple errors into a single *Error.
|
||
// Aggregates messages with "; " separator, merges contexts and stacks; returns nil if no errors provided.
|
||
func Merge(errs ...error) *Error {
|
||
if len(errs) == 0 {
|
||
return nil
|
||
}
|
||
var messages []string
|
||
combined := New("")
|
||
for _, err := range errs {
|
||
if err == nil {
|
||
continue
|
||
}
|
||
messages = append(messages, err.Error())
|
||
if e, ok := err.(*Error); ok {
|
||
if e.stack != nil && combined.stack == nil {
|
||
combined.WithStack() // Capture stack from first *Error with stack
|
||
}
|
||
if ctx := e.Context(); ctx != nil {
|
||
for k, v := range ctx {
|
||
combined.With(k, v)
|
||
}
|
||
}
|
||
if e.cause != nil {
|
||
combined.Wrap(e.cause)
|
||
}
|
||
} else {
|
||
combined.Wrap(err)
|
||
}
|
||
}
|
||
if len(messages) > 0 {
|
||
combined.msg = strings.Join(messages, "; ")
|
||
}
|
||
return combined
|
||
}
|
||
|
||
// Name returns the name of an error, if it is an *Error.
|
||
// Returns an empty string for non-*Error types or unset names.
|
||
func Name(err error) string {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.name
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// UnwrapAll returns a slice of all errors in the chain, including the root error.
|
||
// Traverses both Unwrap() and Cause() chains; returns nil if err is nil.
|
||
func UnwrapAll(err error) []error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
return e.UnwrapAll()
|
||
}
|
||
var result []error
|
||
Walk(err, func(e error) {
|
||
result = append(result, e)
|
||
})
|
||
return result
|
||
}
|
||
|
||
// Stack extracts the stack trace from an error, if it is an *Error.
|
||
// Returns nil for non-*Error types or if no stack is present.
|
||
func Stack(err error) []string {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.Stack()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Transform applies transformations to an error, returning a new *Error.
|
||
// Creates a new *Error from non-*Error types before applying fn; returns nil if err is nil.
|
||
func Transform(err error, fn func(*Error)) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
newErr := e.Copy()
|
||
fn(newErr)
|
||
return newErr
|
||
}
|
||
// If not an *Error, create a new one and transform it
|
||
newErr := New(err.Error())
|
||
fn(newErr)
|
||
return newErr
|
||
}
|
||
|
||
// Unwrap returns the underlying cause of an error, if it implements Unwrap.
|
||
// For *Error, returns cause; for others, returns the error itself; nil if err is nil.
|
||
func Unwrap(err error) error {
|
||
for current := err; current != nil; {
|
||
if e, ok := current.(*Error); ok {
|
||
if e.cause == nil {
|
||
return current
|
||
}
|
||
current = e.cause
|
||
} else {
|
||
return current
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Walk traverses the error chain, applying fn to each error.
|
||
// Supports both Unwrap() and Cause() interfaces; stops at nil or non-unwrappable errors.
|
||
func Walk(err error, fn func(error)) {
|
||
for current := err; current != nil; {
|
||
fn(current)
|
||
|
||
// Attempt to unwrap using Unwrap() or Cause()
|
||
switch v := current.(type) {
|
||
case interface{ Unwrap() error }:
|
||
current = v.Unwrap()
|
||
case interface{ Cause() error }:
|
||
current = v.Cause()
|
||
default:
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// With adds a key-value pair to an error's context, if it is an *Error.
|
||
// Returns the original error unchanged if not an *Error; no-op for non-*Error types.
|
||
func With(err error, key string, value interface{}) error {
|
||
if e, ok := err.(*Error); ok {
|
||
return e.With(key, value)
|
||
}
|
||
return err
|
||
}
|
||
|
||
// WithStack converts any error to an *Error and captures a stack trace.
|
||
// Returns nil if input is nil; adds stack to existing *Error or wraps non-*Error types.
|
||
func WithStack(err error) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
if e, ok := err.(*Error); ok {
|
||
return e.WithStack()
|
||
}
|
||
return New(err.Error()).WithStack().Wrap(err)
|
||
}
|
||
|
||
// Wrap creates a new *Error that wraps another error with additional context.
|
||
// Uses a copy of the provided wrapper *Error; returns nil if err is nil.
|
||
func Wrap(err error, wrapper *Error) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
if wrapper == nil {
|
||
wrapper = newError()
|
||
}
|
||
newErr := wrapper.Copy()
|
||
newErr.cause = err
|
||
return newErr
|
||
}
|
||
|
||
// Wrapf creates a new formatted *Error that wraps another error.
|
||
// Formats the message and sets the cause; returns nil if err is nil.
|
||
func Wrapf(err error, format string, args ...interface{}) *Error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
e := newError()
|
||
e.msg = fmt.Sprintf(format, args...)
|
||
e.cause = err
|
||
return e
|
||
}
|