// Copyright (c) 2019-2021 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 ()// The result interface represents a result produced by a constructor.//// The following implementations exist:// resultList All values returned by the constructor.// resultSingle A single value produced by a constructor.// resultObject dig.Out struct where each field in the struct can be// another result.// resultGrouped A value produced by a constructor that is part of a value// group.type result interface {// Extracts the values for this result from the provided value and // stores them into the provided containerWriter. // // This MAY panic if the result does not consume a single value. Extract(containerWriter, bool, reflect.Value)// DotResult returns a slice of dot.Result(s). DotResult() []*dot.Result}var ( _ result = resultSingle{} _ result = resultObject{} _ result = resultList{} _ result = resultGrouped{})type resultOptions struct {// If set, this is the name of the associated result value. // // For Result Objects, name:".." tags on fields override this. Name string Group string As []interface{}}// newResult builds a result from the given type.func newResult( reflect.Type, resultOptions) (result, error) {switch {caseIsIn() || (.Kind() == reflect.Ptr && IsIn(.Elem())) || embedsType(, _inPtrType):returnnil, newErrInvalidInput(fmt.Sprintf("cannot provide parameter objects: %v embeds a dig.In", ), nil)caseisError():returnnil, newErrInvalidInput("cannot return an error here, return it from the constructor instead", nil)caseIsOut():returnnewResultObject(, )caseembedsType(, _outPtrType):returnnil, newErrInvalidInput(fmt.Sprintf("cannot build a result object by embedding *dig.Out, embed dig.Out instead: %v embeds *dig.Out", ), nil)case .Kind() == reflect.Ptr && IsOut(.Elem()):returnnil, newErrInvalidInput(fmt.Sprintf("cannot return a pointer to a result object, use a value instead: %v is a pointer to a struct that embeds dig.Out", ), nil)caselen(.Group) > 0: , := parseGroupString(.Group)if != nil {returnnil, newErrInvalidInput(fmt.Sprintf("cannot parse group %q", .Group), ) } := resultGrouped{Type: , Group: .Name, Flatten: .Flatten}iflen(.As) > 0 {var []reflect.Typefor , := range .As { := reflect.TypeOf().Elem()if == {continue }if !.Implements() {returnnil, newErrInvalidInput(fmt.Sprintf("invalid dig.As: %v does not implement %v", , ), nil) } = append(, ) }iflen() > 0 { .Type = [0] .As = [1:] } }if .Soft {returnnil, newErrInvalidInput(fmt.Sprintf("cannot use soft with result value groups: soft was used with group:%q", .Name), nil) }if .Flatten {if .Kind() != reflect.Slice {returnnil, newErrInvalidInput(fmt.Sprintf("flatten can be applied to slices only: %v is not a slice", ), nil) } .Type = .Type.Elem() }return , nildefault:returnnewResultSingle(, ) }}// resultVisitor visits every result in a result tree, allowing tracking state// at each level.type resultVisitor interface {// Visit is called on the result being visited. // // If Visit returns a non-nil resultVisitor, that resultVisitor visits all // the child results of this result. Visit(result) resultVisitor// AnnotateWithField is called on each field of a resultObject after // visiting it but before walking its descendants. // // The same resultVisitor is used for all fields: the one returned upon // visiting the resultObject. // // For each visited field, if AnnotateWithField returns a non-nil // resultVisitor, it will be used to walk the result of that field. AnnotateWithField(resultObjectField) resultVisitor// AnnotateWithPosition is called with the index of each result of a // resultList after vising it but before walking its descendants. // // The same resultVisitor is used for all results: the one returned upon // visiting the resultList. // // For each position, if AnnotateWithPosition returns a non-nil // resultVisitor, it will be used to walk the result at that index. AnnotateWithPosition(idx int) resultVisitor}// walkResult walks the result tree for the given result with the provided// visitor.//// resultVisitor.Visit will be called on the provided result and if a non-nil// resultVisitor is received, it will be used to walk its descendants. If a// resultObject or resultList was visited, AnnotateWithField and// AnnotateWithPosition respectively will be called before visiting the// descendants of that resultObject/resultList.//// This is very similar to how go/ast.Walk works.func walkResult( result, resultVisitor) { = .Visit()if == nil {return }switch res := .(type) {caseresultSingle, resultGrouped:// No sub-resultscaseresultObject: := for , := range .Fields {if := .AnnotateWithField(); != nil { (.Result, ) } }caseresultList: := for , := range .Results {if := .AnnotateWithPosition(); != nil { (, ) } }default:digerror.BugPanicf("received unknown result type %T", ) }}// resultList holds all values returned by the constructor as results.type resultList struct { ctype reflect.Type Results []result// For each item at index i returned by the constructor, resultIndexes[i] // is the index in .Results for the corresponding result object. // resultIndexes[i] is -1 for errors returned by constructors. resultIndexes []int}func ( resultList) () []*dot.Result {var []*dot.Resultfor , := range .Results { = append(, .DotResult()...) }return}func newResultList( reflect.Type, resultOptions) (resultList, error) { := .NumOut() := resultList{ctype: ,Results: make([]result, 0, ),resultIndexes: make([]int, ), } := 0for := 0; < ; ++ { := .Out()ifisError() { .resultIndexes[] = -1continue } , := newResult(, )if != nil {return , newErrInvalidInput(fmt.Sprintf("bad result %d", +1), ) } .Results = append(.Results, ) .resultIndexes[] = ++ }return , nil}func (resultList) (containerWriter, bool, reflect.Value) {digerror.BugPanicf("resultList.Extract() must never be called")}func ( resultList) ( containerWriter, bool, []reflect.Value) error {for , := range {if := .resultIndexes[]; >= 0 { .Results[].Extract(, , )continue }if , := .Interface().(error); != nil {return } }returnnil}// resultSingle is an explicit value produced by a constructor, optionally// with a name.//// This object will be added to the graph as-is.type resultSingle struct { Name string Type reflect.Type// If specified, this is a list of types which the value will be made // available as, in addition to its own type. As []reflect.Type}func newResultSingle( reflect.Type, resultOptions) (resultSingle, error) { := resultSingle{Type: ,Name: .Name, }var []reflect.Typefor , := range .As { := reflect.TypeOf().Elem()if == {// Special case: // c.Provide(func() io.Reader, As(new(io.Reader))) // Ignore instead of erroring out.continue }if !.Implements() {return , newErrInvalidInput(fmt.Sprintf("invalid dig.As: %v does not implement %v", , ), nil) } = append(, ) }iflen() == 0 {return , nil }returnresultSingle{Type: [0],Name: .Name,As: [1:], }, nil}func ( resultSingle) () []*dot.Result { := make([]*dot.Result, 0, len(.As)+1) = append(, &dot.Result{Node: &dot.Node{Type: .Type,Name: .Name, }, })for , := range .As { = append(, &dot.Result{Node: &dot.Node{Type: , Name: .Name}, }) }return}func ( resultSingle) ( containerWriter, bool, reflect.Value) {if { .setDecoratedValue(.Name, .Type, )return } .setValue(.Name, .Type, )for , := range .As { .setValue(.Name, , ) }}// resultObject is a dig.Out struct where each field is another result.//// This object is not added to the graph. Its fields are interpreted as// results and added to the graph if needed.type resultObject struct { Type reflect.Type Fields []resultObjectField}func ( resultObject) () []*dot.Result {var []*dot.Resultfor , := range .Fields { = append(, .DotResult()...) }return}func newResultObject( reflect.Type, resultOptions) (resultObject, error) { := resultObject{Type: }iflen(.Name) > 0 {return , newErrInvalidInput(fmt.Sprintf("cannot specify a name for result objects: %v embeds dig.Out", ), nil) }iflen(.Group) > 0 {return , newErrInvalidInput(fmt.Sprintf("cannot specify a group for result objects: %v embeds dig.Out", ), nil) }for := 0; < .NumField(); ++ { := .Field()if .Type == _outType {// Skip over the dig.Out embed.continue } , := newResultObjectField(, , )if != nil {return , newErrInvalidInput(fmt.Sprintf("bad field %q of %v", .Name, ), ) } .Fields = append(.Fields, ) }return , nil}func ( resultObject) ( containerWriter, bool, reflect.Value) {for , := range .Fields { .Result.Extract(, , .Field(.FieldIndex)) }}// resultObjectField is a single field inside a dig.Out struct.type resultObjectField struct {// Name of the field in the struct. FieldName string// Index of the field in the struct. // // We need to track this separately because not all fields of the struct // map to results. FieldIndex int// Result produced by this field. Result result}func ( resultObjectField) () []*dot.Result {return .Result.DotResult()}// newResultObjectField(i, f, opts) builds a resultObjectField from the field// f at index i.func newResultObjectField( int, reflect.StructField, resultOptions) (resultObjectField, error) { := resultObjectField{FieldName: .Name,FieldIndex: , }varresultswitch {case .PkgPath != "":return , newErrInvalidInput(fmt.Sprintf("unexported fields not allowed in dig.Out, did you mean to export %q (%v)?", .Name, .Type), nil)case .Tag.Get(_groupTag) != "":varerror , = newResultGrouped()if != nil {return , }default:varerrorif := .Tag.Get(_nameTag); len() > 0 {// can modify in-place because options are passed-by-value. .Name = } , = newResult(.Type, )if != nil {return , } } .Result = return , nil}// resultGrouped is a value produced by a constructor that is part of a result// group.//// These will be produced as fields of a dig.Out struct.type resultGrouped struct {// Name of the group as specified in the `group:".."` tag. Group string// Type of value produced. Type reflect.Type// Indicates elements of a value are to be injected individually, instead of // as a group. Requires the value's slice to be a group. If set, Type will be // the type of individual elements rather than the group. Flatten bool// If specified, this is a list of types which the value will be made // available as, in addition to its own type. As []reflect.Type}func ( resultGrouped) () []*dot.Result { := make([]*dot.Result, 0, len(.As)+1) = append(, &dot.Result{Node: &dot.Node{Type: .Type,Group: .Group, }, })for , := range .As { = append(, &dot.Result{Node: &dot.Node{Type: , Group: .Group}, }) }return}// newResultGrouped(f) builds a new resultGrouped from the provided field.func newResultGrouped( reflect.StructField) (resultGrouped, error) { , := parseGroupString(.Tag.Get(_groupTag))if != nil {returnresultGrouped{}, } := resultGrouped{Group: .Name,Flatten: .Flatten,Type: .Type, } := .Tag.Get(_nameTag) , := isFieldOptional()switch {case .Flatten && .Type.Kind() != reflect.Slice:return , newErrInvalidInput(fmt.Sprintf("flatten can be applied to slices only: field %q (%v) is not a slice", .Name, .Type), nil)case .Soft:return , newErrInvalidInput(fmt.Sprintf("cannot use soft with result value groups: soft was used with group %q", .Group), nil)case != "":return , newErrInvalidInput(fmt.Sprintf("cannot use named values with value groups: name:%q provided with group:%q", , .Group), nil)case :return , newErrInvalidInput("value groups cannot be optional", nil) }if .Flatten { .Type = .Type.Elem() }return , nil}func ( resultGrouped) ( containerWriter, bool, reflect.Value) {// Decorated values are always flattened.if ! && !.Flatten { .submitGroupedValue(.Group, .Type, )for , := range .As { .submitGroupedValue(.Group, , ) }return }if { .submitDecoratedGroupedValue(.Group, .Type, )return }for := 0; < .Len(); ++ { .submitGroupedValue(.Group, .Type, .Index()) }}
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.