errutil

package module
v0.21.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2021 License: MIT Imports: 6 Imported by: 8

README

Errutil GoDoc Go Report Card

Some Go error helpers

Documentation

Overview

package errutil contains some simple error helpers

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Defer

func Defer(errp *error, f func() error)

Defer is for use when defering a function call that can return an error. If the referenced error is nil but the callback returns a non-nil error, it sets the reference to the value of the returned error.

Example

Calling Close() on an io.WriteCloser can return an important error encountered while flushing to disk. Don't risk missing them by using a plain defer w.Close(). Use errutil.Defer to capture the return value.

package main

import (
	"fmt"

	"github.com/carlmjohnson/errutil"
)

type closer struct{}

func (c closer) Close() error {
	return fmt.Errorf("<had problem closing!>")
}

func openThingie() (c closer, err error) { return }

// Calling Close() on an io.WriteCloser can return an important error
// encountered while flushing to disk. Don't risk missing them by
// using a plain defer w.Close(). Use errutil.Defer to capture the return value.
func main() {
	// If you just defer a close call, you can miss an error
	return1 := func() error {
		thing, err := openThingie()
		if err != nil {
			return err
		}
		defer thing.Close() // oh no, this returned an error!
		// do stuff...
		return nil
	}()
	fmt.Println(return1) // == <nil>

	// Use errutil.Defer and a named return to capture the error
	return2 := func() (err error) {
		thing, err := openThingie()
		if err != nil {
			return err
		}
		defer errutil.Defer(&err, thing.Close)
		// do stuff...
		return nil
	}()
	fmt.Println(return2) // == <had problem closing!>

}
Output:

<nil>
<had problem closing!>

func ExecParallel added in v0.20.1

func ExecParallel(fs ...func() error) error

ExecParallel runs the functions in separate goroutines and then merges the returned errors, if any. Any panics in goroutines will be caught and converted into errors.

Example
package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/carlmjohnson/errutil"
)

func main() {
	start := time.Now()
	err := errutil.ExecParallel(func() error {
		time.Sleep(1 * time.Second)
		return nil
	}, func() error {
		time.Sleep(1 * time.Second)
		return errors.New("one error")
	}, func() error {
		time.Sleep(1 * time.Second)
		panic("ahhh")
	})
	fmt.Println("ran parallel?", time.Since(start) < 2*time.Second)
	for i, suberr := range errutil.AsSlice(err) {
		fmt.Printf("error %d: %v\n", i+1, suberr)
	}
}
Output:

ran parallel? true
error 1: one error
error 2: panic: ahhh

func Merge

func Merge(errs ...error) error

Merge is a convenience method for making a Slice of errors and calling the Merge method.

Example
package main

