replace zxq.co/ripple/hanayo
This commit is contained in:
50
vendor/github.com/ansel1/merry/.gitignore
generated
vendored
Normal file
50
vendor/github.com/ansel1/merry/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
vendor/
|
9
vendor/github.com/ansel1/merry/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/ansel1/merry/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
- tip
|
27
vendor/github.com/ansel1/merry/Gopkg.lock
generated
vendored
Normal file
27
vendor/github.com/ansel1/merry/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b750880cdc8ce044e6f9bf3b331d8a392471c328107b8c3d42e3e11022d76858"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
26
vendor/github.com/ansel1/merry/Gopkg.toml
generated
vendored
Normal file
26
vendor/github.com/ansel1/merry/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.1.4"
|
21
vendor/github.com/ansel1/merry/LICENSE.MIT
generated
vendored
Normal file
21
vendor/github.com/ansel1/merry/LICENSE.MIT
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Russ Egan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
193
vendor/github.com/ansel1/merry/README.md
generated
vendored
Normal file
193
vendor/github.com/ansel1/merry/README.md
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
merry [](https://travis-ci.org/ansel1/merry) [](https://godoc.org/github.com/ansel1/merry) [](https://goreportcard.com/report/github.com/ansel1/merry)
|
||||
=====
|
||||
|
||||
Make your golang errors merry, with stacktraces, inheritance, and arbitrary additional context.
|
||||
|
||||
The package is largely based on http://github.com/go-errors/errors, with additional
|
||||
inspiration from https://github.com/go-errgo/errgo and https://github.com/amattn/deeperror.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
go get github.com/ansel1/merry
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Merry errors work a lot like google's golang.org/x/net/context package.
|
||||
Merry errors wrap normal errors with a context of key/value pairs.
|
||||
Like contexts, merry errors are immutable: adding a key/value to an error
|
||||
always creates a new error which wraps the original.
|
||||
|
||||
`merry` comes with built-in support for adding information to errors:
|
||||
|
||||
* stacktraces
|
||||
* overriding the error message
|
||||
* HTTP status codes
|
||||
* End user error messages
|
||||
|
||||
You can also add your own additional information.
|
||||
|
||||
The stack capturing feature can be turned off for better performance, though it's pretty fast. Benchmarks
|
||||
on an early 2011 MacBook Pro, with go 1.7rc1:
|
||||
|
||||
BenchmarkNew_withStackCapture-8 1000000 1413 ns/op
|
||||
BenchmarkNew_withoutStackCapture-8 10000000 218 ns/op
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* New errors have a stacktrace captured where they are created
|
||||
* Add a stacktrace to existing errors (captured where they are wrapped)
|
||||
|
||||
```go
|
||||
err := lib.Read()
|
||||
return merry.Wrap(err) // no-op if err is already merry
|
||||
```
|
||||
|
||||
* Allow golang idiom of comparing an err value to an exported value, using `Is()`
|
||||
|
||||
```go
|
||||
var ParseError = merry.New("Parse error")
|
||||
|
||||
func Parse() error {
|
||||
err := ParseError.Here() // captures a stacktrace here
|
||||
merry.Is(err, ParseError) // instead of err == ParseError
|
||||
}
|
||||
```
|
||||
|
||||
* Change the message on an error, while still using `Is()` to compare to the original error
|
||||
|
||||
```go
|
||||
err := merry.WithMessage(ParseError, "Bad input")
|
||||
merry.Is(err, ParseError) // yes it is
|
||||
```
|
||||
|
||||
* `Is()` supports hierarchies of errors
|
||||
|
||||
```go
|
||||
var ParseError = merry.New("Parse error")
|
||||
var InvalidCharSet = merry.WithMessage(ParseError, "Invalid char set")
|
||||
var InvalidSyntax = merry.WithMessage(ParseError, "Invalid syntax")
|
||||
|
||||
func Parse(s string) error {
|
||||
// use chainable methods to add context
|
||||
return InvalidCharSet.Here().WithMessagef("Invalid char set: %s", "UTF-8")
|
||||
// or functions
|
||||
// return merry.WithMessagef(merry.Here(InvalidCharSet), "Invalid char set: %s", "UTF-8")
|
||||
}
|
||||
|
||||
func Check() {
|
||||
err := Parse("fields")
|
||||
merry.Is(err, ParseError) // yup
|
||||
merry.Is(err, InvalidCharSet) // yup
|
||||
merry.Is(err, InvalidSyntax) // nope
|
||||
}
|
||||
```
|
||||
|
||||
* Add an HTTP status code
|
||||
|
||||
```go
|
||||
merry.HTTPCode(errors.New("regular error")) // 500
|
||||
merry.HTTPCode(merry.New("merry error").WithHTTPCode(404)) // 404
|
||||
```
|
||||
|
||||
* Set an alternate error message for end users
|
||||
|
||||
```go
|
||||
e := merry.New("crash").WithUserMessage("nothing to see here")
|
||||
merry.UserMessage(e) // returns "nothing to see here"
|
||||
```
|
||||
|
||||
* Functions for printing error details
|
||||
|
||||
```go
|
||||
err := merry.New("boom")
|
||||
m := merry.Stacktrace(err) // just the stacktrace
|
||||
m = merry.Details(err) // error message and stacktrace
|
||||
fmt.Sprintf("%+v", err) == merry.Details(err) // errors implement fmt.Formatter
|
||||
```
|
||||
|
||||
* Add your own context info
|
||||
|
||||
```go
|
||||
err := merry.New("boom").WithValue("explosive", "black powder")
|
||||
```
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
The package contains functions for creating new errors with stacks, or adding a stack to `error`
|
||||
instances. Functions with add context (e.g. `WithValue()`) work on any `error`, and will
|
||||
automatically convert them to merry errors (with a stack) if necessary.
|
||||
|
||||
Capturing the stack can be globally disabled with `SetStackCaptureEnabled(false)`
|
||||
|
||||
Functions which get context values from errors also accept `error`, and will return default
|
||||
values if the error is not merry, or doesn't have that key attached.
|
||||
|
||||
All the functions which create or attach context return concrete instances of `*Error`. `*Error`
|
||||
implements methods to add context to the error (they mirror the functions and do
|
||||
the same thing). They allow for a chainable syntax for adding context.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ansel1/merry"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var InvalidInputs = errors.New("Input is invalid")
|
||||
|
||||
func main() {
|
||||
// create a new error, with a stacktrace attached
|
||||
err := merry.New("bad stuff happened")
|
||||
|
||||
// create a new error with format string, like fmt.Errorf
|
||||
err = merry.Errorf("bad input: %v", os.Args)
|
||||
|
||||
// capture a fresh stacktrace from this callsite
|
||||
err = merry.Here(InvalidInputs)
|
||||
|
||||
// Make err merry if it wasn't already. The stacktrace will be captured here if the
|
||||
// error didn't already have one. Also useful to cast to *Error
|
||||
err = merry.Wrap(err, 0)
|
||||
|
||||
// override the original error's message
|
||||
err.WithMessagef("Input is invalid: %v", os.Args)
|
||||
|
||||
// Use Is to compare errors against values, which is a common golang idiom
|
||||
merry.Is(err, InvalidInputs) // will be true
|
||||
|
||||
// associated an http code
|
||||
err.WithHTTPCode(400)
|
||||
|
||||
perr := parser.Parse("blah")
|
||||
err = Wrap(perr, 0)
|
||||
// Get the original error back
|
||||
merry.Unwrap(err) == perr // will be true
|
||||
|
||||
// Print the error to a string, with the stacktrace, if it has one
|
||||
s := merry.Details(err)
|
||||
|
||||
// Just print the stacktrace (empty string if err is not a RichError)
|
||||
s := merry.Stacktrace(err)
|
||||
|
||||
// Get the location of the error (the first line in the stacktrace)
|
||||
file, line := merry.Location(err)
|
||||
|
||||
// Get an HTTP status code for an error. Defaults to 500 for non-nil errors, and 200 if err is nil.
|
||||
code := merry.HTTPCode(err)
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
See inline docs for more details.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This package is licensed under the MIT license, see LICENSE.MIT for details.
|
556
vendor/github.com/ansel1/merry/errors.go
generated
vendored
Normal file
556
vendor/github.com/ansel1/merry/errors.go
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
package merry
|
||||
|
||||
// The merry package augments standard golang errors with stacktraces
|
||||
// and other context information.
|
||||
//
|
||||
// You can add any context information to an error with `e = merry.WithValue(e, "code", 12345)`
|
||||
// You can retrieve that value with `v, _ := merry.Value(e, "code").(int)`
|
||||
//
|
||||
// Any error augmented like this will automatically get a stacktrace attached, if it doesn't have one
|
||||
// already. If you just want to add the stacktrace, use `Wrap(e)`
|
||||
//
|
||||
// It also providers a way to override an error's message:
|
||||
//
|
||||
// var InvalidInputs = errors.New("Bad inputs")
|
||||
//
|
||||
// `Here()` captures a new stacktrace, and WithMessagef() sets a new error message:
|
||||
//
|
||||
// return merry.Here(InvalidInputs).WithMessagef("Bad inputs: %v", inputs)
|
||||
//
|
||||
// Errors are immutable. All functions and methods which add context return new errors.
|
||||
// But errors can still be compared to the originals with `Is()`
|
||||
//
|
||||
// if merry.Is(err, InvalidInputs) {
|
||||
//
|
||||
// Functions which add context to errors have equivalent methods on *Error, to allow
|
||||
// convenient chaining:
|
||||
//
|
||||
// return merry.New("Invalid body").WithHTTPCode(400)
|
||||
//
|
||||
// merry.Errors also implement fmt.Formatter, similar to github.com/pkg/errors.
|
||||
//
|
||||
// fmt.Sprintf("%+v", e) == merry.Details(e)
|
||||
//
|
||||
// pkg/errors Cause() interface is not implemented (yet).
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// MaxStackDepth is the maximum number of stackframes on any error.
|
||||
var MaxStackDepth = 50
|
||||
|
||||
var captureStacks = true
|
||||
var verbose = false
|
||||
|
||||
// StackCaptureEnabled returns whether stack capturing is enabled
|
||||
func StackCaptureEnabled() bool {
|
||||
return captureStacks
|
||||
}
|
||||
|
||||
// SetStackCaptureEnabled sets stack capturing globally. Disabling stack capture can increase performance
|
||||
func SetStackCaptureEnabled(enabled bool) {
|
||||
captureStacks = enabled
|
||||
}
|
||||
|
||||
// VerboseDefault returns the global default for verbose mode.
|
||||
// When true, e.Error() == Details(e)
|
||||
// When false, e.Error() == Message(e)
|
||||
func VerboseDefault() bool {
|
||||
return verbose
|
||||
}
|
||||
|
||||
// SetVerboseDefault sets the global default for verbose mode.
|
||||
// When true, e.Error() == Details(e)
|
||||
// When false, e.Error() == Message(e)
|
||||
func SetVerboseDefault(b bool) {
|
||||
verbose = b
|
||||
}
|
||||
|
||||
// Error extends the standard golang `error` interface with functions
|
||||
// for attachment additional data to the error
|
||||
type Error interface {
|
||||
error
|
||||
Appendf(format string, args ...interface{}) Error
|
||||
Append(msg string) Error
|
||||
Prepend(msg string) Error
|
||||
Prependf(format string, args ...interface{}) Error
|
||||
WithMessage(msg string) Error
|
||||
WithMessagef(format string, args ...interface{}) Error
|
||||
WithUserMessage(msg string) Error
|
||||
WithUserMessagef(format string, args ...interface{}) Error
|
||||
WithValue(key, value interface{}) Error
|
||||
Here() Error
|
||||
WithStackSkipping(skip int) Error
|
||||
WithHTTPCode(code int) Error
|
||||
fmt.Formatter
|
||||
}
|
||||
|
||||
// New creates a new error, with a stack attached. The equivalent of golang's errors.New()
|
||||
func New(msg string) Error {
|
||||
return WrapSkipping(errors.New(msg), 1)
|
||||
}
|
||||
|
||||
// Errorf creates a new error with a formatted message and a stack. The equivalent of golang's fmt.Errorf()
|
||||
func Errorf(format string, a ...interface{}) Error {
|
||||
return WrapSkipping(fmt.Errorf(format, a...), 1)
|
||||
}
|
||||
|
||||
// UserError creates a new error with a message intended for display to an
|
||||
// end user.
|
||||
func UserError(msg string) Error {
|
||||
return WrapSkipping(errors.New(""), 1).WithUserMessage(msg)
|
||||
}
|
||||
|
||||
// UserErrorf is like UserError, but uses fmt.Sprintf()
|
||||
func UserErrorf(format string, a ...interface{}) Error {
|
||||
return WrapSkipping(errors.New(""), 1).WithUserMessagef(format, a...)
|
||||
}
|
||||
|
||||
// Wrap turns the argument into a merry.Error. If the argument already is a
|
||||
// merry.Error, this is a no-op.
|
||||
// If e == nil, return nil
|
||||
func Wrap(e error) Error {
|
||||
return WrapSkipping(e, 1)
|
||||
}
|
||||
|
||||
// WrapSkipping turns the error arg into a merry.Error if the arg is not
|
||||
// already a merry.Error.
|
||||
// If e is nil, return nil.
|
||||
// If a merry.Error is created by this call, the stack captured will skip
|
||||
// `skip` frames (0 is the call site of `WrapSkipping()`)
|
||||
func WrapSkipping(e error, skip int) Error {
|
||||
switch e1 := e.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *merryErr:
|
||||
return e1
|
||||
default:
|
||||
return &merryErr{
|
||||
err: e,
|
||||
key: stack,
|
||||
value: captureStack(skip + 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithValue adds a context an error. If the key was already set on e,
|
||||
// the new value will take precedence.
|
||||
// If e is nil, returns nil.
|
||||
func WithValue(e error, key, value interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithValue(key, value)
|
||||
}
|
||||
|
||||
// Value returns the value for key, or nil if not set.
|
||||
// If e is nil, returns nil.
|
||||
func Value(e error, key interface{}) interface{} {
|
||||
for {
|
||||
switch m := e.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *merryErr:
|
||||
if m.key == key {
|
||||
return m.value
|
||||
}
|
||||
e = m.err
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Values returns a map of all values attached to the error
|
||||
// If a key has been attached multiple times, the map will
|
||||
// contain the last value mapped
|
||||
// If e is nil, returns nil.
|
||||
func Values(e error) map[interface{}]interface{} {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
var values map[interface{}]interface{}
|
||||
for {
|
||||
w, ok := e.(*merryErr)
|
||||
if !ok {
|
||||
return values
|
||||
}
|
||||
if values == nil {
|
||||
values = make(map[interface{}]interface{}, 1)
|
||||
}
|
||||
if _, ok := values[w.key]; !ok {
|
||||
values[w.key] = w.value
|
||||
}
|
||||
e = w.err
|
||||
}
|
||||
}
|
||||
|
||||
// Here returns an error with a new stacktrace, at the call site of Here().
|
||||
// Useful when returning copies of exported package errors.
|
||||
// If e is nil, returns nil.
|
||||
func Here(e error) Error {
|
||||
switch m := e.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *merryErr:
|
||||
// optimization: only capture the stack once, since its expensive
|
||||
return m.WithStackSkipping(1)
|
||||
default:
|
||||
return WrapSkipping(e, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Stack returns the stack attached to an error, or nil if one is not attached
|
||||
// If e is nil, returns nil.
|
||||
func Stack(e error) []uintptr {
|
||||
stack, _ := Value(e, stack).([]uintptr)
|
||||
return stack
|
||||
}
|
||||
|
||||
// WithHTTPCode returns an error with an http code attached.
|
||||
// If e is nil, returns nil.
|
||||
func WithHTTPCode(e error, code int) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithHTTPCode(code)
|
||||
}
|
||||
|
||||
// HTTPCode converts an error to an http status code. All errors
|
||||
// map to 500, unless the error has an http code attached.
|
||||
// If e is nil, returns 200.
|
||||
func HTTPCode(e error) int {
|
||||
if e == nil {
|
||||
return 200
|
||||
}
|
||||
code, _ := Value(e, httpCode).(int)
|
||||
if code == 0 {
|
||||
return 500
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// UserMessage returns the end-user safe message. Returns empty if not set.
|
||||
// If e is nil, returns "".
|
||||
func UserMessage(e error) string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
msg, _ := Value(e, userMessage).(string)
|
||||
return msg
|
||||
}
|
||||
|
||||
// Message returns just the error message. It is equivalent to
|
||||
// Error() when Verbose is false.
|
||||
// The behavior of Error() is (pseudo-code):
|
||||
//
|
||||
// if verbose
|
||||
// Details(e)
|
||||
// else
|
||||
// Message(e) || UserMessage(e)
|
||||
//
|
||||
// If e is nil, returns "".
|
||||
func Message(e error) string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
m, _ := Value(e, message).(string)
|
||||
if m == "" {
|
||||
return Unwrap(e).Error()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// WithMessage returns an error with a new message.
|
||||
// The resulting error's Error() method will return
|
||||
// the new message.
|
||||
// If e is nil, returns nil.
|
||||
func WithMessage(e error, msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithValue(message, msg)
|
||||
}
|
||||
|
||||
// WithMessagef is the same as WithMessage(), using fmt.Sprintf().
|
||||
func WithMessagef(e error, format string, a ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithMessagef(format, a...)
|
||||
}
|
||||
|
||||
// WithUserMessage adds a message which is suitable for end users to see.
|
||||
// If e is nil, returns nil.
|
||||
func WithUserMessage(e error, msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithUserMessage(msg)
|
||||
}
|
||||
|
||||
// WithUserMessagef is the same as WithMessage(), using fmt.Sprintf()
|
||||
func WithUserMessagef(e error, format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).WithUserMessagef(format, args...)
|
||||
}
|
||||
|
||||
// Append a message after the current error message, in the format "original: new".
|
||||
// If e == nil, return nil.
|
||||
func Append(e error, msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).Append(msg)
|
||||
}
|
||||
|
||||
// Appendf is the same as Append, but uses fmt.Sprintf().
|
||||
func Appendf(e error, format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).Appendf(format, args...)
|
||||
}
|
||||
|
||||
// Prepend a message before the current error message, in the format "new: original".
|
||||
// If e == nil, return nil.
|
||||
func Prepend(e error, msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).Prepend(msg)
|
||||
}
|
||||
|
||||
// Prependf is the same as Prepend, but uses fmt.Sprintf()
|
||||
func Prependf(e error, format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return WrapSkipping(e, 1).Prependf(format, args...)
|
||||
}
|
||||
|
||||
// Is checks whether e is equal to or wraps the original, at any depth.
|
||||
// If e == nil, return false.
|
||||
// This is useful if your package uses the common golang pattern of
|
||||
// exported error constants. If your package exports an ErrEOF constant,
|
||||
// which is initialized like this:
|
||||
//
|
||||
// var ErrEOF = errors.New("End of file error")
|
||||
//
|
||||
// ...and your user wants to compare an error returned by your package
|
||||
// with ErrEOF:
|
||||
//
|
||||
// err := urpack.Read()
|
||||
// if err == urpack.ErrEOF {
|
||||
//
|
||||
// ...the comparison will fail if the error has been wrapped by merry
|
||||
// at some point. Replace the comparison with:
|
||||
//
|
||||
// if merry.Is(err, urpack.ErrEOF) {
|
||||
//
|
||||
func Is(e error, originals ...error) bool {
|
||||
is := func(e, original error) bool {
|
||||
for {
|
||||
if e == original {
|
||||
return true
|
||||
}
|
||||
if e == nil || original == nil {
|
||||
return false
|
||||
}
|
||||
w, ok := e.(*merryErr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
e = w.err
|
||||
}
|
||||
}
|
||||
for _, o := range originals {
|
||||
if is(e, o) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Unwrap returns the innermost underlying error.
|
||||
// Only useful in advanced cases, like if you need to
|
||||
// cast the underlying error to some type to get
|
||||
// additional information from it.
|
||||
// If e == nil, return nil.
|
||||
func Unwrap(e error) error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
w, ok := e.(*merryErr)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
e = w.err
|
||||
}
|
||||
}
|
||||
|
||||
func captureStack(skip int) []uintptr {
|
||||
if !captureStacks {
|
||||
return nil
|
||||
}
|
||||
stack := make([]uintptr, MaxStackDepth)
|
||||
length := runtime.Callers(2+skip, stack[:])
|
||||
return stack[:length]
|
||||
}
|
||||
|
||||
type errorProperty string
|
||||
|
||||
const (
|
||||
stack errorProperty = "stack"
|
||||
message = "message"
|
||||
httpCode = "http status code"
|
||||
userMessage = "user message"
|
||||
)
|
||||
|
||||
type merryErr struct {
|
||||
err error
|
||||
key, value interface{}
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter
|
||||
func (e *merryErr) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, Details(e))
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, e.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// make sure merryErr implements Error
|
||||
var _ Error = (*merryErr)(nil)
|
||||
|
||||
// Error implements golang's error interface
|
||||
// returns the message value if set, otherwise
|
||||
// delegates to inner error
|
||||
func (e *merryErr) Error() string {
|
||||
if verbose {
|
||||
return Details(e)
|
||||
}
|
||||
m := Message(e)
|
||||
if m == "" {
|
||||
return UserMessage(e)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// return a new error with additional context
|
||||
func (e *merryErr) WithValue(key, value interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &merryErr{
|
||||
err: e,
|
||||
key: key,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// Shorthand for capturing a new stack trace
|
||||
func (e *merryErr) Here() Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithStackSkipping(1)
|
||||
}
|
||||
|
||||
// return a new error with a new stack capture
|
||||
func (e *merryErr) WithStackSkipping(skip int) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &merryErr{
|
||||
err: e,
|
||||
key: stack,
|
||||
value: captureStack(skip + 1),
|
||||
}
|
||||
}
|
||||
|
||||
// return a new error with an http status code attached
|
||||
func (e *merryErr) WithHTTPCode(code int) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithValue(httpCode, code)
|
||||
}
|
||||
|
||||
// return a new error with a new message
|
||||
func (e *merryErr) WithMessage(msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithValue(message, msg)
|
||||
}
|
||||
|
||||
// return a new error with a new formatted message
|
||||
func (e *merryErr) WithMessagef(format string, a ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithMessage(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Add a message which is suitable for end users to see
|
||||
func (e *merryErr) WithUserMessage(msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithValue(userMessage, msg)
|
||||
}
|
||||
|
||||
// Add a message which is suitable for end users to see
|
||||
func (e *merryErr) WithUserMessagef(format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithUserMessage(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Append a message after the current error message, in the format "original: new"
|
||||
func (e *merryErr) Append(msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithMessagef("%s: %s", e.Error(), msg)
|
||||
}
|
||||
|
||||
// Append a message after the current error message, in the format "original: new"
|
||||
func (e *merryErr) Appendf(format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Append(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Prepend a message before the current error message, in the format "new: original"
|
||||
func (e *merryErr) Prepend(msg string) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.WithMessagef("%s: %s", msg, e.Error())
|
||||
}
|
||||
|
||||
// Prepend a message before the current error message, in the format "new: original"
|
||||
func (e *merryErr) Prependf(format string, args ...interface{}) Error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Prepend(fmt.Sprintf(format, args...))
|
||||
}
|
556
vendor/github.com/ansel1/merry/errors_test.go
generated
vendored
Normal file
556
vendor/github.com/ansel1/merry/errors_test.go
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
package merry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
err := New("bang")
|
||||
if HTTPCode(err) != 500 {
|
||||
t.Errorf("http code should have been 500, was %v", HTTPCode(err))
|
||||
}
|
||||
if err.Error() != "bang" {
|
||||
t.Errorf("error message should have been bang, was %v", err.Error())
|
||||
}
|
||||
f, l := Location(err)
|
||||
if !strings.Contains(f, "errors_test.go") {
|
||||
t.Errorf("error message should have contained errors_test.go, was %s", f)
|
||||
}
|
||||
if l != rl+1 {
|
||||
t.Errorf("error line should have been %d, was %d", rl+1, 8)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorf(t *testing.T) {
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
err := Errorf("chitty chitty %v %v", "bang", "bang")
|
||||
if HTTPCode(err) != 500 {
|
||||
t.Errorf("http code should have been 500, was %v", HTTPCode(err))
|
||||
}
|
||||
if err.Error() != "chitty chitty bang bang" {
|
||||
t.Errorf("error message should have been chitty chitty bang bang, was %v", err.Error())
|
||||
}
|
||||
f, l := Location(err)
|
||||
if !strings.Contains(f, "errors_test.go") {
|
||||
t.Errorf("error message should have contained errors_test.go, was %s", f)
|
||||
}
|
||||
if l != rl+1 {
|
||||
t.Errorf("error line should have been %d, was %d", rl+1, 8)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserError(t *testing.T) {
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
err := UserError("bang")
|
||||
assert.Equal(t, "bang", UserMessage(err))
|
||||
assert.Empty(t, Message(err))
|
||||
_, l := Location(err)
|
||||
assert.Equal(t, rl+1, l)
|
||||
}
|
||||
|
||||
func TestUserErrorf(t *testing.T) {
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
err := UserErrorf("bang %v", "bang")
|
||||
assert.Equal(t, "bang bang", UserMessage(err))
|
||||
assert.Empty(t, Message(err))
|
||||
_, l := Location(err)
|
||||
assert.Equal(t, rl+1, l)
|
||||
}
|
||||
|
||||
func TestDetails(t *testing.T) {
|
||||
var err error = New("bang")
|
||||
deets := Details(err)
|
||||
t.Log(deets)
|
||||
lines := strings.Split(deets, "\n")
|
||||
if lines[0] != "bang" {
|
||||
t.Errorf("first line should have been bang: %v", lines[0])
|
||||
}
|
||||
if !strings.Contains(deets, Stacktrace(err)) {
|
||||
t.Error("should have contained the error stacktrace")
|
||||
}
|
||||
|
||||
err = WithUserMessage(err, "stay calm")
|
||||
deets = Details(err)
|
||||
t.Log(deets)
|
||||
assert.Contains(t, deets, "User Message: stay calm")
|
||||
|
||||
// Allow nil error
|
||||
assert.Empty(t, Details(nil))
|
||||
}
|
||||
|
||||
func TestStacktrace(t *testing.T) {
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
var err error = New("bang")
|
||||
|
||||
assert.NotEmpty(t, Stack(err))
|
||||
s := Stacktrace(err)
|
||||
t.Log(s)
|
||||
lines := strings.Split(s, "\n")
|
||||
assert.NotEmpty(t, lines)
|
||||
assert.Equal(t, "github.com/ansel1/merry.TestStacktrace", lines[0])
|
||||
assert.Contains(t, lines[1], fmt.Sprintf("errors_test.go:%d", rl+1))
|
||||
// Allow nil error
|
||||
assert.Empty(t, Stacktrace(nil))
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
err := errors.New("simple")
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
wrapped := WrapSkipping(err, 0)
|
||||
f, l := Location(wrapped)
|
||||
if !strings.Contains(f, "errors_test.go") {
|
||||
t.Errorf("error message should have contained errors_test.go, was %s", f)
|
||||
}
|
||||
if l != rl+1 {
|
||||
t.Errorf("error line should have been %d, was %d", rl+1, l)
|
||||
}
|
||||
|
||||
rich2 := WrapSkipping(wrapped, 0)
|
||||
if wrapped != rich2 {
|
||||
t.Error("rich and rich2 are not the same. Wrap should have been no-op if rich was already a RichError")
|
||||
}
|
||||
if !reflect.DeepEqual(Stack(wrapped), Stack(rich2)) {
|
||||
t.Log(Details(rich2))
|
||||
t.Error("wrap should have left the stacktrace alone if the original error already had a stack")
|
||||
}
|
||||
// wrapping nil -> nil
|
||||
assert.Nil(t, Wrap(nil))
|
||||
assert.Nil(t, WrapSkipping(nil, 1))
|
||||
}
|
||||
|
||||
func TestHere(t *testing.T) {
|
||||
ParseError := New("Parse error")
|
||||
InvalidCharSet := WithMessage(ParseError, "Invalid charset").WithHTTPCode(400)
|
||||
InvalidSyntax := ParseError.WithMessage("Syntax error")
|
||||
|
||||
if !Is(InvalidCharSet, ParseError) {
|
||||
t.Error("InvalidCharSet should be a ParseError")
|
||||
}
|
||||
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
pe := Here(ParseError)
|
||||
_, l := Location(pe)
|
||||
if l != rl+1 {
|
||||
t.Errorf("Extend should capture a new stack. Expected %d, got %d", rl+1, l)
|
||||
}
|
||||
|
||||
if !Is(pe, ParseError) {
|
||||
t.Error("pe should be a ParseError")
|
||||
}
|
||||
if Is(pe, InvalidCharSet) {
|
||||
t.Error("pe should not be an InvalidCharSet")
|
||||
}
|
||||
if pe.Error() != "Parse error" {
|
||||
t.Errorf("child error's message is wrong, expected: Parse error, got %v", pe.Error())
|
||||
}
|
||||
icse := Here(InvalidCharSet)
|
||||
if !Is(icse, ParseError) {
|
||||
t.Error("icse should be a ParseError")
|
||||
}
|
||||
if !Is(icse, InvalidCharSet) {
|
||||
t.Error("icse should be an InvalidCharSet")
|
||||
}
|
||||
if Is(icse, InvalidSyntax) {
|
||||
t.Error("icse should not be an InvalidSyntax")
|
||||
}
|
||||
if icse.Error() != "Invalid charset" {
|
||||
t.Errorf("child's message is wrong. Expected: Invalid charset, got: %v", icse.Error())
|
||||
}
|
||||
if HTTPCode(icse) != 400 {
|
||||
t.Errorf("child's http code is wrong. Expected 400, got %v", HTTPCode(icse))
|
||||
}
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, Here(nil))
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
inner := errors.New("bing")
|
||||
wrapper := WrapSkipping(inner, 0)
|
||||
if Unwrap(wrapper) != inner {
|
||||
t.Errorf("unwrapped error should have been the inner err, was %#v", inner)
|
||||
}
|
||||
|
||||
doubleWrap := wrapper.WithMessage("blag")
|
||||
if Unwrap(doubleWrap) != inner {
|
||||
t.Errorf("unwrapped should recurse to inner, but got %#v", inner)
|
||||
}
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, Unwrap(nil))
|
||||
}
|
||||
|
||||
func TestNilValues(t *testing.T) {
|
||||
// Quirk of go
|
||||
// http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#nil_in_nil_in_vals
|
||||
// an interface value isn't nil unless both the type *and* the value are nil
|
||||
// make sure we aren't accidentally returning nil values but non-nil types
|
||||
type e struct{}
|
||||
var anE *e
|
||||
type f interface{}
|
||||
var anF f
|
||||
if anF != nil {
|
||||
t.Error("anF should have been nil here, because it doesn't have a concete type yet")
|
||||
}
|
||||
anF = anE
|
||||
if anF == nil {
|
||||
t.Error("anF should have been not nil here, because it now has a concrete type")
|
||||
}
|
||||
if WithMessage(WithHTTPCode(Wrap(nil), 400), "hey") != nil {
|
||||
t.Error("by using interfaces in all the returns, this should have remained a true nil value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIs(t *testing.T) {
|
||||
ParseError := errors.New("blag")
|
||||
cp := Here(ParseError)
|
||||
if !Is(cp, ParseError) {
|
||||
t.Error("Is(child, parent) should be true")
|
||||
}
|
||||
if Is(ParseError, cp) {
|
||||
t.Error("Is(parent, child) should not be true")
|
||||
}
|
||||
if !Is(ParseError, ParseError) {
|
||||
t.Error("errors are always themselves")
|
||||
}
|
||||
if !Is(cp, cp) {
|
||||
t.Error("should work when comparing rich error to itself")
|
||||
}
|
||||
if Is(Here(ParseError), cp) {
|
||||
t.Error("Is(sibling, sibling) should not be true")
|
||||
}
|
||||
err2 := errors.New("blag")
|
||||
if Is(ParseError, err2) {
|
||||
t.Error("These should not have been equal")
|
||||
}
|
||||
if Is(Here(err2), cp) {
|
||||
t.Error("these were not copies of the same error")
|
||||
}
|
||||
if Is(Here(err2), ParseError) {
|
||||
t.Error("underlying errors were not equal")
|
||||
}
|
||||
|
||||
nilTests := []struct {
|
||||
arg1, arg2 error
|
||||
expect bool
|
||||
msg string
|
||||
}{
|
||||
{nil, New("t"), false, "nil is not any concrete error"},
|
||||
{New("t"), nil, false, "no concrete error is nil"},
|
||||
{nil, nil, true, "nil is nil"},
|
||||
}
|
||||
for _, tst := range nilTests {
|
||||
assert.Equal(t, tst.expect, Is(tst.arg1, tst.arg2), tst.msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPCode(t *testing.T) {
|
||||
basicErr := errors.New("blag")
|
||||
if c := HTTPCode(basicErr); c != 500 {
|
||||
t.Errorf("default code should be 500, was %d", c)
|
||||
}
|
||||
err := New("blug")
|
||||
if c := HTTPCode(err); c != 500 {
|
||||
t.Errorf("default code for rich errors should be 500, was %d", c)
|
||||
}
|
||||
errWCode := err.WithHTTPCode(404)
|
||||
if c := HTTPCode(errWCode); c != 404 {
|
||||
t.Errorf("the code should be set to 404, was %d", c)
|
||||
}
|
||||
if HTTPCode(err) != 500 {
|
||||
t.Error("original error should not have been modified")
|
||||
}
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, WithHTTPCode(nil, 404))
|
||||
assert.Equal(t, 200, HTTPCode(nil), "The code for nil is 200 (ok)")
|
||||
}
|
||||
|
||||
func TestImplicitWrapping(t *testing.T) {
|
||||
// WithXXX functions will implicitly wrap non-merry errors
|
||||
// but if they do so, they should skip a frame, so the merry error's stack
|
||||
// appears to start wherever the WithXXX function was called
|
||||
|
||||
_, _, rl, _ := runtime.Caller(0)
|
||||
tests := []struct {
|
||||
f func() error
|
||||
fname string
|
||||
}{
|
||||
{fname: "WithHTTPCode", f: func() error { return WithHTTPCode(errors.New("bug"), 404) }},
|
||||
{fname: "WithUserMessage", f: func() error { return WithUserMessage(errors.New("bug"), "asdf") }},
|
||||
{fname: "WithUserMessages", f: func() error { return WithUserMessagef(errors.New("bug"), "asdf") }},
|
||||
{fname: "WithMessage", f: func() error { return WithMessage(errors.New("bug"), "asdf") }},
|
||||
{fname: "WithMessagef", f: func() error { return WithMessagef(errors.New("bug"), "asdf") }},
|
||||
{fname: "WithValue", f: func() error { return WithValue(errors.New("bug"), "asdf", "asdf") }},
|
||||
{fname: "Append", f: func() error { return Append(errors.New("bug"), "asdf") }},
|
||||
{fname: "Appendf", f: func() error { return Appendf(errors.New("bug"), "asdf") }},
|
||||
{fname: "Prepend", f: func() error { return Prepend(errors.New("bug"), "asdf") }},
|
||||
{fname: "Prependf", f: func() error { return Prependf(errors.New("bug"), "asdf") }},
|
||||
}
|
||||
for i, test := range tests {
|
||||
t.Log("Testing ", test.fname)
|
||||
err := test.f()
|
||||
f, l := Location(err)
|
||||
assert.Contains(t, f, "errors_test.go", "error message should have contained errors_test.go")
|
||||
assert.Equal(t, rl+5+i, l, "error line number was incorrect")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMessage(t *testing.T) {
|
||||
err1 := New("blug")
|
||||
err2 := err1.WithMessage("blee")
|
||||
err3 := err2.WithMessage("red")
|
||||
assert.EqualError(t, err1, "blug")
|
||||
assert.EqualError(t, err2, "blee", "should have overridden the underlying message")
|
||||
assert.EqualError(t, err3, "red")
|
||||
assert.Equal(t, Stack(err1), Stack(err2), "stack should not have been altered")
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, WithMessage(nil, ""))
|
||||
}
|
||||
|
||||
func TestWithMessagef(t *testing.T) {
|
||||
err1 := New("blug")
|
||||
err2 := err1.WithMessagef("super %v", "stew")
|
||||
err3 := err1.WithMessagef("blue %v", "red")
|
||||
assert.EqualError(t, err1, "blug")
|
||||
assert.EqualError(t, err2, "super stew")
|
||||
assert.EqualError(t, err3, "blue red")
|
||||
assert.Equal(t, Stack(err1), Stack(err2), "stack should not have been altered")
|
||||
// nil -> nil
|
||||
assert.Nil(t, WithMessagef(nil, "", ""))
|
||||
}
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
tests := []error{
|
||||
errors.New("one"),
|
||||
WithMessage(errors.New("blue"), "one"),
|
||||
New("one"),
|
||||
}
|
||||
for _, test := range tests {
|
||||
assert.Equal(t, "one", test.Error())
|
||||
assert.Equal(t, "one", Message(test))
|
||||
}
|
||||
|
||||
// when verbose is on, Error() changes, but Message() doesn't
|
||||
defer SetVerboseDefault(false)
|
||||
SetVerboseDefault(true)
|
||||
e := New("two")
|
||||
assert.Equal(t, "two", Message(e))
|
||||
assert.NotEqual(t, "two", e.Error())
|
||||
|
||||
// when error is nil, return ""
|
||||
assert.Empty(t, Message(nil))
|
||||
|
||||
}
|
||||
|
||||
func TestWithUserMessage(t *testing.T) {
|
||||
fault := New("seg fault")
|
||||
e := WithUserMessage(fault, "a glitch")
|
||||
assert.Equal(t, "seg fault", e.Error())
|
||||
assert.Equal(t, "a glitch", UserMessage(e))
|
||||
e = WithUserMessagef(e, "not a %s deal", "huge")
|
||||
assert.Equal(t, "not a huge deal", UserMessage(e))
|
||||
// If user message is set and regular message isn't, set regular message to user message
|
||||
e = New("").WithUserMessage("a blag")
|
||||
assert.Equal(t, "a blag", UserMessage(e))
|
||||
assert.Equal(t, "a blag", e.Error())
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
blug := New("blug")
|
||||
err := blug.Append("blog")
|
||||
assert.Equal(t, err.Error(), "blug: blog")
|
||||
err = Append(err, "blig")
|
||||
assert.Equal(t, err.Error(), "blug: blog: blig")
|
||||
err = blug.Appendf("%s", "blog")
|
||||
assert.Equal(t, err.Error(), "blug: blog")
|
||||
err = Appendf(err, "%s", "blig")
|
||||
assert.Equal(t, err.Error(), "blug: blog: blig")
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, Append(nil, ""))
|
||||
assert.Nil(t, Appendf(nil, "", ""))
|
||||
}
|
||||
|
||||
func TestPrepend(t *testing.T) {
|
||||
blug := New("blug")
|
||||
err := blug.Prepend("blog")
|
||||
assert.Equal(t, err.Error(), "blog: blug")
|
||||
err = Prepend(err, "blig")
|
||||
assert.Equal(t, err.Error(), "blig: blog: blug")
|
||||
err = blug.Prependf("%s", "blog")
|
||||
assert.Equal(t, err.Error(), "blog: blug")
|
||||
err = Prependf(err, "%s", "blig")
|
||||
assert.Equal(t, err.Error(), "blig: blog: blug")
|
||||
|
||||
// nil -> nil
|
||||
assert.Nil(t, Prepend(nil, ""))
|
||||
assert.Nil(t, Prependf(nil, "", ""))
|
||||
}
|
||||
|
||||
func TestLocation(t *testing.T) {
|
||||
// nil -> nil
|
||||
f, l := Location(nil)
|
||||
assert.Equal(t, "", f)
|
||||
assert.Equal(t, 0, l)
|
||||
}
|
||||
|
||||
func TestSourceLine(t *testing.T) {
|
||||
source := SourceLine(nil)
|
||||
assert.Equal(t, source, "")
|
||||
|
||||
err := New("foo")
|
||||
source = SourceLine(err)
|
||||
t.Log(source)
|
||||
assert.NotEqual(t, source, "")
|
||||
|
||||
parts := strings.Split(source, ":")
|
||||
assert.Equal(t, len(parts), 2)
|
||||
|
||||
if !strings.HasSuffix(parts[0], "errors_test.go") {
|
||||
t.Error("source line should contain file name")
|
||||
}
|
||||
if i, e := strconv.Atoi(parts[1]); e != nil {
|
||||
t.Errorf("not a number: %s", parts[1])
|
||||
} else if i <= 0 {
|
||||
t.Errorf("source line must be > 1: %s", parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestValue(t *testing.T) {
|
||||
// nil -> nil
|
||||
assert.Nil(t, WithValue(nil, "", ""))
|
||||
assert.Nil(t, Value(nil, ""))
|
||||
}
|
||||
|
||||
func TestValues(t *testing.T) {
|
||||
// nil -> nil
|
||||
values := Values(nil)
|
||||
assert.Nil(t, values)
|
||||
|
||||
var e error
|
||||
e = New("bad stuff")
|
||||
e = WithValue(e, "key1", "val1")
|
||||
e = WithValue(e, "key2", "val2")
|
||||
|
||||
values = Values(e)
|
||||
assert.NotNil(t, values)
|
||||
assert.Equal(t, values["key1"], "val1")
|
||||
assert.Equal(t, values["key2"], "val2")
|
||||
assert.NotNil(t, values[stack])
|
||||
|
||||
// make sure the last value attached is returned
|
||||
e = WithValue(e, "key3", "val3")
|
||||
e = WithValue(e, "key3", "val4")
|
||||
values = Values(e)
|
||||
assert.Equal(t, values["key3"], "val4")
|
||||
|
||||
}
|
||||
|
||||
func TestStackCaptureEnabled(t *testing.T) {
|
||||
// on by default
|
||||
assert.True(t, StackCaptureEnabled())
|
||||
|
||||
SetStackCaptureEnabled(false)
|
||||
assert.False(t, StackCaptureEnabled())
|
||||
e := New("yikes")
|
||||
assert.Empty(t, Stack(e))
|
||||
// let's just make sure none of the print functions bomb when there's no stack
|
||||
assert.Empty(t, SourceLine(e))
|
||||
f, l := Location(e)
|
||||
assert.Empty(t, f)
|
||||
assert.Equal(t, 0, l)
|
||||
assert.Empty(t, Stacktrace(e))
|
||||
assert.NotPanics(t, func() { Details(e) })
|
||||
|
||||
// turn it back on
|
||||
SetStackCaptureEnabled(true)
|
||||
assert.True(t, StackCaptureEnabled())
|
||||
|
||||
e = New("mommy")
|
||||
assert.NotEmpty(t, Stack(e))
|
||||
}
|
||||
|
||||
func TestVerboseDefault(t *testing.T) {
|
||||
defer SetVerboseDefault(false)
|
||||
// off by default
|
||||
assert.False(t, VerboseDefault())
|
||||
|
||||
SetVerboseDefault(true)
|
||||
assert.True(t, VerboseDefault())
|
||||
e := New("yikes")
|
||||
// test verbose on
|
||||
assert.Equal(t, Details(e), e.Error())
|
||||
// test verbose off
|
||||
SetVerboseDefault(false)
|
||||
s := e.Error()
|
||||
assert.Equal(t, Message(e), s)
|
||||
assert.Equal(t, "yikes", s)
|
||||
}
|
||||
|
||||
func TestMerryErr_Error(t *testing.T) {
|
||||
origVerbose := verbose
|
||||
defer func() {
|
||||
verbose = origVerbose
|
||||
}()
|
||||
|
||||
// test with verbose on
|
||||
verbose = false
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
verbose bool
|
||||
message, userMessage string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "with message",
|
||||
message: "blue",
|
||||
expected: "blue",
|
||||
},
|
||||
{
|
||||
desc: "with user message",
|
||||
userMessage: "red",
|
||||
expected: "red",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Log("error message tests: " + test.desc)
|
||||
verbose = test.verbose
|
||||
err := New(test.message).WithUserMessage(test.userMessage)
|
||||
t.Log(err.Error())
|
||||
assert.Equal(t, test.expected, err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMerryErr_Format(t *testing.T) {
|
||||
e := New("Hi")
|
||||
assert.Equal(t, fmt.Sprintf("%v", e), e.Error())
|
||||
assert.Equal(t, fmt.Sprintf("%s", e), e.Error())
|
||||
assert.Equal(t, fmt.Sprintf("%q", e), fmt.Sprintf("%q", e.Error()))
|
||||
assert.Equal(t, fmt.Sprintf("%+v", e), Details(e))
|
||||
}
|
||||
|
||||
func BenchmarkNew_withStackCapture(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
New("boom")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNew_withoutStackCapture(b *testing.B) {
|
||||
SetStackCaptureEnabled(false)
|
||||
for i := 0; i < b.N; i++ {
|
||||
New("boom")
|
||||
}
|
||||
}
|
68
vendor/github.com/ansel1/merry/print.go
generated
vendored
Normal file
68
vendor/github.com/ansel1/merry/print.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package merry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Location returns zero values if e has no stacktrace
|
||||
func Location(e error) (file string, line int) {
|
||||
s := Stack(e)
|
||||
if len(s) > 0 {
|
||||
fnc := runtime.FuncForPC(s[0])
|
||||
if fnc != nil {
|
||||
return fnc.FileLine(s[0])
|
||||
}
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
|
||||
// SourceLine returns the string representation of
|
||||
// Location's result or an empty string if there's
|
||||
// no stracktrace.
|
||||
func SourceLine(e error) string {
|
||||
file, line := Location(e)
|
||||
if line != 0 {
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Stacktrace returns the error's stacktrace as a string formatted
|
||||
// the same way as golangs runtime package.
|
||||
// If e has no stacktrace, returns an empty string.
|
||||
func Stacktrace(e error) string {
|
||||
s := Stack(e)
|
||||
if len(s) > 0 {
|
||||
buf := bytes.Buffer{}
|
||||
for _, fp := range s {
|
||||
fnc := runtime.FuncForPC(fp)
|
||||
if fnc != nil {
|
||||
f, l := fnc.FileLine(fp)
|
||||
buf.WriteString(fnc.Name())
|
||||
buf.WriteString(fmt.Sprintf("\n\t%s:%d\n", f, l))
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Details returns e.Error() and e's stacktrace and user message, if set.
|
||||
func Details(e error) string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
msg := Message(e)
|
||||
userMsg := UserMessage(e)
|
||||
if userMsg != "" {
|
||||
msg = fmt.Sprintf("%s\n\nUser Message: %s", msg, userMsg)
|
||||
}
|
||||
s := Stacktrace(e)
|
||||
if s != "" {
|
||||
msg += "\n\n" + s
|
||||
}
|
||||
return msg
|
||||
}
|
Reference in New Issue
Block a user