diff options
| author | Joe Richey joerichey@google.com <joerichey@google.com> | 2017-05-23 18:32:18 -0700 |
|---|---|---|
| committer | Joe Richey joerichey@google.com <joerichey@google.com> | 2017-05-31 12:33:58 -0700 |
| commit | 4b6d0ce14b8553a93b2d14fd858dfd35bfd61104 (patch) | |
| tree | 39dd221bd5f7118537ae33c43ea23e4d4af65c86 | |
| parent | bd6a1acc8c6b6c03f999558baa4aab464417548d (diff) | |
util: better handing of custom errors
This commit changes how we handle InvalidInput and System errors.
Instead of having formatting functions, the now just wrap a string and
should be created with fmt.Sprintf or similar. We also move all of the
error related code into a single place.
Finally, the utils package gets additional functionality with MinInt64
and GetUsername, and the UnderlyingError function gets better logging.
Note that this will break packages that depend on it. For instance,
metadata and crypto currently do not build. This is fixed in a later
commit.
Change-Id: I819e4d1970604456a5b4b6a7c86426f180a6d092
| -rw-r--r-- | util/errors.go | 107 | ||||
| -rw-r--r-- | util/util.go | 74 | ||||
| -rw-r--r-- | util/util_test.go | 2 |
3 files changed, 102 insertions, 81 deletions
diff --git a/util/errors.go b/util/errors.go index bd63ac8..e5eea4b 100644 --- a/util/errors.go +++ b/util/errors.go @@ -21,25 +21,94 @@ package util import ( "fmt" + "io" "log" "os" ) -// InvalidInputF creates an error that should indicate either bad input from a -// caller of a public library function or bad user input. -func InvalidInputF(format string, a ...interface{}) error { - return fmt.Errorf("invalid input: "+format, a...) +// ErrReader wraps an io.Reader, passing along calls to Read() until a read +// fails. Then, the error is stored, and all subsequent calls to Read() do +// nothing. This allows you to write code which has many subsequent reads and +// do all of the error checking at the end. For example: +// +// r := NewErrReader(reader) +// r.Read(foo) +// r.Read(bar) +// r.Read(baz) +// if r.Err() != nil { +// // Handle error +// } +// +// Taken from https://blog.golang.org/errors-are-values by Rob Pike. +type ErrReader struct { + r io.Reader + err error +} + +// NewErrReader creates an ErrReader which wraps the provided reader. +func NewErrReader(reader io.Reader) *ErrReader { + return &ErrReader{r: reader, err: nil} +} + +// Read runs ReadFull on the wrapped reader if no errors have occurred. +// Otherwise, the previous error is just returned and no reads are attempted. +func (e *ErrReader) Read(p []byte) (n int, err error) { + if e.err == nil { + n, e.err = io.ReadFull(e.r, p) + } + return n, e.err +} + +// Err returns the first encountered err (or nil if no errors occurred). +func (e *ErrReader) Err() error { + return e.err +} + +// ErrWriter works exactly like ErrReader, except with io.Writer. +type ErrWriter struct { + w io.Writer + err error +} + +// NewErrWriter creates an ErrWriter which wraps the provided reader. +func NewErrWriter(writer io.Writer) *ErrWriter { + return &ErrWriter{w: writer, err: nil} +} + +// Write runs the wrapped writer's Write if no errors have occurred. Otherwise, +// the previous error is just returned and no writes are attempted. +func (e *ErrWriter) Write(p []byte) (n int, err error) { + if e.err == nil { + n, e.err = e.w.Write(p) + } + return n, e.err +} + +// Err returns the first encountered err (or nil if no errors occurred). +func (e *ErrWriter) Err() error { + return e.err +} + +// InvalidInput is an error that should indicate either bad input from a caller +// of a public package function. +type InvalidInput string + +func (i InvalidInput) Error() string { + return "invalid input: " + string(i) } // InvalidLengthError indicates name should have had length expected. -func InvalidLengthError(name string, expected int, actual int) error { - return InvalidInputF("expected %s of length %d, actual length was %d", name, expected, actual) +func InvalidLengthError(name string, expected int, actual int) InvalidInput { + message := fmt.Sprintf("length of %s: expected=%d, actual=%d", name, expected, actual) + return InvalidInput(message) } -// SystemErrorF creates an error that should indicate something has gone wrong -// in the underlying system (syscall failure, bad ioctl, etc...). -func SystemErrorF(format string, a ...interface{}) error { - return fmt.Errorf("system error: "+format, a...) +// SystemError is an error that should indicate something has gone wrong in the +// underlying system (syscall failure, bad ioctl, etc...). +type SystemError string + +func (s SystemError) Error() string { + return "system error: " + string(s) } // NeverError panics if a non-nil error is passed in. It should be used to check @@ -50,16 +119,20 @@ func NeverError(err error) { } } -// UnderlyingError returns the underlying error for known os error types. -// From: src/os/error.go +// UnderlyingError returns the underlying error for known os error types and +// logs the full error. From: src/os/error.go func UnderlyingError(err error) error { - switch err := err.(type) { + var newErr error + switch typedErr := err.(type) { case *os.PathError: - return err.Err + newErr = typedErr.Err case *os.LinkError: - return err.Err + newErr = typedErr.Err case *os.SyscallError: - return err.Err + newErr = typedErr.Err + default: + return err } - return err + log.Print(err) + return newErr } diff --git a/util/util.go b/util/util.go index dc1b85d..2f20151 100644 --- a/util/util.go +++ b/util/util.go @@ -24,74 +24,14 @@ package util import ( - "io" "unsafe" ) -// ErrReader wraps an io.Reader, passing along calls to Read() until a read -// fails. Then, the error is stored, and all subsequent calls to Read() do -// nothing. This allows you to write code which has many subsequent reads and -// do all of the error checking at the end. For example: -// -// r := NewErrReader(reader) -// r.Read(foo) -// io.ReadFull(r, bar) -// if r.Err() != nil { -// // Handle error -// } -// -// Taken from https://blog.golang.org/errors-are-values by Rob Pike. -type ErrReader struct { - r io.Reader - err error -} - -// NewErrReader creates an ErrReader which wraps the provided reader. -func NewErrReader(reader io.Reader) *ErrReader { - return &ErrReader{r: reader, err: nil} -} - -// Read runs ReadFull on the wrapped reader if no errors have occurred. -// Otherwise, the previous error is just returned and no reads are attempted. -func (e *ErrReader) Read(p []byte) (n int, err error) { - if e.err == nil { - n, e.err = io.ReadFull(e.r, p) - } - return n, e.err -} - -// Err returns the first encountered err (or nil if no errors occurred). -func (e *ErrReader) Err() error { - return e.err -} - -// ErrWriter works exactly like ErrReader, except with io.Writer. -type ErrWriter struct { - w io.Writer - err error -} - -// NewErrWriter creates an ErrWriter which wraps the provided reader. -func NewErrWriter(writer io.Writer) *ErrWriter { - return &ErrWriter{w: writer, err: nil} -} - -// Write runs the wrapped writer's Write if no errors have occurred. Otherwise, -// the previous error is just returned and no writes are attempted. -func (e *ErrWriter) Write(p []byte) (n int, err error) { - if e.err == nil { - n, e.err = e.w.Write(p) - } - return n, e.err -} - -// Err returns the first encountered err (or nil if no errors occurred). -func (e *ErrWriter) Err() error { - return e.err -} - // Ptr converts an Go byte array to a pointer to the start of the array. func Ptr(slice []byte) unsafe.Pointer { + if len(slice) == 0 { + return nil + } return unsafe.Pointer(&slice[0]) } @@ -124,3 +64,11 @@ func MinInt(a, b int) int { } return b } + +// MinInt64 returns the lesser of a and b. +func MinInt64(a, b int64) int64 { + if a < b { + return a + } + return b +} diff --git a/util/util_test.go b/util/util_test.go index 65541b3..33ce2ff 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -44,7 +44,7 @@ func TestNeverErrorPanic(t *testing.T) { } }() - err := SystemErrorF("Hello") + err := SystemError("Hello") NeverError(err) } |