diff options
Diffstat (limited to 'vendor/github.com/dropbox')
-rw-r--r-- | vendor/github.com/dropbox/godropbox/errors/errors.go | 260 | ||||
-rw-r--r-- | vendor/github.com/dropbox/godropbox/license.txt | 27 |
2 files changed, 287 insertions, 0 deletions
diff --git a/vendor/github.com/dropbox/godropbox/errors/errors.go b/vendor/github.com/dropbox/godropbox/errors/errors.go new file mode 100644 index 0000000..8090784 --- /dev/null +++ b/vendor/github.com/dropbox/godropbox/errors/errors.go @@ -0,0 +1,260 @@ +// This module implements functions which manipulate errors and provide stack +// trace information. +// +// NOTE: This package intentionally mirrors the standard "errors" module. +// All dropbox code should use this. +package errors + +import ( + "bytes" + "fmt" + "reflect" + "runtime" + "sync" +) + +// This interface exposes additional information about the error. +type DropboxError interface { + // This returns the error message without the stack trace. + GetMessage() string + + // This returns the wrapped error. This returns nil if this does not wrap + // another error. + GetInner() error + + // Implements the built-in error interface. + Error() string + + // Returns stack addresses as a string that can be supplied to + // a helper tool to get the actual stack trace. This function doesn't result + // in resolving full stack frames thus is a lot more efficient. + StackAddrs() string + + // Returns stack frames. + StackFrames() []StackFrame + + // Returns string representation of stack frames. + // Stack frame formatting looks generally something like this: + // dropbox/rpc.(*clientV4).Do + // /srv/server/go/src/dropbox/rpc/client.go:87 +0xbf9 + // dropbox/exclog.Report + // /srv/server/go/src/dropbox/exclog/client.go:129 +0x9e5 + // main.main + // /home/cdo/tmp/report_exception.go:13 +0x84 + // It is discouraged to parse stack frames using string parsing since it can change at any time. + // Use StackFrames() function instead to get actual stack frame metadata. + GetStack() string +} + +// Represents a single stack frame. +type StackFrame struct { + PC uintptr + Func *runtime.Func + FuncName string + File string + LineNumber int +} + +// Standard struct for general types of errors. +// +// For an example of custom error type, look at databaseError/newDatabaseError +// in errors_test.go. +type baseError struct { + msg string + inner error + + stack []uintptr + framesOnce sync.Once + stackFrames []StackFrame +} + +// This returns the error string without stack trace information. +func GetMessage(err interface{}) string { + switch e := err.(type) { + case DropboxError: + return extractFullErrorMessage(e, false) + case runtime.Error: + return runtime.Error(e).Error() + case error: + return e.Error() + default: + return "Passed a non-error to GetMessage" + } +} + +// This returns a string with all available error information, including inner +// errors that are wrapped by this errors. +func (e *baseError) Error() string { + return extractFullErrorMessage(e, true) +} + +// Implements DropboxError interface. +func (e *baseError) GetMessage() string { + return e.msg +} + +// Implements DropboxError interface. +func (e *baseError) GetInner() error { + return e.inner +} + +// Implements DropboxError interface. +func (e *baseError) StackAddrs() string { + buf := bytes.NewBuffer(make([]byte, 0, len(e.stack)*8)) + for _, pc := range e.stack { + fmt.Fprintf(buf, "0x%x ", pc) + } + bufBytes := buf.Bytes() + return string(bufBytes[:len(bufBytes)-1]) +} + +// Implements DropboxError interface. +func (e *baseError) StackFrames() []StackFrame { + e.framesOnce.Do(func() { + e.stackFrames = make([]StackFrame, len(e.stack)) + for i, pc := range e.stack { + frame := &e.stackFrames[i] + frame.PC = pc + frame.Func = runtime.FuncForPC(pc) + if frame.Func != nil { + frame.FuncName = frame.Func.Name() + frame.File, frame.LineNumber = frame.Func.FileLine(frame.PC - 1) + } + } + }) + return e.stackFrames +} + +// Implements DropboxError interface. +func (e *baseError) GetStack() string { + stackFrames := e.StackFrames() + buf := bytes.NewBuffer(make([]byte, 0, 256)) + for _, frame := range stackFrames { + _, _ = buf.WriteString(frame.FuncName) + _, _ = buf.WriteString("\n") + fmt.Fprintf(buf, "\t%s:%d +0x%x\n", + frame.File, frame.LineNumber, frame.PC) + } + return buf.String() +} + +// This returns a new baseError initialized with the given message and +// the current stack trace. +func New(msg string) DropboxError { + return new(nil, msg) +} + +// Same as New, but with fmt.Printf-style parameters. +func Newf(format string, args ...interface{}) DropboxError { + return new(nil, fmt.Sprintf(format, args...)) +} + +// Wraps another error in a new baseError. +func Wrap(err error, msg string) DropboxError { + return new(err, msg) +} + +// Same as Wrap, but with fmt.Printf-style parameters. +func Wrapf(err error, format string, args ...interface{}) DropboxError { + return new(err, fmt.Sprintf(format, args...)) +} + +// Internal helper function to create new baseError objects, +// note that if there is more than one level of redirection to call this function, +// stack frame information will include that level too. +func new(err error, msg string) *baseError { + stack := make([]uintptr, 200) + stackLength := runtime.Callers(3, stack) + return &baseError{ + msg: msg, + stack: stack[:stackLength], + inner: err, + } +} + +// Constructs full error message for a given DropboxError by traversing +// all of its inner errors. If includeStack is True it will also include +// stack trace from deepest DropboxError in the chain. +func extractFullErrorMessage(e DropboxError, includeStack bool) string { + var ok bool + var lastDbxErr DropboxError + errMsg := bytes.NewBuffer(make([]byte, 0, 1024)) + + dbxErr := e + for { + lastDbxErr = dbxErr + errMsg.WriteString(dbxErr.GetMessage()) + + innerErr := dbxErr.GetInner() + if innerErr == nil { + break + } + dbxErr, ok = innerErr.(DropboxError) + if !ok { + // We have reached the end and traveresed all inner errors. + // Add last message and exit loop. + errMsg.WriteString(innerErr.Error()) + break + } + errMsg.WriteString("\n") + } + if includeStack { + errMsg.WriteString("\nORIGINAL STACK TRACE:\n") + errMsg.WriteString(lastDbxErr.GetStack()) + } + return errMsg.String() +} + +// Return a wrapped error or nil if there is none. +func unwrapError(ierr error) (nerr error) { + // Internal errors have a well defined bit of context. + if dbxErr, ok := ierr.(DropboxError); ok { + return dbxErr.GetInner() + } + + // At this point, if anything goes wrong, just return nil. + defer func() { + if x := recover(); x != nil { + nerr = nil + } + }() + + // Go system errors have a convention but paradoxically no + // interface. All of these panic on error. + errV := reflect.ValueOf(ierr).Elem() + errV = errV.FieldByName("Err") + return errV.Interface().(error) +} + +// Keep peeling away layers or context until a primitive error is revealed. +func RootError(ierr error) (nerr error) { + nerr = ierr + for i := 0; i < 20; i++ { + terr := unwrapError(nerr) + if terr == nil { + return nerr + } + nerr = terr + } + return fmt.Errorf("too many iterations: %T", nerr) +} + +// Perform a deep check, unwrapping errors as much as possilbe and +// comparing the string version of the error. +func IsError(err, errConst error) bool { + if err == errConst { + return true + } + // Must rely on string equivalence, otherwise a value is not equal + // to its pointer value. + rootErrStr := "" + rootErr := RootError(err) + if rootErr != nil { + rootErrStr = rootErr.Error() + } + errConstStr := "" + if errConst != nil { + errConstStr = errConst.Error() + } + return rootErrStr == errConstStr +} diff --git a/vendor/github.com/dropbox/godropbox/license.txt b/vendor/github.com/dropbox/godropbox/license.txt new file mode 100644 index 0000000..04d4210 --- /dev/null +++ b/vendor/github.com/dropbox/godropbox/license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2014 Dropbox, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |