// 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 digimport ()// Error is an interface implemented by all Dig errors.//// Use this interface, in conjunction with [RootCause], in order to// determine if errors you encounter come from Dig, or if they come// from provided constructors or invoked functions. See [RootCause]// for more info.typeErrorinterface {error// Writes the message or context for this error in the chain. // // Note: the Error interface must always have a private function // such as this one in order to maintain properly sealed. // // verb is either %v or %+v. writeMessage(w io.Writer, v string)}// a digError is a dig.Error with additional functionality for// internal use - namely the ability to be formatted.type digError interface {Errorfmt.Formatter}// A PanicError occurs when a panic occurs while running functions given to the container// with the [RecoverFromPanic] option being set. It contains the panic message from the// original panic. A PanicError does not wrap other errors, and it does not implement// dig.Error, meaning it will be returned from [RootCause]. With the [RecoverFromPanic]// option set, a panic can be distinguished from dig errors and errors from provided/// invoked/decorated functions like so://// rootCause := dig.RootCause(err)//// var pe dig.PanicError// var de dig.Error// if errors.As(rootCause, &pe) {// // This is caused by a panic// } else if errors.As(err, &de) {// // This is a dig error// } else {// // This is an error from one of my provided/invoked functions or decorators// }//// Or, if only interested in distinguishing panics from errors://// var pe dig.PanicError// if errors.As(err, &pe) {// // This is caused by a panic// } else {// // This is an error// }typePanicErrorstruct {// The function the panic occurred at fn *digreflect.Func// The panic that was returned from recover() Panic any}// Format will format the PanicError, expanding the corresponding function if in +v mode.func ( PanicError) ( fmt.State, rune) {if .Flag('+') && == 'v' {fmt.Fprintf(, "panic: %q in func: %+v", .Panic, .fn) } else {fmt.Fprintf(, "panic: %q in func: %v", .Panic, .fn) }}func ( PanicError) () string {returnfmt.Sprint()}// formatError will call a dig.Error's writeMessage() method to print the error message// and then will automatically attempt to print errors wrapped underneath (which can create// a recursive effect if the wrapped error's Format() method then points back to this function).func formatError( digError, fmt.State, rune) { := .Flag('+') && == 'v' := "%v"if { = "%+v" }// "context: " or "context:\n" .writeMessage(, )// Will route back to this function recursively if next error // is also wrapped and points back here := errors.Unwrap()if == nil {return }io.WriteString(, ":")if {io.WriteString(, "\n") } else {io.WriteString(, " ") }fmt.Fprintf(, , )}// RootCause returns the first non-dig.Error in a chain of wrapped// errors, if there is one. Otherwise, RootCause returns the error// on the bottom of the chain of wrapped errors.//// Use this function and errors.As to differentiate between Dig errors// and errors thrown by provided constructors or invoked functions://// rootCause := dig.RootCause(err)// var de dig.Error// if errors.As(rootCause, &de) {// // Is a Dig error// } else {// // Is an error thrown by one of my provided/invoked/decorated functions// }//// See [PanicError] for an example showing how to additionally detect// and handle panics in provided/invoked/decorated functions.func ( error) error {varError// Dig down to first non dig.Error, or bottom of chainfor ; errors.As(, &); = errors.Unwrap() { }if == nil {return }return}// errInvalidInput is returned whenever the user provides bad input when// interacting with the container. May optionally have a more detailed// error wrapped underneath.type errInvalidInput struct { Message string Cause error}var _ digError = errInvalidInput{}// newErrInvalidInput creates a new errInvalidInput, wrapping the given// other error that caused this error. If there is no underlying cause,// pass in nil. This will cause all attempts to unwrap this error to return// nil, replicating errors.Unwrap's behavior when passed an error without// an Unwrap() method.func newErrInvalidInput( string, error) errInvalidInput {returnerrInvalidInput{, }}func ( errInvalidInput) () string { returnfmt.Sprint() }func ( errInvalidInput) () error { return .Cause }func ( errInvalidInput) ( io.Writer, string) {fmt.Fprint(, .Message)}func ( errInvalidInput) ( fmt.State, rune) {formatError(, , )}// errProvide is returned when a constructor could not be Provided into the// container.type errProvide struct { Func *digreflect.Func Reason error}var _ digError = errProvide{}func ( errProvide) () string { returnfmt.Sprint() }func ( errProvide) () error { return .Reason }func ( errProvide) ( io.Writer, string) {fmt.Fprintf(, "cannot provide function "+, .Func)}func ( errProvide) ( fmt.State, rune) {formatError(, , )}// errConstructorFailed is returned when a user-provided constructor failed// with a non-nil error.type errConstructorFailed struct { Func *digreflect.Func Reason error}var _ digError = errConstructorFailed{}func ( errConstructorFailed) () string { returnfmt.Sprint() }func ( errConstructorFailed) () error { return .Reason }func ( errConstructorFailed) ( io.Writer, string) {fmt.Fprintf(, "received non-nil error from function "+, .Func)}func ( errConstructorFailed) ( fmt.State, rune) {formatError(, , )}// 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 _ digError = errArgumentsFailed{}func ( errArgumentsFailed) () string { returnfmt.Sprint() }func ( errArgumentsFailed) () error { return .Reason }func ( errArgumentsFailed) ( io.Writer, string) {fmt.Fprintf(, "could not build arguments for function "+, .Func)}func ( errArgumentsFailed) ( fmt.State, rune) {formatError(, , )}// errMissingDependencies is returned when the dependencies of a function are// not available in the container.type errMissingDependencies struct { Func *digreflect.Func Reason error}var _ digError = errMissingDependencies{}func ( errMissingDependencies) () string { returnfmt.Sprint() }func ( errMissingDependencies) () error { return .Reason }func ( errMissingDependencies) ( io.Writer, string) {fmt.Fprintf(, "missing dependencies for function "+, .Func)}func ( errMissingDependencies) ( fmt.State, rune) {formatError(, , )}// errParamSingleFailed is returned when a paramSingle could not be built.type errParamSingleFailed struct { Key key Reason error CtorID dot.CtorID}var _ digError = errParamSingleFailed{}func ( errParamSingleFailed) () string { returnfmt.Sprint() }func ( errParamSingleFailed) () error { return .Reason }func ( errParamSingleFailed) ( io.Writer, string) {fmt.Fprintf(, "failed to build %v", .Key)}func ( errParamSingleFailed) ( fmt.State, rune) {formatError(, , )}func ( errParamSingleFailed) ( *dot.Graph) { := &dot.Result{Node: &dot.Node{Name: .Key.name,Group: .Key.group,Type: .Key.t, }, } .FailNodes([]*dot.Result{}, .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 _ digError = errParamGroupFailed{}func ( errParamGroupFailed) () string { returnfmt.Sprint() }func ( errParamGroupFailed) () error { return .Reason }func ( errParamGroupFailed) ( io.Writer, string) {fmt.Fprintf(, "could not build value group %v", .Key)}func ( errParamGroupFailed) ( fmt.State, rune) {formatError(, , )}func ( errParamGroupFailed) ( *dot.Graph) { .FailGroupNodes(.Key.group, .Key.t, .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 ( missingType) ( fmt.State, rune) { := .Flag('+') && == 'v'fmt.Fprint(, .Key)switchlen(.suggestions) {case0:if {io.WriteString(, " (did you mean to Provide it?)") }case1: := .suggestions[0]if {fmt.Fprintf(, " (did you mean to use %v?)", ) } else {fmt.Fprintf(, " (did you mean %v?)", ) }default:if {io.WriteString(, " (did you mean to use one of ") } else {io.WriteString(, " (did you mean ") } := len(.suggestions) - 1for , := range .suggestions {if > 0 {io.WriteString(, ", ")if == {io.WriteString(, "or ") } }fmt.Fprint(, ) }io.WriteString(, "?)") }}// 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 > 0var _ digError = errMissingTypes(nil)func newErrMissingTypes( containerStore, 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. := []reflect.Type{reflect.PointerTo(.t)}if .t.Kind() == reflect.Ptr {// The user requested a pointer but maybe we have a value. = append(, .t.Elem()) }if .t.Kind() == reflect.Slice {// Maybe the user meant a slice of pointers while we have the slice of elements = append(, reflect.SliceOf(reflect.PointerTo(.t.Elem())))// Maybe the user meant a slice of elements while we have the slice of pointers := .t.Elem()if .Kind() == reflect.Ptr { = append(, reflect.SliceOf(.Elem())) } }if .t.Kind() == reflect.Array {// Maybe the user meant an array of pointers while we have the array of elements = append(, reflect.ArrayOf(.t.Len(), reflect.PointerTo(.t.Elem())))// Maybe the user meant an array of elements while we have the array of pointers := .t.Elem()if .Kind() == reflect.Ptr { = append(, reflect.ArrayOf(.t.Len(), .Elem())) } } := .knownTypes()if .t.Kind() == reflect.Interface {// Maybe we have an implementation of the interface.for , := range {if .Implements(.t) { = append(, ) } } } else {// Maybe we have an interface that this type implements.for , := range {if .Kind() == reflect.Interface {if .t.Implements() { = append(, ) } } } }// range through c.providers is non-deterministic. Let's sort the list of // suggestions.sort.Sort(byTypeName()) := missingType{Key: }for , := range {iflen(.getValueProviders(.name, )) > 0 { .t = .suggestions = append(.suggestions, ) } }returnerrMissingTypes{}}func ( errMissingTypes) () string { returnfmt.Sprint() }func ( errMissingTypes) ( io.Writer, string) { := == "%+v"iflen() == 1 {io.WriteString(, "missing type:") } else {io.WriteString(, "missing types:") }if ! {// With %v, we need a space between : since the error // won't be on a new line.io.WriteString(, " ") }for , := range {if {io.WriteString(, "\n\t- ") } elseif > 0 {io.WriteString(, "; ") }if {fmt.Fprintf(, "%+v", ) } else {fmt.Fprintf(, "%v", ) } }}func ( errMissingTypes) ( fmt.State, rune) {formatError(, , )}func ( errMissingTypes) ( *dot.Graph) { := make([]*dot.Result, len())for , := range { [] = &dot.Result{Node: &dot.Node{Name: .Key.name,Group: .Key.group,Type: .Key.t, }, } } .AddMissingNodes()}type errVisualizer interface { updateGraph(*dot.Graph)}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.