2015-07-12 06:18:58 +00:00
|
|
|
|
package util
|
|
|
|
|
|
2015-09-06 03:55:20 +00:00
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
)
|
2015-07-12 06:18:58 +00:00
|
|
|
|
|
2015-05-17 19:33:41 +00:00
|
|
|
|
/*
|
|
|
|
|
Err is the implementation of error that all goadb functions return.
|
|
|
|
|
|
|
|
|
|
Best Practice
|
|
|
|
|
|
|
|
|
|
External errors should be wrapped using WrapErrorf, as soon as they are known about.
|
|
|
|
|
|
|
|
|
|
Intermediate code should pass *Errs up until they will be returned outside the library.
|
|
|
|
|
Errors should *not* be wrapped at every return site.
|
|
|
|
|
|
|
|
|
|
Just before returning an *Err outside the library, it can be wrapped again, preserving the
|
|
|
|
|
ErrCode (e.g. with WrapErrf).
|
|
|
|
|
*/
|
2015-07-12 06:18:58 +00:00
|
|
|
|
type Err struct {
|
|
|
|
|
// Code is the high-level "type" of error.
|
|
|
|
|
Code ErrCode
|
|
|
|
|
// Message is a human-readable description of the error.
|
|
|
|
|
Message string
|
|
|
|
|
// Details is optional, and can be used to associate any auxiliary data with an error.
|
|
|
|
|
Details interface{}
|
|
|
|
|
// Cause is optional, and points to the more specific error that caused this one.
|
|
|
|
|
Cause error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ error = &Err{}
|
|
|
|
|
|
|
|
|
|
//go:generate stringer -type=ErrCode
|
|
|
|
|
type ErrCode byte
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
AssertionError ErrCode = iota
|
2015-07-24 03:51:08 +00:00
|
|
|
|
ParseError
|
2015-05-17 19:33:41 +00:00
|
|
|
|
// The server was not available on the requested port.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
ServerNotAvailable
|
2015-07-12 06:18:58 +00:00
|
|
|
|
// General network error communicating with the server.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
NetworkError
|
2015-05-17 19:33:41 +00:00
|
|
|
|
// The connection to the server was reset in the middle of an operation. Server probably died.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
ConnectionResetError
|
2015-07-12 06:18:58 +00:00
|
|
|
|
// The server returned an error message, but we couldn't parse it.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
AdbError
|
2015-07-12 06:18:58 +00:00
|
|
|
|
// The server returned a "device not found" error.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
DeviceNotFound
|
2015-07-12 06:18:58 +00:00
|
|
|
|
// Tried to perform an operation on a path that doesn't exist on the device.
|
2015-07-24 03:51:08 +00:00
|
|
|
|
FileNoExistError
|
2015-07-12 06:18:58 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func Errorf(code ErrCode, format string, args ...interface{}) error {
|
|
|
|
|
return &Err{
|
|
|
|
|
Code: code,
|
|
|
|
|
Message: fmt.Sprintf(format, args...),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
WrapErrf returns an *Err that wraps another *Err and has the same ErrCode.
|
|
|
|
|
Panics if cause is not an *Err.
|
|
|
|
|
|
|
|
|
|
To wrap generic errors, use WrapErrorf.
|
|
|
|
|
*/
|
|
|
|
|
func WrapErrf(cause error, format string, args ...interface{}) error {
|
|
|
|
|
if cause == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := cause.(*Err)
|
|
|
|
|
return &Err{
|
|
|
|
|
Code: err.Code,
|
|
|
|
|
Message: fmt.Sprintf(format, args...),
|
|
|
|
|
Cause: err,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-28 09:08:51 +00:00
|
|
|
|
// CombineErrs returns an error that wraps all the non-nil errors passed to it.
|
|
|
|
|
// If all errors are nil, returns nil.
|
|
|
|
|
// If there's only one non-nil error, returns that error without wrapping.
|
|
|
|
|
// Else, returns an error with the message and code as passed, with the cause set to an error
|
|
|
|
|
// that contains all the non-nil errors and for which Error() returns the concatenation of all their messages.
|
|
|
|
|
func CombineErrs(msg string, code ErrCode, errs ...error) error {
|
|
|
|
|
var nonNilErrs []error
|
|
|
|
|
for _, err := range errs {
|
|
|
|
|
if err != nil {
|
|
|
|
|
nonNilErrs = append(nonNilErrs, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch len(nonNilErrs) {
|
|
|
|
|
case 0:
|
|
|
|
|
return nil
|
|
|
|
|
case 1:
|
|
|
|
|
return nonNilErrs[0]
|
|
|
|
|
default:
|
|
|
|
|
return WrapErrorf(multiError(nonNilErrs), code, "%s", msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type multiError []error
|
|
|
|
|
|
|
|
|
|
func (errs multiError) Error() string {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
fmt.Fprintf(&buf, "%d errors: [", len(errs))
|
|
|
|
|
for i, err := range errs {
|
|
|
|
|
buf.WriteString(err.Error())
|
|
|
|
|
if i < len(errs)-1 {
|
|
|
|
|
buf.WriteString(" ∪ ")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
buf.WriteRune(']')
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 06:18:58 +00:00
|
|
|
|
/*
|
|
|
|
|
WrapErrorf returns an *Err that wraps another arbitrary error with an ErrCode and a message.
|
|
|
|
|
|
|
|
|
|
If cause is nil, returns nil, so you can use it like
|
|
|
|
|
return util.WrapErrorf(DoSomethingDangerous(), util.NetworkError, "well that didn't work")
|
|
|
|
|
|
|
|
|
|
If cause is known to be of type *Err, use WrapErrf.
|
|
|
|
|
*/
|
|
|
|
|
func WrapErrorf(cause error, code ErrCode, format string, args ...interface{}) error {
|
|
|
|
|
if cause == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Err{
|
|
|
|
|
Code: code,
|
|
|
|
|
Message: fmt.Sprintf(format, args...),
|
|
|
|
|
Cause: cause,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AssertionErrorf(format string, args ...interface{}) error {
|
|
|
|
|
return &Err{
|
|
|
|
|
Code: AssertionError,
|
|
|
|
|
Message: fmt.Sprintf(format, args...),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (err *Err) Error() string {
|
|
|
|
|
msg := fmt.Sprintf("%s: %s", err.Code, err.Message)
|
|
|
|
|
if err.Details != nil {
|
|
|
|
|
msg = fmt.Sprintf("%s (%+v)", msg, err.Details)
|
|
|
|
|
}
|
|
|
|
|
return msg
|
|
|
|
|
}
|
2015-07-19 13:43:13 +00:00
|
|
|
|
|
|
|
|
|
// HasErrCode returns true if err is an *Err and err.Code == code.
|
|
|
|
|
func HasErrCode(err error, code ErrCode) bool {
|
|
|
|
|
switch err := err.(type) {
|
|
|
|
|
case *Err:
|
|
|
|
|
return err.Code == code
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-06 03:55:20 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
ErrorWithCauseChain formats err and all its causes if it's an *Err, else returns
|
|
|
|
|
err.Error().
|
|
|
|
|
*/
|
|
|
|
|
func ErrorWithCauseChain(err error) string {
|
|
|
|
|
var buffer bytes.Buffer
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
if wrappedErr, ok := err.(*Err); ok && wrappedErr.Cause != nil {
|
|
|
|
|
fmt.Fprintln(&buffer, wrappedErr.Error())
|
|
|
|
|
fmt.Fprint(&buffer, "caused by ")
|
|
|
|
|
err = wrappedErr.Cause
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-30 09:56:50 +00:00
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
buffer.WriteString(err.Error())
|
|
|
|
|
} else {
|
|
|
|
|
buffer.WriteString("<err=nil>")
|
|
|
|
|
}
|
2015-09-06 03:55:20 +00:00
|
|
|
|
|
|
|
|
|
return buffer.String()
|
|
|
|
|
}
|