import (
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func main() {
	// A function that sometimes returns an error
	called := 0
	someFunc := func() error {
		called++
		if called%2 == 0 {
			return fmt.Errorf("even error: %d!", called)
		}
		return nil
	}

	// We do a series of operations that might return an error.
	err := someFunc()

	// This time, it didn't return an error.
	fmt.Printf("%+v\n", err)

	// After each operation, we merge it into our existing error variable
	// then do the next operation.
	err = errutil.Merge(err, someFunc())
	err = errutil.Merge(err, someFunc())
	err = errutil.Merge(err, someFunc())

	// Finally, we can return the result
	fmt.Printf("%+v", err)

	// Or loop through results
	for i, suberr := range errutil.AsSlice(err) {
		fmt.Printf("suberr[%d]: %v\n", i, suberr)
	}

}
Output:

<nil>
2 errors:
	error 1: even error: 2!
	error 2: even error: 4!
suberr[0]: even error: 2!
suberr[1]: even error: 4!

func Prefix added in v0.21.1

func Prefix(errp *error, prefixformat string, a ...interface{})

Prefix will prefix an error with a fixed string if it is non-nil.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func main() {
	maybeErr1 := func(ok bool) (err error) {
		defer errutil.Prefix(&err, "maybeErr1")
		if !ok {
			return errors.New("oh no!")
		}
		return nil
	}

	maybeErr2 := func(x, y int) (err error) {
		defer errutil.Prefix(&err, "maybeErr2(%d, %d)", x, y)
		if x+y > 1 {
			return errors.New("uh oh!")
		}
		return nil
	}
	fmt.Println(maybeErr1(true))
	fmt.Println(maybeErr1(false))
	fmt.Println(maybeErr2(1, -1))
	fmt.Println(maybeErr2(1, 1))
}
Output:

<nil>
maybeErr1: oh no!
<nil>
maybeErr2(1, 1): uh oh!

func Recover added in v0.21.5

func Recover(errp *error)

Recover catches any panics and converts them to errors when deferred.

Example
package main

import (
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func main() {
	maybePanic := func(throws bool) (err error) {
		defer errutil.Recover(&err)

		if throws {
			panic("ahhh!")
		}
		return nil
	}

	err := maybePanic(false)
	fmt.Printf("error 1: %v\n", err)

	err = maybePanic(true)
	fmt.Printf("error 2: %v\n", err)

}
Output:

error 1: <nil>
error 2: panic: ahhh!

func Trace added in v0.21.4

func Trace(errp *error)

Trace prefixes an error with caller information if the error is not nil.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func traceErr1(ok bool) (err error) {
	defer errutil.Trace(&err)
	if !ok {
		return errors.New("oh no!")
	}
	return nil
}

func traceErr2(x, y int) (err error) {
	defer errutil.Trace(&err)
	if x+y > 1 {
		return errors.New("uh oh!")
	}
	return nil
}

func main() {
	fmt.Println(traceErr1(true))
	fmt.Println(traceErr1(false))
	fmt.Println(traceErr2(1, -1))
	fmt.Println(traceErr2(1, 1))
}
Output:

<nil>
problem in github.com/carlmjohnson/errutil_test.traceErr1 (prefix_example_test.go:13): oh no!
<nil>
problem in github.com/carlmjohnson/errutil_test.traceErr2 (prefix_example_test.go:21): uh oh!

Types

type Multierr

type Multierr interface {
	error
	Errors() []error
}

Multierr is an interface allowing external types containing multiple errors (such as uber-go/multierr) to be treated as a Slice.

type Slice

type Slice []error

Slice is a slice of errors. Use it to collect possible errors then create a Multierr with the Merge method.

Example
package main

import (
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func main() {
	// A function that sometimes returns an error
	called := 0
	someFunc := func() error {
		called++
		if called%2 == 0 {
			return fmt.Errorf("even error!")
		}
		return nil
	}

	// The empty value can be used
	var errs errutil.Slice

	// Do something that returns an error sometimes
	err := someFunc()
	errs.Push(err)

	// Now merging them to produces <nil> error
	fmt.Println(errs.Merge())

	// But if we add non-nil errors...
	err = someFunc()
	errs.Push(err)
	errs.Push(err)

	// Merge returns non-nil
	fmt.Println(errs.Merge())
}
Output:

<nil>
2 errors: even error!; even error!
Example (ExtendedFormat)
package main

import (
	"fmt"

	"github.com/carlmjohnson/errutil"
)

func main() {
	var errs errutil.Slice

	// Collect several errors
	err := fmt.Errorf("error 1")
	errs.Push(err)

	err = fmt.Errorf("error 2")
	errs.Push(err)

	// ...and a nil error
	err = nil
	errs.Push(err)

	// ...then a real error again
	err = fmt.Errorf("error 3")
	errs.Push(err)

	// Now merge and output them in extended format
	fmt.Printf("%+v", errs.Merge())

}
Output:

3 errors:
	error 1: error 1
	error 2: error 2
	error 3: error 3

func AsSlice added in v0.21.2

func AsSlice(err error) Slice

AsSlice converts err into Slice. If err is nil, the slice has length 0. If the err is a Multierr, it returns the underlying Slice. All other errors become a slice of length 1.

AsSlice also understands how to unwrap hashicorp/go-multierror.

func (*Slice) Merge

func (s *Slice) Merge() error

Merge first removes any nil errors from the Slice. If the resulting length of the Slice is zero, it returns nil. If there is only one error, it returns that error as is. If there are multiple errors, it returns a Multierr containing all the errors.

func (*Slice) Push

func (s *Slice) Push(err error)

Push extends a Slice with an error if the error is non-nil.

If a Multierr is passed to Push, the result is flattened.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL