package errs import ( "core/errs/stack" "fmt" "regexp" ) const ( // errCodePattern is the regular expression that defines a valid error code format. // The error code can optionally have a module, which is separated by "::". // The valid characters are alphanumeric characters and the symbols ":-_". errCodePattern string = "^[a-zA-Z0-9._-]+(::[a-zA-Z0-9._-]+)?$" ) const ( // ModuleSeparator is the separator used between the module and value parts of the error code. ModuleSeparator string = "::" ) // Module configuration const moduleName = "core.errs" + ModuleSeparator // Error messages for internal use const ( errNilWrapMsg string = "error trying to wrap % into a nil error" errNilCauseMsg string = "error trying to wrap % into a nil cause" ) // Standard error codes for common error scenarios const ( CodePanicError string = "panic" CodeMultiError string = "multiple-errors" CodeParamError string = "Param" CodeParamNilError string = "Param-nil" CodeParamOutOfRangeError string = "Param-out-of-range" ) // G provides a global access point for common errors and information // This struct combines documentation links and pre-initialized error instances var ( G = struct { InfoHelp, InfoLink string ErrParam, ErrParamNil, ErrParamOutOfRange, ErrInvalidError, ErrInvalidNilError, ErrUnknownPanicError, ErrInvalidErrCode ErrCode }{ InfoLink: "https://git.activething.com/go/active", InfoHelp: "https://doc.activething.com/go/active", ErrParam: ErrCode(CodeParamError), ErrParamNil: ErrCode(CodeParamNilError), ErrParamOutOfRange: ErrCode(CodeParamOutOfRangeError), ErrUnknownPanicError: ErrCode(moduleName + "unknown-panic-error"), ErrInvalidNilError: ErrCode(moduleName + "invalid-nil-error"), ErrInvalidError: ErrCode(moduleName + "invalid-error"), ErrInvalidErrCode: ErrCode(moduleName + "invalid-err-code"), } // g contains private package-level variables g = struct { panicErrNil *ErrPanic codeValidator *regexp.Regexp }{ panicErrNil: NewErrPanic(G.ErrInvalidNilError), codeValidator: regexp.MustCompile(errCodePattern), } ) // ToError converts any value to an error type // Returns nil if the input is nil // Converts []error to ErrMulti // Uses fmt.Errorf for other types func ToError(err any) error { if err == nil { return nil } switch e := err.(type) { case error: return e case []error: if len(e) == 0 { return nil } return NewErrMulti(e...) default: return fmt.Errorf("%v", err) } } // PanicRecoveredError creates an error from a recovered panic value // Returns nil if the recovered value is nil func PanicRecoveredError(rec any) error { if rec == nil { return nil } return *NewErrPanic(ToError(rec)) } // Unwrap provides standard error unwrapping functionality // Implements the unwrap interface for error chains func Unwrap(err error) error { if err == nil { return nil } if cs, is := err.(Unwrapper); is { return cs.Unwrap() } return nil } // UnwrapCause retrieves the root cause of an error chain // Traverses the error chain looking for Causer implementation func UnwrapCause(err error) error { e := err for e != nil { cs, is := e.(Causer) if is { return cs.Cause() } e = Unwrap(e) } return nil } // UnwrapCauses retrieves all causes in an error chain // Returns a slice of errors representing the cause chain func UnwrapCauses(err error) []error { if err == nil { return nil } if al, is := err.(Causers); is { return al.Causes() } var l ErrorList e := err for e != nil { l = append(l, e) e = Unwrap(e) } return l } // UnwrapList retrieves all errors in an error chain as an ErrorList // Handles both UnwrapperList implementations and standard error chains func UnwrapList(err error) ErrorList { if err == nil { return nil } if al, is := err.(UnwrapperList); is { return al.Unwrap() } var l ErrorList e := err for e != nil { l = append(l, e) e = Unwrap(e) } return l } // UnwrapTrace retrieves the stack trace from an error func UnwrapTrace(err error) stack.Stack { return stack.UnwrapStack(err, false) } // WrapError combines two errors into a single error, establishing a cause relationship. // If either error is nil, the non-nil error is returned. // Otherwise, a new error is returned that has the first error as the cause. func WrapError(err, cause error) error { if err == nil { return cause } if cause == nil { return err } return *NewErrWithCause(err, cause) } // ValidateCode check if un err code is valid func ValidateCode(code string) bool { return g.codeValidator.MatchString(code) }