This commit is contained in:
193
vendor/gopkg.in/warnings.v0/warnings.go
generated
vendored
Normal file
193
vendor/gopkg.in/warnings.v0/warnings.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
// Package warnings implements error handling with non-fatal errors (warnings).
|
||||
//
|
||||
// A recurring pattern in Go programming is the following:
|
||||
//
|
||||
// func myfunc(params) error {
|
||||
// if err := doSomething(...); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := doSomethingElse(...); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if ok := doAnotherThing(...); !ok {
|
||||
// return errors.New("my error")
|
||||
// }
|
||||
// ...
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// This pattern allows interrupting the flow on any received error. But what if
|
||||
// there are errors that should be noted but still not fatal, for which the flow
|
||||
// should not be interrupted? Implementing such logic at each if statement would
|
||||
// make the code complex and the flow much harder to follow.
|
||||
//
|
||||
// Package warnings provides the Collector type and a clean and simple pattern
|
||||
// for achieving such logic. The Collector takes care of deciding when to break
|
||||
// the flow and when to continue, collecting any non-fatal errors (warnings)
|
||||
// along the way. The only requirement is that fatal and non-fatal errors can be
|
||||
// distinguished programmatically; that is a function such as
|
||||
//
|
||||
// IsFatal(error) bool
|
||||
//
|
||||
// must be implemented. The following is an example of what the above snippet
|
||||
// could look like using the warnings package:
|
||||
//
|
||||
// import "gopkg.in/warnings.v0"
|
||||
//
|
||||
// func isFatal(err error) bool {
|
||||
// _, ok := err.(WarningType)
|
||||
// return !ok
|
||||
// }
|
||||
//
|
||||
// func myfunc(params) error {
|
||||
// c := warnings.NewCollector(isFatal)
|
||||
// c.FatalWithWarnings = true
|
||||
// if err := c.Collect(doSomething()); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := c.Collect(doSomethingElse(...)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if ok := doAnotherThing(...); !ok {
|
||||
// if err := c.Collect(errors.New("my error")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// ...
|
||||
// return c.Done()
|
||||
// }
|
||||
//
|
||||
// For an example of a non-trivial code base using this library, see
|
||||
// gopkg.in/gcfg.v1
|
||||
//
|
||||
// Rules for using warnings
|
||||
//
|
||||
// - ensure that warnings are programmatically distinguishable from fatal
|
||||
// errors (i.e. implement an isFatal function and any necessary error types)
|
||||
// - ensure that there is a single Collector instance for a call of each
|
||||
// exported function
|
||||
// - ensure that all errors (fatal or warning) are fed through Collect
|
||||
// - ensure that every time an error is returned, it is one returned by a
|
||||
// Collector (from Collect or Done)
|
||||
// - ensure that Collect is never called after Done
|
||||
//
|
||||
// TODO
|
||||
//
|
||||
// - optionally limit the number of warnings (e.g. stop after 20 warnings) (?)
|
||||
// - consider interaction with contexts
|
||||
// - go vet-style invocations verifier
|
||||
// - semi-automatic code converter
|
||||
package warnings // import "gopkg.in/warnings.v0"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// List holds a collection of warnings and optionally one fatal error.
|
||||
type List struct {
|
||||
Warnings []error
|
||||
Fatal error
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (l List) Error() string {
|
||||
b := bytes.NewBuffer(nil)
|
||||
if l.Fatal != nil {
|
||||
fmt.Fprintln(b, "fatal:")
|
||||
fmt.Fprintln(b, l.Fatal)
|
||||
}
|
||||
switch len(l.Warnings) {
|
||||
case 0:
|
||||
// nop
|
||||
case 1:
|
||||
fmt.Fprintln(b, "warning:")
|
||||
default:
|
||||
fmt.Fprintln(b, "warnings:")
|
||||
}
|
||||
for _, err := range l.Warnings {
|
||||
fmt.Fprintln(b, err)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// A Collector collects errors up to the first fatal error.
|
||||
type Collector struct {
|
||||
// IsFatal distinguishes between warnings and fatal errors.
|
||||
IsFatal func(error) bool
|
||||
// FatalWithWarnings set to true means that a fatal error is returned as
|
||||
// a List together with all warnings so far. The default behavior is to
|
||||
// only return the fatal error and discard any warnings that have been
|
||||
// collected.
|
||||
FatalWithWarnings bool
|
||||
|
||||
l List
|
||||
done bool
|
||||
}
|
||||
|
||||
// NewCollector returns a new Collector; it uses isFatal to distinguish between
|
||||
// warnings and fatal errors.
|
||||
func NewCollector(isFatal func(error) bool) *Collector {
|
||||
return &Collector{IsFatal: isFatal}
|
||||
}
|
||||
|
||||
// Collect collects a single error (warning or fatal). It returns nil if
|
||||
// collection can continue (only warnings so far), or otherwise the errors
|
||||
// collected. Collect mustn't be called after the first fatal error or after
|
||||
// Done has been called.
|
||||
func (c *Collector) Collect(err error) error {
|
||||
if c.done {
|
||||
panic("warnings.Collector already done")
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if c.IsFatal(err) {
|
||||
c.done = true
|
||||
c.l.Fatal = err
|
||||
} else {
|
||||
c.l.Warnings = append(c.l.Warnings, err)
|
||||
}
|
||||
if c.l.Fatal != nil {
|
||||
return c.erorr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Done ends collection and returns the collected error(s).
|
||||
func (c *Collector) Done() error {
|
||||
c.done = true
|
||||
return c.erorr()
|
||||
}
|
||||
|
||||
func (c *Collector) erorr() error {
|
||||
if !c.FatalWithWarnings && c.l.Fatal != nil {
|
||||
return c.l.Fatal
|
||||
}
|
||||
if c.l.Fatal == nil && len(c.l.Warnings) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Note that a single warning is also returned as a List. This is to make it
|
||||
// easier to determine fatal-ness of the returned error.
|
||||
return c.l
|
||||
}
|
||||
|
||||
// FatalOnly returns the fatal error, if any, **in an error returned by a
|
||||
// Collector**. It returns nil if and only if err is nil or err is a List
|
||||
// with err.Fatal == nil.
|
||||
func FatalOnly(err error) error {
|
||||
l, ok := err.(List)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
return l.Fatal
|
||||
}
|
||||
|
||||
// WarningsOnly returns the warnings **in an error returned by a Collector**.
|
||||
func WarningsOnly(err error) []error {
|
||||
l, ok := err.(List)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return l.Warnings
|
||||
}
|
Reference in New Issue
Block a user