summaryrefslogtreecommitdiffstats
path: root/vendor/gopkg.in/warnings.v0/warnings.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/warnings.v0/warnings.go')
-rw-r--r--vendor/gopkg.in/warnings.v0/warnings.go194
1 files changed, 194 insertions, 0 deletions
diff --git a/vendor/gopkg.in/warnings.v0/warnings.go b/vendor/gopkg.in/warnings.v0/warnings.go
new file mode 100644
index 0000000000..b849d1e3d9
--- /dev/null
+++ b/vendor/gopkg.in/warnings.v0/warnings.go
@@ -0,0 +1,194 @@
+// 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
+}