Documentation
¶
Overview ¶
More Expressive Error Patterns.
Embed `meep` types in your errors structures to effortlessly get fancy behaviors. Stacks, automatic message generation from your fields, common handling: declaring useful error types is now *much* easier.
Index ¶
- func DamageControl(handle func(error))
- func Meep(err error, opts ...Opts) error
- func New(err error, opts ...Opts) error
- func RecoverPanics(fn func()) (e error)
- func Try(fn func(), plan TryPlan)
- func TryHandlerDiscard(_ error)
- type AllTraits
- type ErrInvalidParam
- type ErrNotYetImplemented
- type ErrProgrammer
- type ErrUnderspecified
- type ErrUntypedPanic
- type Opts
- type Stack
- type StackFrame
- type TraitAutodescribing
- type TraitCausable
- type TraitTraceable
- type TryHandler
- type TryPlan
- type TryRoute
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DamageControl ¶
func DamageControl(handle func(error))
Use `DamageControl` for a safe, terse default mechanism to gather panics before they crash your program.
Damage control can be used like this:
defer DamageControl(func(e error) { errCh <- e })
Using damage control in this way at the top of all your goroutines is advised if there's the slightest possibility of panics arising. Typically, pushing the error into a channel handled by the spawning goroutine is a good response.
`DamageControl` uses `recover()` internally. Note that this means you must defer `DamageControl` itself (you cannot defer another func which calls `DamageControl`; `recover` doesn't work like that).
The error type given to the `handle` function will always be `*ErrUnderspecified`. If you have different, more specific error handling paths and types in mind, you should express those by writing your own recovers.
func RecoverPanics ¶
func RecoverPanics(fn func()) (e error)
Invokes your function, captures any panics, and returns them.
This is simply shorthand for writing your own defers/recovers.
Note that RecoverPanics returns `error` rather than `interface{}`. Any values panicked which do not match the `error` interface will be wrapped in the `ErrUntypedPanic` type, with the original value in the `Cause` field.
func Try ¶
func Try(fn func(), plan TryPlan)
Invokes your function, captures any panics, and routes them through the TryPlan.
This may be superior to calling a function that returns an error and calling `TryPlan.MustHandle` yourself, because any panics that *do* occur will retain a stack including their original panic location until the TryPlan evaluates, meaning you can capture it properly with any error with the `meep.TraitTraceable` property.
Example ¶
meep.Try(func() { panic(meep.New(&meep.AllTraits{})) }, meep.TryPlan{ {ByType: &meep.ErrInvalidParam{}, Handler: meep.TryHandlerMapto(&meep.ErrProgrammer{})}, {ByVal: io.EOF, Handler: meep.TryHandlerDiscard}, {CatchAny: true, Handler: func(error) { fmt.Println("caught wildcard") }}, })
Output: caught wildcard
func TryHandlerDiscard ¶
func TryHandlerDiscard(_ error)
Types ¶
type AllTraits ¶
type AllTraits struct { TraitTraceable TraitCausable TraitAutodescribing }
Bundles all the behaviors for quick and easy use!
type ErrInvalidParam ¶
type ErrInvalidParam struct { TraitAutodescribing TraitTraceable Param string Reason string }
type ErrNotYetImplemented ¶
type ErrNotYetImplemented struct { TraitAutodescribing TraitTraceable }
type ErrProgrammer ¶
type ErrProgrammer struct { Msg string TraitAutodescribing TraitTraceable TraitCausable }
type ErrUnderspecified ¶
type ErrUnderspecified struct { TraitAutodescribing TraitTraceable TraitCausable }
A default type for grabbag, underspecified errors; it is the type used by `DamageControl` to wrap recovered panics.
type ErrUntypedPanic ¶
type ErrUntypedPanic struct { TraitAutodescribing TraitTraceable Cause interface{} }
A wrapper for non-error types raised from a panic.
The `Try` system will coerce all non-error types to this automatically.
type Opts ¶
type Opts struct {
// contains filtered or unexported fields
}
type Stack ¶
type Stack struct {
Frames []StackFrame
}
func CaptureStack ¶
func CaptureStack() *Stack
Captures a trace of the current stack.
You probably won't want to use this directly; instead, use a `TraitTraceable` like this:
//type ErrXYZ struct { TraitTraceable } err := meep.New(&ErrXYZ{}) // `err` now automatically has a stack capture!
There's nothing more magical here than `runtime.Callers`; just some additional types and prettyprinting which are absent from stdlib `runtime` because of stdlib's necessary avoidance of invoking complex features in that (zerodep!) package.
type StackFrame ¶
type StackFrame uintptr
func (StackFrame) String ¶
func (pc StackFrame) String() string
`String` returns a human readable form of the frame.
The string includes the path to the file and the linenumber associated with the frame, formatted to match the `file:lineno: ` convention (so your IDE, if it supports that convention, may let you click-to-jump); following the source location info, the function name is suffixed.
type TraitAutodescribing ¶
type TraitAutodescribing struct {
// contains filtered or unexported fields
}
Errors that generate their messages automatically from their fields!
func (*TraitAutodescribing) Error ¶
func (m *TraitAutodescribing) Error() string
Implements `error`.
If you're using other mixins, you may want to override this again; if you're just using `Autodescriber`, it'll do fine.
func (*TraitAutodescribing) ErrorMessage ¶
func (m *TraitAutodescribing) ErrorMessage() string
type TraitCausable ¶
type TraitCausable struct {
Cause error
}
Errors with other errors as their cause!
type TraitTraceable ¶
type TraitTraceable struct {
Stack Stack
}
Errors with stacks!
func (TraitTraceable) IsStackSet ¶
func (m TraitTraceable) IsStackSet() bool
func (TraitTraceable) StackString ¶
func (m TraitTraceable) StackString() string
Return the stack of the error formatted as a human readable string: one frame per line. Each line lists the source file, line number, and the name of the function.
func (TraitTraceable) WriteStack ¶
func (m TraitTraceable) WriteStack(w io.Writer)
Same job as StackString; use StackString for convenience, use this for performance.
type TryHandler ¶
type TryHandler func(error)
func TryHandlerMapto ¶
func TryHandlerMapto(toTmpl interface{}) TryHandler
type TryPlan ¶
type TryPlan []TryRoute
TryPlan is a declarative error handling plan.
You can dispatch errors according to several patterns, since both the golang stdlib and many libraries have seen fit to use a variety of different patterns, and they can't easily be distinguished by type alone (e.g. should we typeswitch, or do we need to do actual val/ptr compare):
TryRoute{ByType: exampleVal error, Handler: fn} TryRoute{ByVal: ptrOrVal error, Handler: fn} TryRoute{ByFunc: func(error) bool, Handler: fn} TryRoute{CatchAny: true, Handler: fn}
The `By*` fields are used to check whether an error should be handled by that route; then the handler is called when a route matches. Errors are checked against routes in the order they're listed in your TryPlan.
Use `ByType` as much as you can. Meep's typed error helpers should make typed errors your default coding style.
Use `ByVal` where you have to; `io.EOF` is one you must check by value.
Use `ByFunc` as a last resort -- but if you really have to do something complicated, go for it.
If you want to catch *everything*, set the CatchAny flag. If using CatchAny, be sure to add it last -- since it matches any error, routes after it will never be called.
func (TryPlan) Handle ¶
Checks the TryPlan for handlers that match the error, and invokes the first one that does.
The return value is nil if we found and called a handler, or the original error if there was no matching handler.
If the error parameter was nil, no handler will be called, and the result will always be nil.
func (TryPlan) MustHandle ¶
Like `Handle(e)`, but will panic if the (non-nil) error is not handled.
type TryRoute ¶
type TryRoute struct { ByType interface{} ByVal interface{} ByFunc func(error) bool CatchAny bool Handler TryHandler }
A single route, used to compose a TryPlan.
Typical usage is to set a Handler function, and then treat the other fields as if it's a 'union' (that is, only set one of them), like this:
TryRoute{ByVal: io.EOF, Handler: func(e error) { fmt.Println(e) }}