errorx package - github.com/joomcode/errorx - Go Packages (original) (raw)

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.

This section is empty.

View Source

var (

CommonErrors = [NewNamespace](#NewNamespace)("common")


IllegalArgument = [CommonErrors](#CommonErrors).NewType("illegal_argument")

IllegalState = [CommonErrors](#CommonErrors).NewType("illegal_state")

IllegalFormat = [CommonErrors](#CommonErrors).NewType("illegal_format")

InitializationFailed = [CommonErrors](#CommonErrors).NewType("initialization_failed")

DataUnavailable = [CommonErrors](#CommonErrors).NewType("data_unavailable")

UnsupportedOperation = [CommonErrors](#CommonErrors).NewType("unsupported_operation")

RejectedOperation = [CommonErrors](#CommonErrors).NewType("rejected_operation")

Interrupted = [CommonErrors](#CommonErrors).NewType("interrupted")

AssertionFailed = [CommonErrors](#CommonErrors).NewType("assertion_failed")

InternalError = [CommonErrors](#CommonErrors).NewType("internal_error")

ExternalError = [CommonErrors](#CommonErrors).NewType("external_error")

ConcurrentUpdate = [CommonErrors](#CommonErrors).NewType("concurrent_update")

TimeoutElapsed = [CommonErrors](#CommonErrors).NewType("timeout", [Timeout](#Timeout)())

NotImplemented = [UnsupportedOperation](#UnsupportedOperation).NewSubtype("not_implemented")

UnsupportedVersion = [UnsupportedOperation](#UnsupportedOperation).NewSubtype("version")

)

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.

package main

import ( "fmt" "github.com/joomcode/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(recoverResult interface{}) (error, bool)

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.

ExtractContext is a statically typed helper to extract a context property from an error.

func ExtractPayload(err error) (interface{}, bool)

ExtractPayload is a helper to extract a payload property from an error.

func ExtractProperty(err error, key Property) (interface{}, bool)

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.

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.

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.

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.

package main

import ( "fmt" "github.com/joomcode/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 more info, cause: common.illegal_argument

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.

package main

import ( "fmt" "github.com/joomcode/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 more info, cause: common.timeout

IsDuplicate checks for Duplicate trait.

IsNotFound checks for NotFound trait.

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.

package main

import ( "fmt" "github.com/joomcode/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

IsTemporary checks for Temporary trait.

IsTimeout checks for Timeout trait.

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(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.

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.

WrapMany is a utility to wrap multiple errors. If there are no errors, or all errors are nil, returns nil. Otherwise, the fist error is treated as an original cause, others are added as underlying.

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.

Cast attempts to cast an error to errorx Type, returns nil if cast has failed.

func Decorate(err error, message string, args ...interface{}) *Error

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.

package main

import ( "fmt" "github.com/joomcode/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(err error, message string, args ...interface{}) *Error

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.

package main

import ( "fmt" "github.com/joomcode/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/joomcode/errorx_test.ExampleEnhanceStackTrace()
//	/Users/username/go/src/github.com/joomcode/errorx/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/joomcode/errorx_test.someFunc()
//	/Users/username/go/src/github.com/joomcode/errorx/example_test.go:106
// at github.com/joomcode/errorx_test.nestedCall()
//	/Users/username/go/src/github.com/joomcode/errorx/example_test.go:102
// at github.com/joomcode/errorx_test.ExampleEnhanceStackTrace.func1()
//	/Users/username/go/src/github.com/joomcode/errorx/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") }

func EnsureStackTrace(err error) *Error

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.

WithContext is a statically typed helper to add a context property to an error.

func WithPayload(err *Error, payload interface{}) *Error

WithPayload is a helper to add a payload property to an error.

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.

Error implements the error interface. A result is the same as with %s formatter and does not contain a stack trace.

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.

package main

import ( "fmt" "github.com/joomcode/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/joomcode/errorx_test.someFunc()
//	/Users/username/go/src/github.com/joomcode/errorx/example_test.go:102
// at github.com/joomcode/errorx_test.nestedCall()
//	/Users/username/go/src/github.com/joomcode/errorx/example_test.go:98
// at github.com/joomcode/errorx_test.ExampleError_Format()
//	/Users/username/go/src/github.com/joomcode/errorx/example_test.go:66
// <...> more

}

func nestedCall() error { return someFunc() }

func someFunc() error { return errorx.AssertionFailed.New("example") }

func (e *Error) HasTrait(key Trait) bool

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.

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 (e *Error) IsOfType(t *Type) bool

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.

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 (e *Error) Property(key Property) (interface{}, bool)

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 (e *Error) Type() *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.

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 (e *Error) WithProperty(key Property, value interface{}) *Error

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 (e *Error) WithUnderlyingErrors(errs ...error) *Error

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.

package main

import ( "fmt" "github.com/joomcode/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 struct {

}

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(t *Type) ErrorBuilder

NewErrorBuilder creates error builder from an existing error type.

func (eb ErrorBuilder) Create() *Error

Create returns an error with specified params.

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 (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.

WithCause provides an original cause for error. For non-errorx errors, a stack trace is collected unless Type tells otherwise. 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 (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 struct {

}

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(name string, traits ...Trait) Namespace

NewNamespace defines a namespace with a name and, optionally, a number of inheritable traits.

func (n Namespace) ApplyModifiers(modifiers ...TypeModifier) Namespace

ApplyModifiers makes a one-time modification of defaults in error creation.

FullName returns a full name of a namespace.

func (n Namespace) IsNamespaceOf(t *Type) bool

IsNamespaceOf checks whether or not an error belongs either to this namespace or some of its sub-namespaces.

func (n Namespace) Key() NamespaceKey

Key returns a comparison key for namespace.

func (n Namespace) NewSubNamespace(name string, traits ...Trait) Namespace

NewSubNamespace defines a child namespace that inherits all that is defined for a parent and, optionally, adds some more.

func (n Namespace) NewType(typeName string, traits ...Trait) *Type

NewType creates a new type within a namespace that inherits all that is defined for namespace and, optionally, adds some more.

func (n Namespace) Parent() *Namespace

Parent returns the immediate parent namespace, if present. The use of this function outside of a system layer that handles error types (see TypeSubscriber) is a code smell.

type NamespaceKey struct {

}

NamespaceKey is a comparable descriptor of a Namespace.

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() Property

PropertyContext is a context property, value is expected to be of context.Context type.

func PropertyPayload() Property

PropertyPayload is a payload property, value may contain user defined structure with arbitrary data passed along with an error.

func RegisterPrintableProperty(label string) Property

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(label string) Property

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.

StackTraceFilePathTransformer is a used defined transformer for file path in stack trace output.

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.

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.

CaseNoError is a synthetic trait used in TraitSwitch, signifying an absence of error.

CaseNoTrait is a synthetic trait used in TraitSwitch, signifying a presence of non-nil error that lacks specified traits.

Duplicate is a trait that marks such an error where an update is failed as a duplicate.

NotFound is a trait that marks such an error where the requested object is not found.

func RegisterTrait(label string) Trait

RegisterTrait declares a new distinct traits. Traits are matched exactly, distinct traits are considered separate event if they have the same label.

Temporary is a trait that signifies that an error is temporary in nature.

Timeout is a trait that signifies that an error is some sort iof timeout.

func TraitSwitch(err error, traits ...Trait) Trait

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.

package main

import ( "fmt" "github.com/joomcode/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 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 NewType(namespace Namespace, name string, traits ...Trait) *Type

NewType defines a new distinct type within a namespace.

func NotRecognisedType() *Type

NotRecognisedType is a synthetic type used in TypeSwitch, signifying a presence of non-nil error of some other type.

func TypeSwitch(err error, types ...*Type) *Type

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.

package main

import ( "fmt" "github.com/joomcode/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 (t *Type) ApplyModifiers(modifiers ...TypeModifier) *Type

ApplyModifiers makes a one-time modification of defaults in error creation.

FullName returns a fully qualified name if type, is not presumed to be unique, see TypeSubscriber.

func (t *Type) HasTrait(key Trait) bool

HasTrait checks if a type possesses the expected trait.

func (t *Type) IsOfType(other *Type) bool

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 (t *Type) MarshalText() (text []byte, err error)

MarshalText implements encoding.TextMarshaler

func (t *Type) Namespace() Namespace

Namespace returns a namespace this type belongs to.

func (t *Type) New(message string, args ...interface{}) *Error

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 (t *Type) NewSubtype(name string, traits ...Trait) *Type

NewSubtype defines a new subtype within a namespace of a parent type.

func (t *Type) NewWithNoMessage() *Error

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 (t *Type) RootNamespace() Namespace

RootNamespace returns a base namespace this type belongs to.

func (t *Type) Supertype() *Type

Supertype returns a parent type, if present.

func (t *Type) Wrap(err error, message string, args ...interface{}) *Error

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.

package main

import ( "fmt" "github.com/joomcode/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 (t *Type) WrapWithNoMessage(err error) *Error

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.

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 [TypeModifier](#TypeModifier) = 1

TypeModifierOmitStackTrace [TypeModifier](#TypeModifier) = 2

)

type TypeSubscriber interface {

OnNamespaceCreated(namespace [Namespace](#Namespace))

OnTypeCreated(t *[Type](#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.