Documentation ¶
Overview ¶
Package errorx provides error implementation and error-related utilities.
Conventional approach towards errors in Go is quite limited. The typical case implies an error being created at some point:
return errors.New("now this is unfortunate")
Then being passed along with a no-brainer:
if err != nil { return err }
And, finally, handled by printing it to the log file:
log.Errorf("Error: %s", err)
This approach is simple, but quite often it is not enough. There is a need to add context information to error, to check or hide its properties. If all else fails, it pays to have a stack trace printed along with error text.
Syntax ¶
The code above could be modified in this fashion:
return errorx.IllegalState.New("unfortunate") if err != nil { return errorx.Decorate(err, "this could be so much better") } log.Errorf("Error: %+v", err)
Here errorx.Decorate is used to add more information, and syntax like errorx.IsOfType can still be used to check the original error. This error also holds a stack trace captured at the point of creation. With errorx syntax, any of this may be customized: stack trace can be omitted, error type can be hidden. Type can be further customized with Traits, and error with Properties. Package provides utility functions to compose, switch over, check, and ignore errors based on their types and properties.
See documentation for Error, Type and Namespace for more details.
Index ¶
- Variables
- func DecorateMany(message string, errs ...error) error
- func ErrorFromPanic(recoverResult interface{}) (error, bool)
- func ExtractContext(err error) (context.Context, bool)
- func ExtractPayload(err error) (interface{}, bool)
- func ExtractProperty(err error, key Property) (interface{}, bool)
- func GetTypeName(err error) string
- func HasTrait(err error, key Trait) bool
- func Ignore(err error, types ...*Type) error
- func IgnoreWithTrait(err error, traits ...Trait) error
- func IsDuplicate(err error) bool
- func IsNotFound(err error) bool
- func IsOfType(err error, t *Type) bool
- func IsOfTypeIgnoreTransparent(err error, t *Type) bool
- func IsTemporary(err error) bool
- func IsTimeout(err error) bool
- func Panic(err error) error
- func RegisterTypeSubscriber(s TypeSubscriber)
- func ReplicateError(err error, count int) []error
- func WrapMany(errorType *Type, message string, errs ...error) error
- type Error
- func Cast(err error) *Error
- func Decorate(err error, message string, args ...interface{}) *Error
- func EnhanceStackTrace(err error, message string, args ...interface{}) *Error
- func EnsureStackTrace(err error) *Error
- func WithContext(err *Error, ctx context.Context) *Error
- func WithPayload(err *Error, payload interface{}) *Error
- func (e *Error) Cause() error
- func (e *Error) Error() string
- func (e *Error) Format(s fmt.State, verb rune)
- func (e *Error) HasTrait(key Trait) bool
- func (e *Error) Is(target error) bool
- func (e *Error) IsOfType(t *Type) bool
- func (e *Error) IsOfTypeIgnoreTransparent(t *Type) bool
- func (e *Error) Message() string
- func (e *Error) Property(key Property) (interface{}, bool)
- func (e *Error) Type() *Type
- func (e *Error) Unwrap() error
- func (e *Error) WithProperty(key Property, value interface{}) *Error
- func (e *Error) WithUnderlyingErrors(errs ...error) *Error
- type ErrorBuilder
- func (eb ErrorBuilder) Create() *Error
- func (eb ErrorBuilder) EnhanceStackTrace() ErrorBuilder
- func (eb ErrorBuilder) Transparent() ErrorBuilder
- func (eb ErrorBuilder) WithCause(err error) ErrorBuilder
- func (eb ErrorBuilder) WithConditionallyFormattedMessage(message string, args ...interface{}) ErrorBuilder
- type Namespace
- func (n Namespace) ApplyModifiers(modifiers ...TypeModifier) Namespace
- func (n Namespace) FullName() string
- func (n Namespace) IsNamespaceOf(t *Type) bool
- func (n Namespace) Key() NamespaceKey
- func (n Namespace) NewSubNamespace(name string, traits ...Trait) Namespace
- func (n Namespace) NewType(typeName string, traits ...Trait) *Type
- func (n Namespace) Parent() *Namespace
- func (n Namespace) String() string
- type NamespaceKey
- type Property
- type StackTraceFilePathTransformer
- type Trait
- type Type
- func (t *Type) ApplyModifiers(modifiers ...TypeModifier) *Type
- func (t *Type) FullName() string
- func (t *Type) HasTrait(key Trait) bool
- func (t *Type) IsOfType(other *Type) bool
- func (t *Type) MarshalText() (text []byte, err error)
- func (t *Type) Namespace() Namespace
- func (t *Type) New(message string, args ...interface{}) *Error
- func (t *Type) NewSubtype(name string, traits ...Trait) *Type
- func (t *Type) NewWithNoMessage() *Error
- func (t *Type) RootNamespace() Namespace
- func (t *Type) String() string
- func (t *Type) Supertype() *Type
- func (t *Type) Wrap(err error, message string, args ...interface{}) *Error
- func (t *Type) WrapWithNoMessage(err error) *Error
- type TypeModifier
- type TypeSubscriber
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // CommonErrors is a namespace for general purpose errors designed for universal use. // These errors should typically be used in opaque manner, implying no handing in user code. // When handling is required, it is best to use custom error types with both standard and custom traits. CommonErrors = NewNamespace("common") // IllegalArgument is a type for invalid argument error IllegalArgument = CommonErrors.NewType("illegal_argument") // IllegalState is a type for invalid state error IllegalState = CommonErrors.NewType("illegal_state") // IllegalFormat is a type for invalid format error IllegalFormat = CommonErrors.NewType("illegal_format") // InitializationFailed is a type for initialization error InitializationFailed = CommonErrors.NewType("initialization_failed") DataUnavailable = CommonErrors.NewType("data_unavailable") // UnsupportedOperation is a type for unsupported operation error UnsupportedOperation = CommonErrors.NewType("unsupported_operation") // RejectedOperation is a type for rejected operation error RejectedOperation = CommonErrors.NewType("rejected_operation") // Interrupted is a type for interruption error Interrupted = CommonErrors.NewType("interrupted") // AssertionFailed is a type for assertion error AssertionFailed = CommonErrors.NewType("assertion_failed") // InternalError is a type for internal error InternalError = CommonErrors.NewType("internal_error") // ExternalError is a type for external error ExternalError = CommonErrors.NewType("external_error") // ConcurrentUpdate is a type for concurrent update error ConcurrentUpdate = CommonErrors.NewType("concurrent_update") // TimeoutElapsed is a type for timeout error TimeoutElapsed = CommonErrors.NewType("timeout", Timeout()) // NotImplemented is an error type for lacking implementation NotImplemented = UnsupportedOperation.NewSubtype("not_implemented") // UnsupportedVersion is a type for unsupported version error UnsupportedVersion = UnsupportedOperation.NewSubtype("version") )
Functions ¶
func DecorateMany ¶
DecorateMany performs a transparent wrap of multiple errors with additional message. If there are no errors, or all errors are nil, returns nil. If all errors are of the same type (for example, if there is only one), wraps them transparently. Otherwise, an opaque wrap is performed, that is, IsOfType checks will fail on underlying error types.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err0 := someFunc() err1 := someFunc() err := errorx.DecorateMany("both calls failed", err0, err1) fmt.Println(err.Error()) } func someFunc() error { return errorx.AssertionFailed.New("example") }
Output: both calls failed, cause: common.assertion_failed: example (hidden: common.assertion_failed: example)
func ErrorFromPanic ¶
ErrorFromPanic recovers the original error from panic, best employed along with Panic() function from the same package. The original error, if present, typically holds more relevant data than a combination of panic message and the stack trace which can be collected after recover().
More importantly, it allows for greater composability, if ever there is a need to recover from panic and pass the error information forwards in its proper form.
Note that panic is not a proper means to report errors, so this mechanism should never be used where a error based control flow is at all possible.
func ExtractContext ¶
ExtractContext is a statically typed helper to extract a context property from an error.
func ExtractPayload ¶
ExtractPayload is a helper to extract a payload property from an error.
func ExtractProperty ¶
ExtractProperty attempts to extract a property value by a provided key. A property may belong to this error or be extracted from the original cause.
func GetTypeName ¶
GetTypeName returns the full type name if an error; returns an empty string for non-errorx error. For decorated errors, the type of an original cause is used.
func HasTrait ¶
HasTrait checks if an error possesses the expected trait. Traits are always properties of a type rather than of an instance, so trait check is an alternative to a type check. This alternative is preferable, though, as it is less brittle and generally creates less of a dependency.
func Ignore ¶
Ignore returns nil if an error is of one of the provided types, returns the provided error otherwise. May be used if a particular error signifies a mark in control flow rather than an error to be reported to the caller.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := errorx.IllegalArgument.NewWithNoMessage() err = errorx.Decorate(err, "more info") fmt.Println(err) fmt.Println(errorx.Ignore(err, errorx.IllegalArgument)) fmt.Println(errorx.Ignore(err, errorx.AssertionFailed)) }
Output: more info, cause: common.illegal_argument <nil> more info, cause: common.illegal_argument
func IgnoreWithTrait ¶
IgnoreWithTrait returns nil if an error has one of the provided traits, returns the provided error otherwise. May be used if a particular error trait signifies a mark in control flow rather than an error to be reported to the caller.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := errorx.TimeoutElapsed.NewWithNoMessage() err = errorx.Decorate(err, "more info") fmt.Println(err) fmt.Println(errorx.IgnoreWithTrait(err, errorx.Timeout())) fmt.Println(errorx.IgnoreWithTrait(err, errorx.NotFound())) }
Output: more info, cause: common.timeout <nil> more info, cause: common.timeout
func IsOfType ¶
IsOfType is a type check for errors. Returns true either if both are of exactly the same type, or if the same is true for one of current type's ancestors. Go 1.12 and below: for an error that does not have an errorx type, returns false. Go 1.13 and above: for an error that does not have an errorx type, returns false unless it wraps another error of errorx type.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err0 := errorx.DataUnavailable.NewWithNoMessage() err1 := errorx.Decorate(err0, "decorated") err2 := errorx.RejectedOperation.Wrap(err0, "wrapped") fmt.Println(errorx.IsOfType(err0, errorx.DataUnavailable)) fmt.Println(errorx.IsOfType(err1, errorx.DataUnavailable)) fmt.Println(errorx.IsOfType(err2, errorx.DataUnavailable)) }
Output: true true false
func Panic ¶
Panic is an alternative to the built-in panic call. When calling panic as a reaction to error, prefer this function over vanilla panic(). If err happens to be an errorx error, it may hold the original stack trace of the issue. With panic(err), this information may be lost if panic is handled by the default handler. With errorx.Panic(err), all data is preserved regardless of the handle mechanism. It can be recovered either from default panic message, recover() result or ErrorFromPanic() function.
Even if err stack trace is exactly the same as default panic trace, this can be tolerated, as panics must not be a way to report conventional errors and are therefore rare. With this in mind, it is better to err on the side of completeness rather than brevity.
This function never returns, but the signature may be used for convenience:
return nil, errorx.Panic(err) panic(errorx.Panic(err))
func RegisterTypeSubscriber ¶
func RegisterTypeSubscriber(s TypeSubscriber)
RegisterTypeSubscriber adds a new TypeSubscriber. A subscriber is guaranteed to receive callbacks for all namespaces and types. If a type is already registered at the moment of subscription, a callback for this type is called immediately.
func ReplicateError ¶
ReplicateError is a utility function to duplicate error N times. May be handy do demultiplex a single original error to a number of callers/requests.
Types ¶
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error is an instance of error object. At the moment of creation, Error collects information based on context, creation modifiers and type it belongs to. Error is mostly immutable, and distinct errors composition is achieved through wrap.
func Decorate ¶
Decorate allows to pass some text info along with a message, leaving its semantics totally intact. Perceived type, traits and properties of the resulting error are those of the original. Without args, leaves the provided message intact, so a message may be generated or provided externally. With args, a formatting is performed, and it is therefore expected a format string to be constant.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := someFunc() fmt.Println(err.Error()) err = errorx.Decorate(err, "decorate") fmt.Println(err.Error()) err = errorx.Decorate(err, "outer decorate") fmt.Println(err.Error()) } func someFunc() error { return errorx.AssertionFailed.New("example") }
Output: common.assertion_failed: example decorate, cause: common.assertion_failed: example outer decorate, cause: decorate, cause: common.assertion_failed: example
func EnhanceStackTrace ¶
EnhanceStackTrace has all the properties of the Decorate() method and additionally extends the stack trace of the original error. Designed to be used when a original error is passed from another goroutine rather than from a direct method call. If, however, it is called in the same goroutine, formatter makes some moderated effort to remove duplication.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { errCh := make(chan error) go func() { errCh <- nestedCall() }() err := <-errCh verboseOutput := fmt.Sprintf("Error full: %+v", errorx.EnhanceStackTrace(err, "another goroutine")) fmt.Println(verboseOutput) // Example output: //Error full: another goroutine, cause: common.assertion_failed: example // at github.com/zignd_test.ExampleEnhanceStackTrace() // /Users/username/go/src/github.com/zignd/example_test.go:94 // at testing.runExample() // /usr/local/Cellar/go/1.10.3/libexec/src/testing/example.go:122 // at testing.runExamples() // /usr/local/Cellar/go/1.10.3/libexec/src/testing/example.go:46 // at testing.(*M).Run() // /usr/local/Cellar/go/1.10.3/libexec/src/testing/testing.go:979 // at main.main() // _testmain.go:146 // ... // (1 duplicated frames) // ---------------------------------- // at github.com/zignd_test.someFunc() // /Users/username/go/src/github.com/zignd/example_test.go:106 // at github.com/zignd_test.nestedCall() // /Users/username/go/src/github.com/zignd/example_test.go:102 // at github.com/zignd_test.ExampleEnhanceStackTrace.func1() // /Users/username/go/src/github.com/zignd/example_test.go:90 // at runtime.goexit() // /usr/local/Cellar/go/1.10.3/libexec/src/runtime/asm_amd64.s:2361 } func nestedCall() error { return someFunc() } func someFunc() error { return errorx.AssertionFailed.New("example") }
Output:
func EnsureStackTrace ¶
EnsureStackTrace is a utility to ensure the stack trace is captured in provided error. If this is already true, it is returned unmodified. Otherwise, it is decorated with stack trace.
func WithContext ¶
WithContext is a statically typed helper to add a context property to an error.
func WithPayload ¶
WithPayload is a helper to add a payload property to an error.
func (*Error) Cause ¶
Cause returns the immediate (wrapped) cause of current error. This method could be used to dig for root cause of the error, but it is not advised to do so. Errors should not require a complex navigation through causes to be properly handled, and the need to do so is a code smell. Manually extracting cause defeats features such as opaque wrap, behaviour of properties etc. This method is, therefore, reserved for system utilities, not for general use.
func (*Error) Error ¶
Error implements the error interface. A result is the same as with %s formatter and does not contain a stack trace.
func (*Error) Format ¶
Format implements the Formatter interface. Supported verbs:
%s simple message output %v same as %s %+v full output complete with a stack trace
In is nearly always preferable to use %+v format. If a stack trace is not required, it should be omitted at the moment of creation rather in formatting.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := nestedCall() simpleOutput := fmt.Sprintf("Error short: %v\n", err) verboseOutput := fmt.Sprintf("Error full: %+v", err) fmt.Println(simpleOutput) fmt.Println(verboseOutput) // Example output: //Error short: common.assertion_failed: example // //Error full: common.assertion_failed: example // at github.com/zignd_test.someFunc() // /Users/username/go/src/github.com/zignd/example_test.go:102 // at github.com/zignd_test.nestedCall() // /Users/username/go/src/github.com/zignd/example_test.go:98 // at github.com/zignd_test.ExampleError_Format() // /Users/username/go/src/github.com/zignd/example_test.go:66 // <...> more } func nestedCall() error { return someFunc() } func someFunc() error { return errorx.AssertionFailed.New("example") }
Output:
func (*Error) HasTrait ¶
HasTrait checks if an error possesses the expected trait. Trait check works just as a type check would: opaque wrap hides the traits of the cause. Traits are always properties of a type rather than of an instance, so trait check is an alternative to a type check. This alternative is preferable, though, as it is less brittle and generally creates less of a dependency.
func (*Error) Is ¶
Is returns true if and only if target is errorx error that passes errorx type check against current error. This behaviour is exactly the same as that of IsOfType(). See also: errors.Is()
func (*Error) IsOfType ¶
IsOfType is a proper type check for an errorx-based errors. It takes the transparency and error types hierarchy into account, so that type check against any supertype of the original cause passes. Go 1.13 and above: it also tolerates non-errorx errors in chain if those errors support errors unwrap.
func (*Error) IsOfTypeIgnoreTransparent ¶
func (*Error) Message ¶
Message returns a message of this particular error, disregarding the cause. The result of this method, like a result of an Error() method, should never be used to infer the meaning of an error. In most cases, message is only used as a part of formatting to print error contents into a log file. Manual extraction may be required, however, to transform an error into another format - say, API response.
func (*Error) Property ¶
Property extracts a dynamic property value from an error. A property may belong to this error or be extracted from the original cause. The transparency rules are respected to some extent: both the original cause and the transparent wrapper may have accessible properties, but an opaque wrapper hides the original properties.
func (*Error) Type ¶
Type returns the exact type of this error. With transparent wrapping, such as in Decorate(), returns the type of the original cause. The result is always not nil, even if the resulting type is impossible to successfully type check against.
NB: the exact error type may fail an equality check where a IsOfType() check would succeed. This may happen if a type is checked against one of its supertypes, for example. Therefore, handle direct type checks with care or avoid it altogether and use TypeSwitch() or IsForType() instead.
func (*Error) Unwrap ¶
From errors package: if e.Unwrap() returns a non-nil error w, then we say that e wraps w. Unwrap returns cause of current error in case it is wrapped transparently, nil otherwise. See also: errors.Unwrap()
func (*Error) WithProperty ¶
WithProperty adds a dynamic property to error instance. If an error already contained another value for the same property, it is overwritten. It is a caller's responsibility to accumulate and update a property, if needed. Dynamic properties is a brittle mechanism and should therefore be used with care and in a simple and robust manner. Currently, properties are implemented as a linked list, therefore it is not safe to have many dozens of them. But couple of dozen is just ok.
func (*Error) WithUnderlyingErrors ¶
WithUnderlyingErrors adds multiple additional related (hidden, suppressed) errors to be used exclusively in error output. Note that these errors make no other effect whatsoever: their traits, types, properties etc. are lost on the observer. Consider using errorx.DecorateMany instead.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { fn := func() error { bytes, err := getBodyAndError() if err != nil { _, unmarshalErr := getDetailsFromBody(bytes) if unmarshalErr != nil { return errorx.AssertionFailed.Wrap(err, "failed to read details").WithUnderlyingErrors(unmarshalErr) } } return nil } fmt.Println(fn().Error()) } func getBodyAndError() ([]byte, error) { return nil, errorx.AssertionFailed.New("example") } func getDetailsFromBody(s []byte) (string, error) { return "", errorx.IllegalFormat.New(string(s)) }
Output: common.assertion_failed: failed to read details, cause: common.assertion_failed: example (hidden: common.illegal_format)
type ErrorBuilder ¶
type ErrorBuilder struct {
// contains filtered or unexported fields
}
ErrorBuilder is a utility to compose an error from type. Typically, a direct usage is not required: either Type methods of helpers like Decorate are sufficient. Only use builder if no simpler alternative is available.
func NewErrorBuilder ¶
func NewErrorBuilder(t *Type) ErrorBuilder
NewErrorBuilder creates error builder from an existing error type.
func (ErrorBuilder) Create ¶
func (eb ErrorBuilder) Create() *Error
Create returns an error with specified params.
func (ErrorBuilder) EnhanceStackTrace ¶
func (eb ErrorBuilder) EnhanceStackTrace() ErrorBuilder
EnhanceStackTrace is a signal to collect the current stack trace along with the original one, and use both in formatting. If the original error does not hold a stack trace for whatever reason, it will be collected it this point. This is typically a way to handle an error received from another goroutine - say, a worker pool. When stack traces overlap, formatting makes a conservative attempt not to repeat itself, preserving the *original* stack trace in its entirety.
func (ErrorBuilder) Transparent ¶
func (eb ErrorBuilder) Transparent() ErrorBuilder
Transparent makes a wrap transparent rather than opaque (default). Transparent wrap hides the current error type from the type checks and exposes the error type of the cause instead. The same holds true for traits, and the dynamic properties are visible from both cause and transparent wrapper. Note that if the cause error is non-errorx, transparency will still hold, type check against wrapper will still fail.
func (ErrorBuilder) WithCause ¶
func (eb ErrorBuilder) WithCause(err error) ErrorBuilder
WithCause provides an original cause for error. For non-errorx errors, a stack trace is collected. Otherwise, it is inherited by default, as error wrapping is typically performed 'en passe'. Note that even if an original error explicitly omitted the stack trace, it could be added on wrap.
func (ErrorBuilder) WithConditionallyFormattedMessage ¶
func (eb ErrorBuilder) WithConditionallyFormattedMessage(message string, args ...interface{}) ErrorBuilder
WithConditionallyFormattedMessage provides a message for an error in flexible format, to simplify its usages. Without args, leaves the original message intact, so a message may be generated or provided externally. With args, a formatting is performed, and it is therefore expected a format string to be constant.
type Namespace ¶
type Namespace struct {
// contains filtered or unexported fields
}
Namespace is a way go group a number of error types together, and each error type belongs to exactly one namespace. Namespaces may form hierarchy, with child namespaces inheriting the traits and modifiers of a parent. Those modifiers and traits are then passed upon all error types in the namespace. In formatting, a dot notation is used, for example:
namespace.sub_namespace.type.subtype
func NewNamespace ¶
NewNamespace defines a namespace with a name and, optionally, a number of inheritable traits.
func (Namespace) ApplyModifiers ¶
func (n Namespace) ApplyModifiers(modifiers ...TypeModifier) Namespace
ApplyModifiers makes a one-time modification of defaults in error creation.
func (Namespace) IsNamespaceOf ¶
IsNamespaceOf checks whether or not an error belongs either to this namespace or some of its sub-namespaces.
func (Namespace) Key ¶
func (n Namespace) Key() NamespaceKey
Key returns a comparison key for namespace.
func (Namespace) NewSubNamespace ¶
NewSubNamespace defines a child namespace that inherits all that is defined for a parent and, optionally, adds some more.
func (Namespace) NewType ¶
NewType creates a new type within a namespace that inherits all that is defined for namespace and, optionally, adds some more.
type NamespaceKey ¶
type NamespaceKey struct {
// contains filtered or unexported fields
}
NamespaceKey is a comparable descriptor of a Namespace.
type Property ¶
type Property struct {
// contains filtered or unexported fields
}
Property is a key to a dynamic property of an error. Property value belongs to an error instance only, never inherited from a type. Property visibility is hindered by Wrap, preserved by Decorate.
func PropertyContext ¶
func PropertyContext() Property
PropertyContext is a context property, value is expected to be of context.Context type.
func PropertyPayload ¶
func PropertyPayload() Property
PropertyPayload is a payload property, value may contain user defined structure with arbitrary data passed along with an error.
func RegisterPrintableProperty ¶
RegisterPrintableProperty registers a new property key for informational value. It is used both to add a dynamic property to an error instance, and to extract property value back from error. Printable property will be included in Error() message, both name and value.
func RegisterProperty ¶
RegisterProperty registers a new property key. It is used both to add a dynamic property to an error instance, and to extract property value back from error.
type StackTraceFilePathTransformer ¶
StackTraceFilePathTransformer is a used defined transformer for file path in stack trace output.
func InitializeStackTraceTransformer ¶
func InitializeStackTraceTransformer(transformer StackTraceFilePathTransformer) (StackTraceFilePathTransformer, error)
InitializeStackTraceTransformer provides a transformer to be used in formatting of all the errors. It is OK to leave it alone, stack trace will retain its exact original information. This feature may be beneficial, however, if a shortening of file path will make it more convenient to use. One of such examples is to transform a project-related path from absolute to relative and thus more IDE-friendly.
NB: error is returned if a transformer was already registered. Transformer is changed nonetheless, the old one is returned along with an error. User is at liberty to either ignore it, panic, reinstate the old transformer etc.
type Trait ¶
type Trait struct {
// contains filtered or unexported fields
}
Trait is a static characteristic of an error type. All errors of a specific type possess exactly the same traits. Traits are both defined along with an error and inherited from a supertype and a namespace.
func CaseNoError ¶
func CaseNoError() Trait
CaseNoError is a synthetic trait used in TraitSwitch, signifying an absence of error.
func CaseNoTrait ¶
func CaseNoTrait() Trait
CaseNoTrait is a synthetic trait used in TraitSwitch, signifying a presence of non-nil error that lacks specified traits.
func Duplicate ¶
func Duplicate() Trait
Duplicate is a trait that marks such an error where an update is failed as a duplicate.
func NotFound ¶
func NotFound() Trait
NotFound is a trait that marks such an error where the requested object is not found.
func RegisterTrait ¶
RegisterTrait declares a new distinct traits. Traits are matched exactly, distinct traits are considered separate event if they have the same label.
func Temporary ¶
func Temporary() Trait
Temporary is a trait that signifies that an error is temporary in nature.
func Timeout ¶
func Timeout() Trait
Timeout is a trait that signifies that an error is some sort iof timeout.
func TraitSwitch ¶
TraitSwitch is used to perform a switch around the trait of an error. For nil errors, returns CaseNoError(). For error types that lack any of the provided traits, including non-errorx errors, CaseNoTrait() is returned. It is safe to treat CaseNoTrait() as 'any other kind of not-nil error' case. The effect is equivalent to a series of HasTrait() checks.
NB: if more than one provided types matches the error, the first match in the providers list is recognised.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := errorx.TimeoutElapsed.NewWithNoMessage() switch errorx.TraitSwitch(err, errorx.Timeout()) { case errorx.Timeout(): fmt.Println("good") case errorx.CaseNoError(): fmt.Println("bad") default: fmt.Println("bad") } switch errorx.TraitSwitch(nil, errorx.Timeout()) { case errorx.Timeout(): fmt.Println("bad") case errorx.CaseNoError(): fmt.Println("good") default: fmt.Println("bad") } switch errorx.TraitSwitch(err, errorx.NotFound()) { case errorx.NotFound(): fmt.Println("bad") case errorx.CaseNoError(): fmt.Println("bad") default: fmt.Println("good") } }
Output: good good good
type Type ¶
type Type struct {
// contains filtered or unexported fields
}
Type is a distinct error type. Belongs to a namespace, may be a descendant of another type in the same namespace. May contain or inherit modifiers that alter the default properties for any error of this type. May contain or inherit traits that all errors of this type will possess.
func NotRecognisedType ¶
func NotRecognisedType() *Type
NotRecognisedType is a synthetic type used in TypeSwitch, signifying a presence of non-nil error of some other type.
func TypeSwitch ¶
TypeSwitch is used to perform a switch around the type of an error. For nil errors, returns nil. For error types not in the 'types' list, including non-errorx errors, NotRecognisedType() is returned. It is safe to treat NotRecognisedType() as 'any other type of not-nil error' case. The effect is equivalent to a series of IsOfType() checks.
NB: if more than one provided types matches the error, the first match in the providers list is recognised.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { err := errorx.DataUnavailable.NewWithNoMessage() switch errorx.TypeSwitch(err, errorx.DataUnavailable) { case errorx.DataUnavailable: fmt.Println("good") case nil: fmt.Println("bad") default: fmt.Println("bad") } switch errorx.TypeSwitch(nil, errorx.DataUnavailable) { case errorx.DataUnavailable: fmt.Println("bad") case nil: fmt.Println("good") default: fmt.Println("bad") } switch errorx.TypeSwitch(err, errorx.TimeoutElapsed) { case errorx.TimeoutElapsed: fmt.Println("bad") case nil: fmt.Println("bad") default: fmt.Println("good") } }
Output: good good good
func (*Type) ApplyModifiers ¶
func (t *Type) ApplyModifiers(modifiers ...TypeModifier) *Type
ApplyModifiers makes a one-time modification of defaults in error creation.
func (*Type) FullName ¶
FullName returns a fully qualified name if type, is not presumed to be unique, see TypeSubscriber.
func (*Type) IsOfType ¶
IsOfType is a type check for error. Returns true either if both are of exactly the same type, or if the same is true for one of current type's ancestors.
func (*Type) MarshalText ¶
MarshalText implements encoding.TextMarshaler
func (*Type) New ¶
New creates an error of this type with a message. Without args, leaves the original message intact, so a message may be generated or provided externally. With args, a formatting is performed, and it is therefore expected a format string to be constant.
func (*Type) NewSubtype ¶
NewSubtype defines a new subtype within a namespace of a parent type.
func (*Type) NewWithNoMessage ¶
NewWithNoMessage creates an error of this type without any message. May be used when other information is sufficient, such as error type and stack trace.
func (*Type) RootNamespace ¶
RootNamespace returns a base namespace this type belongs to.
func (*Type) Wrap ¶
Wrap creates an error of this type with another as original cause. As far as type checks are concerned, this error is the only one visible, with original present only in error message. The original error will not pass its dynamic properties, and those are accessible only via direct walk over Cause() chain. Without args, leaves the original message intact, so a message may be generated or provided externally. With args, a formatting is performed, and it is therefore expected a format string to be constant. NB: Wrap is NOT the reverse of errors.Unwrap() or Error.Unwrap() method; name may be changed in future releases to avoid confusion.
Example ¶
package main import ( "fmt" "github.com/zignd/errorx" ) func main() { originalErr := errorx.IllegalArgument.NewWithNoMessage() err := errorx.AssertionFailed.Wrap(originalErr, "wrapped") fmt.Println(errorx.IsOfType(originalErr, errorx.IllegalArgument)) fmt.Println(errorx.IsOfType(err, errorx.IllegalArgument)) fmt.Println(errorx.IsOfType(err, errorx.AssertionFailed)) fmt.Println(err.Error()) }
Output: true false true common.assertion_failed: wrapped, cause: common.illegal_argument
func (*Type) WrapWithNoMessage ¶
WrapWithNoMessage creates an error of this type with another as original cause and with no additional message. May be used when other information is sufficient, such as error type, cause and its stack trace and message. As far as type checks are concerned, this error is the only one visible, with original visible only in error message. The original error will, however, pass its dynamic properties.
type TypeModifier ¶
type TypeModifier int
TypeModifier is a way to change a default behaviour for an error type, directly or via type hierarchy. Modification is intentionally one-way, as it provides much more clarity. If there is a modifier on a type or a namespace, all its descendants definitely have the same default behaviour. If some of a subtypes must lack a specific modifier, then the modifier must be removed from the common ancestor.
const ( // TypeModifierTransparent is a type modifier; an error type with such modifier creates transparent wrappers by default TypeModifierTransparent TypeModifier = 1 // TypeModifierOmitStackTrace is a type modifier; an error type with such modifier omits the stack trace collection upon creation of an error instance TypeModifierOmitStackTrace TypeModifier = 2 )
type TypeSubscriber ¶
type TypeSubscriber interface { // OnNamespaceCreated is called exactly once for each namespace OnNamespaceCreated(namespace Namespace) // OnTypeCreated is called exactly once for each type OnTypeCreated(t *Type) }
TypeSubscriber is an interface to receive callbacks on the registered error namespaces and types. This may be used to create a user-defined registry, for example, to check if all type names are unique. ISSUE: if .ApplyModifiers is called for a type/namespace, callback still receives a value without those modifiers.