status-go/vendor/go.uber.org/dig/error.go

523 lines
14 KiB
Go

// Copyright (c) 2019 Uber Technologies, Inc.
//
// 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.
package dig
import (
"errors"
"fmt"
"io"
"reflect"
"sort"
"go.uber.org/dig/internal/digerror"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/dot"
)
// Errors which know their underlying cause should implement this interface to
// be compatible with RootCause.
//
// We use unexported methods because we don't want dig-internal causes to be
// confused with the cause of the user-provided errors. For example, if we
// used Unwrap(), then user-provided methods would also be unwrapped by
// RootCause. We want RootCause to eliminate the dig error chain only.
type causer interface {
fmt.Formatter
// Returns the next error in the chain.
cause() error
// Writes the message or context for this error in the chain.
//
// verb is either %v or %+v.
writeMessage(w io.Writer, verb string)
}
// Implements fmt.Formatter for errors that implement causer.
//
// This Format method supports %v and %+v. In the %v form, the error is
// printed on one line. In the %+v form, the error is split across multiple
// lines on each error in the error chain.
func formatCauser(c causer, w fmt.State, v rune) {
multiline := w.Flag('+') && v == 'v'
verb := "%v"
if multiline {
verb = "%+v"
}
// "context: " or "context:\n"
c.writeMessage(w, verb)
io.WriteString(w, ":")
if multiline {
io.WriteString(w, "\n")
} else {
io.WriteString(w, " ")
}
fmt.Fprintf(w, verb, c.cause())
}
// RootCause returns the original error that caused the provided dig failure.
//
// RootCause may be used on errors returned by Invoke to get the original
// error returned by a constructor or invoked function.
func RootCause(err error) error {
for {
if e, ok := err.(causer); ok {
err = e.cause()
} else {
return err
}
}
}
// errf is a version of fmt.Errorf with support for a chain of multiple
// formatted error messages.
//
// After msg, N arguments are consumed as formatting arguments for that
// message, where N is the number of % symbols in msg. Following that, another
// string may be added to become the next error in the chain. Each new error
// is the `cause()` for the prior error.
//
// err := errf(
// "could not process %v", thing,
// "name %q is invalid", thing.Name,
// )
// fmt.Println(err) // could not process Thing: name Foo is invalid
// fmt.Println(RootCause(err)) // name Foo is invalid
//
// In place of a string, the last error can be another error, in which case it
// will be treated as the cause of the prior error chain.
//
// errf(
// "could not process %v", thing,
// "date %q could not be parsed", thing.Date,
// parseError,
// )
func errf(msg string, args ...interface{}) error {
// By implementing buildErrf as a closure rather than a standalone
// function, we're able to ensure that it is called only from errf, or
// from itself (recursively). By controlling these invocations in such
// a tight space, we are able to easily verify manually that we
// checked len(args) > 0 before making the call.
var buildErrf func([]interface{}) error
buildErrf = func(args []interface{}) error {
arg, args := args[0], args[1:] // assume len(args) > 0
if arg == nil {
digerror.BugPanicf("arg must not be nil")
}
switch v := arg.(type) {
case string:
need := numFmtArgs(v)
if len(args) < need {
digerror.BugPanicf("string %q needs %v arguments, got %v",
v,
need,
len(args))
}
msg := fmt.Sprintf(v, args[:need]...)
args := args[need:]
// If we don't have anything left to chain with, build the
// final error.
if len(args) == 0 {
return errors.New(msg)
}
return wrappedError{
msg: msg,
err: buildErrf(args),
}
case error:
if len(args) > 0 {
digerror.BugPanicf("error must be the last element but got %v", args)
}
return v
default:
digerror.BugPanicf("unexpected errf-argument type %T", arg)
return nil
}
}
// Prepend msg to the args list so that we can re-use the same
// args processing logic. The msg is a string just for type-safety of
// the first error.
newArgs := make([]interface{}, len(args)+1)
newArgs[0] = msg
copy(newArgs[1:], args)
return buildErrf(newArgs)
}
// Returns the number of formatting arguments in the provided string. Does not
// count escaped % symbols, specifically the string "%%".
//
// fmt.Println(numFmtArgs("rate: %d%%")) // 1
func numFmtArgs(s string) int {
var (
count int
percent bool // saw %
)
for _, c := range s {
if percent && c != '%' {
// Counts only if it's not a %%.
count++
}
// Next iteration should consider % only if the current %
// stands alone.
percent = !percent && c == '%'
}
return count
}
type wrappedError struct {
err error
msg string
}
var _ causer = wrappedError{}
func (e wrappedError) cause() error {
return e.err
}
func (e wrappedError) writeMessage(w io.Writer, _ string) {
io.WriteString(w, e.msg)
}
func (e wrappedError) Error() string { return fmt.Sprint(e) }
func (e wrappedError) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
// errProvide is returned when a constructor could not be Provided into the
// container.
type errProvide struct {
Func *digreflect.Func
Reason error
}
var _ causer = errProvide{}
func (e errProvide) cause() error {
return e.Reason
}
func (e errProvide) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "cannot provide function "+verb, e.Func)
}
func (e errProvide) Error() string { return fmt.Sprint(e) }
func (e errProvide) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
// errConstructorFailed is returned when a user-provided constructor failed
// with a non-nil error.
type errConstructorFailed struct {
Func *digreflect.Func
Reason error
}
var _ causer = errConstructorFailed{}
func (e errConstructorFailed) cause() error {
return e.Reason
}
func (e errConstructorFailed) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "received non-nil error from function "+verb, e.Func)
}
func (e errConstructorFailed) Error() string { return fmt.Sprint(e) }
func (e errConstructorFailed) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
// errArgumentsFailed is returned when a function could not be run because one
// of its dependencies failed to build for any reason.
type errArgumentsFailed struct {
Func *digreflect.Func
Reason error
}
var _ causer = errArgumentsFailed{}
func (e errArgumentsFailed) cause() error {
return e.Reason
}
func (e errArgumentsFailed) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "could not build arguments for function "+verb, e.Func)
}
func (e errArgumentsFailed) Error() string { return fmt.Sprint(e) }
func (e errArgumentsFailed) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
// errMissingDependencies is returned when the dependencies of a function are
// not available in the container.
type errMissingDependencies struct {
Func *digreflect.Func
Reason error
}
var _ causer = errMissingDependencies{}
func (e errMissingDependencies) cause() error {
return e.Reason
}
func (e errMissingDependencies) writeMessage(w io.Writer, verb string) {
fmt.Fprintf(w, "missing dependencies for function "+verb, e.Func)
}
func (e errMissingDependencies) Error() string { return fmt.Sprint(e) }
func (e errMissingDependencies) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
// errParamSingleFailed is returned when a paramSingle could not be built.
type errParamSingleFailed struct {
Key key
Reason error
CtorID dot.CtorID
}
var _ causer = errParamSingleFailed{}
func (e errParamSingleFailed) cause() error {
return e.Reason
}
func (e errParamSingleFailed) writeMessage(w io.Writer, _ string) {
fmt.Fprintf(w, "failed to build %v", e.Key)
}
func (e errParamSingleFailed) Error() string { return fmt.Sprint(e) }
func (e errParamSingleFailed) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
func (e errParamSingleFailed) updateGraph(g *dot.Graph) {
failed := &dot.Result{
Node: &dot.Node{
Name: e.Key.name,
Group: e.Key.group,
Type: e.Key.t,
},
}
g.FailNodes([]*dot.Result{failed}, e.CtorID)
}
// errParamGroupFailed is returned when a value group cannot be built because
// any of the values in the group failed to build.
type errParamGroupFailed struct {
Key key
Reason error
CtorID dot.CtorID
}
var _ causer = errParamGroupFailed{}
func (e errParamGroupFailed) cause() error {
return e.Reason
}
func (e errParamGroupFailed) writeMessage(w io.Writer, _ string) {
fmt.Fprintf(w, "could not build value group %v", e.Key)
}
func (e errParamGroupFailed) Error() string { return fmt.Sprint(e) }
func (e errParamGroupFailed) Format(w fmt.State, c rune) {
formatCauser(e, w, c)
}
func (e errParamGroupFailed) updateGraph(g *dot.Graph) {
g.FailGroupNodes(e.Key.group, e.Key.t, e.CtorID)
}
// missingType holds information about a type that was missing in the
// container.
type missingType struct {
Key key // item that was missing
// If non-empty, we will include suggestions for what the user may have
// meant.
suggestions []key
}
// Format prints a string representation of missingType.
//
// With %v, it prints a short representation ideal for an itemized list.
//
// io.Writer
// io.Writer: did you mean *bytes.Buffer?
// io.Writer: did you mean *bytes.Buffer, or *os.File?
//
// With %+v, it prints a longer representation ideal for standalone output.
//
// io.Writer: did you mean to Provide it?
// io.Writer: did you mean to use *bytes.Buffer?
// io.Writer: did you mean to use one of *bytes.Buffer, or *os.File?
func (mt missingType) Format(w fmt.State, v rune) {
plusV := w.Flag('+') && v == 'v'
fmt.Fprint(w, mt.Key)
switch len(mt.suggestions) {
case 0:
if plusV {
io.WriteString(w, " (did you mean to Provide it?)")
}
case 1:
sug := mt.suggestions[0]
if plusV {
fmt.Fprintf(w, " (did you mean to use %v?)", sug)
} else {
fmt.Fprintf(w, " (did you mean %v?)", sug)
}
default:
if plusV {
io.WriteString(w, " (did you mean to use one of ")
} else {
io.WriteString(w, " (did you mean ")
}
lastIdx := len(mt.suggestions) - 1
for i, sug := range mt.suggestions {
if i > 0 {
io.WriteString(w, ", ")
if i == lastIdx {
io.WriteString(w, "or ")
}
}
fmt.Fprint(w, sug)
}
io.WriteString(w, "?)")
}
}
// errMissingType is returned when one or more values that were expected in
// the container were not available.
//
// Multiple instances of this error may be merged together by appending them.
type errMissingTypes []missingType // inv: len > 0
func newErrMissingTypes(c containerStore, k key) errMissingTypes {
// Possible types we will look for in the container. We will always look
// for pointers to the requested type and some extras on a per-Kind basis.
suggestions := []reflect.Type{reflect.PtrTo(k.t)}
if k.t.Kind() == reflect.Ptr {
// The user requested a pointer but maybe we have a value.
suggestions = append(suggestions, k.t.Elem())
}
knownTypes := c.knownTypes()
if k.t.Kind() == reflect.Interface {
// Maybe we have an implementation of the interface.
for _, t := range knownTypes {
if t.Implements(k.t) {
suggestions = append(suggestions, t)
}
}
} else {
// Maybe we have an interface that this type implements.
for _, t := range knownTypes {
if t.Kind() == reflect.Interface {
if k.t.Implements(t) {
suggestions = append(suggestions, t)
}
}
}
}
// range through c.providers is non-deterministic. Let's sort the list of
// suggestions.
sort.Sort(byTypeName(suggestions))
mt := missingType{Key: k}
for _, t := range suggestions {
if len(c.getValueProviders(k.name, t)) > 0 {
k.t = t
mt.suggestions = append(mt.suggestions, k)
}
}
return errMissingTypes{mt}
}
func (e errMissingTypes) Error() string {
return fmt.Sprint(e)
}
func (e errMissingTypes) Format(w fmt.State, v rune) {
multiline := w.Flag('+') && v == 'v'
if len(e) == 1 {
io.WriteString(w, "missing type:")
} else {
io.WriteString(w, "missing types:")
}
if !multiline {
// With %v, we need a space between : since the error
// won't be on a new line.
io.WriteString(w, " ")
}
for i, mt := range e {
if multiline {
io.WriteString(w, "\n\t- ")
} else if i > 0 {
io.WriteString(w, "; ")
}
if multiline {
fmt.Fprintf(w, "%+v", mt)
} else {
fmt.Fprintf(w, "%v", mt)
}
}
}
func (e errMissingTypes) updateGraph(g *dot.Graph) {
missing := make([]*dot.Result, len(e))
for i, mt := range e {
missing[i] = &dot.Result{
Node: &dot.Node{
Name: mt.Key.name,
Group: mt.Key.group,
Type: mt.Key.t,
},
}
}
g.AddMissingNodes(missing)
}
type errVisualizer interface {
updateGraph(*dot.Graph)
}