Documentation ¶
Overview ¶
Package queue allows streamlined error handling and piping of returned values.
This package is considered stable and ready for production. It requires Go >= 1.1.
Motivation:
In go, sometimes you need to run a bunch of functions that return errors and/or results. You might end up writing stuff like this
err = fn1(...) if err != nil { // handle error somehow } err = fn2(...) if err != nil { // handle error somehow } ...
a lot of times.
This is especially annoying if you want to handle all errors the same way (e.g. return the first error).
This package provides a way to call functions in a queue while collecting the errors via a predefined or custom error handler. The predefined handler returns on the first error and custom error handlers might be used to catch/handle some/all kinds of errors while keeping the queue running.
Usage:
... // create a new queue err := New(). // add function get to the queue that should be called with "Age" and m Add(get, "Age", m). // add function strconv.Atoi and pass the value returned from get via PIPE Add(strconv.Atoi, PIPE). // add method SetAge of p and pass the value returned from strconv.Atoi // note that the second return value error is not part of the pipe // it will however be sent to the error handler if it is not nil Add(p.SetAge, PIPE). ... .OnError(STOP) // optional custom error handler, STOP is default .Run() // run it, returning unhandled errors. - OR - .CheckAndRun() // if you want to check for type errors of the functions/arguments before the run ...
The functions in the queue are checked for the type of the last return value. If it is an error, the value will be checked when running the queue and the error handler is invoked if the error is not nil.
The error handler decides, if it can handle the error and the run continues (by returning nil) or if it can't and the run stops (by returning an/the error).
Custom error handlers must fullfill the ErrHandler interface.
When running the queue, the return values of the previous function with be injected into the argument list of the next function at the position of the pseudo argument PIPE. However, if the last return value is an error, it will be omitted.
There is also a different running mode invoked by the method Fallback() that runs the queue until the first function returns no error.
A package with shortcuts that has a more compact syntax and is better includable with dot (.) is provided at github.com/go-on/queue/q
Example ¶
package main import ( "fmt" "strconv" ) type person struct { Name string Age int } func (p *person) SetAge(i int) { p.Age = i } func (p *person) SetName(n string) error { if n == "Peter" { return fmt.Errorf("Peter is not allowed") } p.Name = n return nil } func getMap(k string, m map[string]string) string { return m[k] } func setStruct(p *person, m map[string]string, handler ErrHandler) { // create a new queue with the default error handler q := New(). // get the name from the map Add(getMap, "Name", m). // set the name in the struct Add(p.SetName, PIPE). // get the age from the map Add(getMap, "Age", m). // convert the age to int Add(strconv.Atoi, PIPE). // set the age in the struct Add(p.SetAge, PIPE). // inspect the struct Add(fmt.Printf, "SUCCESS %#v\n", p) // if a custom error handler is passed, use it, // otherwise the default error handler queue.STOP is used // which stops on the first error, returning it if handler != nil { q.OnError(handler) } // check the whole queue and run it err := q.CheckAndRun() // report, if there is an unhandled error if err != nil { fmt.Printf("ERROR %#v: %s\n", p, err) } } var ignoreAge = ErrHandlerFunc(func(err error) error { _, ok := err.(*strconv.NumError) if ok { return nil } return err }) func main() { var arthur = map[string]string{"Name": "Arthur", "Age": "42"} setStruct(&person{}, arthur, nil) var anne = map[string]string{"Name": "Anne", "Age": "4b"} // this will report the error of the invalid age that could not be parsed setStruct(&person{}, anne, nil) // this will ignore the invalid age, but no other errors setStruct(&person{}, anne, ignoreAge) var peter = map[string]string{"Name": "Peter", "Age": "4c"} // this will ignore the invalid age, but no other errors, so // it should err for the fact that peter is not allowed setStruct(&person{}, peter, ignoreAge) // this will ignore any errors and continue the queue run setStruct(&person{}, peter, IGNORE) }
Output: SUCCESS &queue.person{Name:"Arthur", Age:42} ERROR &queue.person{Name:"Anne", Age:0}: strconv.ParseInt: parsing "4b": invalid syntax SUCCESS &queue.person{Name:"Anne", Age:0} ERROR &queue.person{Name:"", Age:0}: Peter is not allowed SUCCESS &queue.person{Name:"", Age:0}
Index ¶
- Variables
- func Call(function interface{}, arguments ...interface{}) *call
- func CallNamed(name string, function interface{}, arguments ...interface{}) *call
- func Collect(val interface{}, vals ...interface{}) interface{}
- func Fallback(qs ...Queuer) callfallback
- func Get(ptr interface{}) interface{}
- func Ok()
- func Run(qs ...Queuer) callrun
- func Set(ptrToSet interface{}, val interface{})
- func Value(i interface{}) interface{}
- type CallPanic
- type ErrHandler
- type ErrHandlerFunc
- type InvalidArgument
- type InvalidFunc
- type Queue
- func (q *Queue) Add(function interface{}, arguments ...interface{}) *Queue
- func (q *Queue) AddNamed(name string, function interface{}, arguments ...interface{}) *Queue
- func (q *Queue) Check() (err error)
- func (q *Queue) CheckAndRun() (err error)
- func (q *Queue) LogDebugTo(logTarget io.Writer) *Queue
- func (q *Queue) LogErrorsTo(logTarget io.Writer) *Queue
- func (q *Queue) Name() string
- func (q *Queue) OnError(handler ErrHandler) *Queue
- func (q *Queue) Queue() *Queue
- func (q *Queue) Run() (err error)
- func (q *Queue) SetName(name string) *Queue
- func (q *Queue) Sub(feededQs ...Queuer) *Queue
- func (q *Queue) Tee(function interface{}, arguments ...interface{}) *Queue
- func (q *Queue) TeeAndCheckAndFallback(feededQs ...Queuer) *Queue
- func (q *Queue) TeeAndCheckAndRun(feededQs ...Queuer) *Queue
- func (q *Queue) TeeAndFallback(feededQs ...Queuer) *Queue
- func (q *Queue) TeeAndRun(feededQs ...Queuer) *Queue
- func (q *Queue) TeeNamed(name string, function interface{}, arguments ...interface{}) *Queue
- type Queuer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrHandler, stops on the first error STOP = ErrHandlerFunc(func(err error) error { return err }) // ErrHandler, ignores all errors IGNORE = ErrHandlerFunc(func(err error) error { return nil }) // ErrHandler, panics on the first error PANIC = ErrHandlerFunc(func(err error) error { panic(err.Error()) return err }) )
var PIPE = pipe{}
PIPE is a pseudo parameter that will be replaced by the returned non error values of the previous function
Functions ¶
func Collect ¶
func Collect(val interface{}, vals ...interface{}) interface{}
collects different values and puts them in an interface interface is a slice of the type of the first given value
Types ¶
type CallPanic ¶
type CallPanic struct { // position of the function in the queue Position int // type signature of the function Type string // arguments passed to the function Params []interface{} // error message ErrorMessage string // name of the function call, if it is named Name string }
Error returned if a function call triggered a panic
type ErrHandler ¶
type ErrHandler interface { // HandleError receives a non nil error and may handle it. // An error is considered not handled, if HandleError() returns the given error. // An error is considered handled, if HandleError() returns something other than // the given error. // An error is considered catched, if HandleError() returns nil. // If HandleError() catches an error, the queue run will continue. // Otherwise the queue will be stopped and the error is returned. // See Run() and Fallback() for more details about returning errors. HandleError(error) error }
Each Queue has an error handler that is called if a function returns an error.
The default error handler when calling Run() is STOP and when calling Fallback() is IGNORE. The error handler PANIC might be chosen to panic on the first error (some kind of "Must" for every function call).
type ErrHandlerFunc ¶
shortcut to let a func be an error handler
func (ErrHandlerFunc) HandleError ¶
func (f ErrHandlerFunc) HandleError(err error) error
type InvalidArgument ¶
type InvalidArgument struct { // position of the function in the queue Position int // type signature of the function Type string // error message ErrorMessage string // name of the function call, if it is named Name string }
Error returned if a function is not valid
func (InvalidArgument) Error ¶
func (i InvalidArgument) Error() string
type InvalidFunc ¶
type InvalidFunc struct { // position of the function in the queue Position int // type signature of the function Type string // error message ErrorMessage string // name of the function call, if it is named Name string }
func (InvalidFunc) Error ¶
func (i InvalidFunc) Error() string
type Queue ¶
type Queue struct {
// contains filtered or unexported fields
}
func New ¶
func New() *Queue
New creates a new function queue
Use Add() for adding calls to the Queue.
Use OnError() to set a custom error handler.
The default error handler is set by the runner function, Run() or Fallback().
Use one of these runner calls to run the queue.
func OnError ¶
func OnError(handler ErrHandler) (q *Queue)
OnError returns a new empty *Queue, where the errHandler is set to the given handler
More about adding functions to the Queue: see Add(). More about error handling and running a Queue: see Run() and Fallback().
func (*Queue) Add ¶
Add creates a call consisting of the given function and the given arguments and adds it to the call chain.
The special argument PIPE is a placeholder for the return values of the previous call in the chain (minus returned errors).
The number and type signature of the arguments and piped return values must match with the receiving function.
More about valid queues: see Check() More about function calling: see Run() and Fallback()
func (*Queue) AddNamed ¶
AddNamed behaves like Add, but names the call with the given name. This is useful for logging and debugging
func (*Queue) Check ¶
Check checks if the function signatures and argument types match and returns any errors
func (*Queue) CheckAndRun ¶
CheckAndRun first runs Check() to see, if there are any type errors in the function signatures or arguments and returns them. Without such errors, it then calls Run()
func (*Queue) LogDebugTo ¶
LogDebugTo logs debugging information to the given io.Writer with the given prefix
LogDebugTo() is an alternative to LogErrorsTo() and they should no be called both, because they are both changing the logging target and verbosity.
If more than one logging setter is called, only the last one has any effect.
func (*Queue) LogErrorsTo ¶
LogErrorsTo logs errors and panics to the given io.Writer with the given prefix
LogErrorsTo() is an alternative to LogDebugTo() and they should no be called both, because they are both changing the logging target and verbosity.
If more than one logging setter is called, only the last one has any effect.
func (*Queue) OnError ¶
func (q *Queue) OnError(handler ErrHandler) *Queue
OnError sets the errHandler and may be chained.
If OnError() is called multiple times, only the last call has any effect.
func (*Queue) Run ¶
Run runs the function queue.
In the run, every function in the queue is called with its arguments. If one of the arguments is PIPE, PIPE is replaced by the returned values of previous calls.
If the last return value of a function is of type error, it value is skipped when piping to the next function and the error is checked.
If the error is not nil, the ErrHandler of the Queue is called. If the ErrHandler returns nil, the next function is called. If it returns an error, the queue is stopped and the error is returned.
The default ErrHandler is STOP, which will stop the run on the first error.
If there are any errors with the given function types and arguments, the errors will no be very descriptive. In this cases use CheckAndRun() to see if there are any errors in the function or argument types or use LogDebugTo to get detailed debugging informations.
Since no arguments are saved inside the queue, a queue might be run multiple times.
func (*Queue) Tee ¶
Tee allows piping of the same return value to different function calls.
The return values from the given function are (apart from errors) discarded. If any of the arguments is the placeholder PIPE, it will be replaced by the return values of the previous regular call.
If the tee call returns an error, it will be passed to the error handler of the queue and may stop the next regular call.
If any check is performed on the queue, the tee calls are checked as well.
To tee into other queues, use TeeAndRun or TeeAndFallback
func (*Queue) TeeAndCheckAndFallback ¶
TeeAndCheckAndFallback tees the given queues and in the run checks them before running the Fallback method
func (*Queue) TeeAndCheckAndRun ¶
TeeAndCheckAndRun tees the given queues and in the run checks them before running the Run method
func (*Queue) TeeAndFallback ¶
TeeAndFallback works like TeeAndRun but runs the target queues via Fallback(). The position returned by the particular Fallback() call on the target queue is discarded.
func (*Queue) TeeAndRun ¶
TeeAndRun allows piping of the same return value to different queues.
The first call in each target queue should have the placeholder argument PIPE. When the main queue is run, each target queue is run via Run().
If the Run() call of a target queue returns an error, it will be passed to the error handler of the main queue and immediately stop further processing (of other target queues and the next regular call).
To be chainable, TeeAndRun returns the main queue.