// 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 dig

import (
	
	
	
	
	

	
	
)

// The param interface represents a dependency for a constructor.
//
// The following implementations exist:
//
//	paramList     All arguments of the constructor.
//	paramSingle   An explicitly requested type.
//	paramObject   dig.In struct where each field in the struct can be another
//	              param.
//	paramGroupedSlice
//	              A slice consuming a value group. This will receive all
//	              values produced with a `group:".."` tag with the same name
//	              as a slice.
type param interface {
	fmt.Stringer

	// Build this dependency and any of its dependencies from the provided
	// Container.
	//
	// This MAY panic if the param does not produce a single value.
	Build(store containerStore) (reflect.Value, error)

	// DotParam returns a slice of dot.Param(s).
	DotParam() []*dot.Param
}

var (
	_ param = paramSingle{}
	_ param = paramObject{}
	_ param = paramList{}
	_ param = paramGroupedSlice{}
)

// newParam builds a param from the given type. If the provided type is a
// dig.In struct, an paramObject will be returned.
func newParam( reflect.Type,  containerStore) (param, error) {
	switch {
	case IsOut() || (.Kind() == reflect.Ptr && IsOut(.Elem())) || embedsType(, _outPtrType):
		return nil, newErrInvalidInput(fmt.Sprintf(
			"cannot depend on result objects: %v embeds a dig.Out", ), nil)
	case IsIn():
		return newParamObject(, )
	case embedsType(, _inPtrType):
		return nil, newErrInvalidInput(fmt.Sprintf(
			"cannot build a parameter object by embedding *dig.In, embed dig.In instead: %v embeds *dig.In", ), nil)
	case .Kind() == reflect.Ptr && IsIn(.Elem()):
		return nil, newErrInvalidInput(fmt.Sprintf(
			"cannot depend on a pointer to a parameter object, use a value instead: %v is a pointer to a struct that embeds dig.In", ), nil)
	default:
		return paramSingle{Type: }, nil
	}
}

// paramList holds all arguments of the constructor as params.
//
// NOTE: Build() MUST NOT be called on paramList. Instead, BuildList
// must be called.
type paramList struct {
	ctype reflect.Type // type of the constructor

	Params []param
}

func ( paramList) () []*dot.Param {
	var  []*dot.Param
	for ,  := range .Params {
		 = append(, .DotParam()...)
	}
	return 
}

func ( paramList) () string {
	 := make([]string, len(.Params))
	for ,  := range .Params {
		[] = .String()
	}
	return fmt.Sprint()
}

// newParamList builds a paramList from the provided constructor type.
//
// Variadic arguments of a constructor are ignored and not included as
// dependencies.
func newParamList( reflect.Type,  containerStore) (paramList, error) {
	 := .NumIn()
	if .IsVariadic() {
		// NOTE: If the function is variadic, we skip the last argument
		// because we're not filling variadic arguments yet. See #120.
		--
	}

	 := paramList{
		ctype:  ,
		Params: make([]param, ),
	}

	for  := 0;  < ; ++ {
		,  := newParam(.In(), )
		if  != nil {
			return , newErrInvalidInput(fmt.Sprintf("bad argument %d", +1), )
		}
		.Params[] = 
	}

	return , nil
}

func ( paramList) (containerStore) (reflect.Value, error) {
	digerror.BugPanicf("paramList.Build() must never be called")
	panic("") // Unreachable, as BugPanicf above will panic.
}

// BuildList returns an ordered list of values which may be passed directly
// to the underlying constructor.
func ( paramList) ( containerStore) ([]reflect.Value, error) {
	 := make([]reflect.Value, len(.Params))
	for ,  := range .Params {
		var  error
		[],  = .Build()
		if  != nil {
			return nil, 
		}
	}
	return , nil
}

// paramSingle is an explicitly requested type, optionally with a name.
//
// This object must be present in the graph as-is unless it's specified as
// optional.
type paramSingle struct {
	Name     string
	Optional bool
	Type     reflect.Type
}

func ( paramSingle) () []*dot.Param {
	return []*dot.Param{
		{
			Node: &dot.Node{
				Type: .Type,
				Name: .Name,
			},
			Optional: .Optional,
		},
	}
}

func ( paramSingle) () string {
	// tally.Scope[optional] means optional
	// tally.Scope[optional, name="foo"] means named optional

	var  []string
	if .Optional {
		 = append(, "optional")
	}
	if .Name != "" {
		 = append(, fmt.Sprintf("name=%q", .Name))
	}

	if len() == 0 {
		return fmt.Sprint(.Type)
	}

	return fmt.Sprintf("%v[%v]", .Type, strings.Join(, ", "))
}

// search the given container and its ancestors for a decorated value.
func ( paramSingle) ( containerStore) (reflect.Value, bool) {
	for ,  := range .storesToRoot() {
		if ,  := .getDecoratedValue(.Name, .Type);  {
			return , 
		}
	}
	return _noValue, false
}

// builds the parameter using decorators in all scopes that affect the
// current scope, if there are any. If there are multiple Scopes that decorates
// this parameter, the closest one to the Scope that invoked this will be used.
// If there are no decorators associated with this parameter, _noValue is returned.
func ( paramSingle) ( containerStore) ( reflect.Value,  bool,  error) {
	var (
		               decorator
		 containerStore
	)
	 := .storesToRoot()

	for ,  := range  {
		if ,  = .getValueDecorator(.Name, .Type); ! {
			continue
		}
		if .State() == decoratorOnStack {
			// This decorator is already being run.
			// Avoid a cycle and look further.
			 = nil
			continue
		}
		 = 
		break
	}
	if ! ||  == nil {
		return _noValue, false, nil
	}
	if  = .Call();  != nil {
		,  = _noValue, errParamSingleFailed{
			CtorID: 1,
			Key:    key{t: .Type, name: .Name},
			Reason: ,
		}
		return , , 
	}
	, _ = .getDecoratedValue(.Name, .Type)
	return
}

func ( paramSingle) ( containerStore) (reflect.Value, error) {
	, ,  := .buildWithDecorators()
	if  {
		return , 
	}

	// Check whether the value is a decorated value first.
	if ,  := .getDecoratedValue();  {
		return , nil
	}

	// Starting at the given container and working our way up its parents,
	// find one that provides this dependency.
	//
	// Once found, we'll use that container for the rest of the invocation.
	// Dependencies of this type will begin searching at that container,
	// rather than starting at base.
	var  []provider
	var  containerStore
	for ,  := range .storesToRoot() {
		// first check if the scope already has cached a value for the type.
		if ,  := .getValue(.Name, .Type);  {
			return , nil
		}
		 = .getValueProviders(.Name, .Type)
		if len() > 0 {
			 = 
			break
		}
	}

	if len() == 0 {
		if .Optional {
			return reflect.Zero(.Type), nil
		}
		return _noValue, newErrMissingTypes(, key{name: .Name, t: .Type})
	}

	for ,  := range  {
		 := .Call(.OrigScope())
		if  == nil {
			continue
		}

		// If we're missing dependencies but the parameter itself is optional,
		// we can just move on.
		if errors.As(, new(errMissingDependencies)) && .Optional {
			return reflect.Zero(.Type), nil
		}

		return _noValue, errParamSingleFailed{
			CtorID: .ID(),
			Key:    key{t: .Type, name: .Name},
			Reason: ,
		}
	}

	// If we get here, it's impossible for the value to be absent from the
	// container.
	, _ = .getValue(.Name, .Type)
	return , nil
}

// paramObject is a dig.In struct where each field is another param.
//
// This object is not expected in the graph as-is.
type paramObject struct {
	Type        reflect.Type
	Fields      []paramObjectField
	FieldOrders []int
}

func ( paramObject) () []*dot.Param {
	var  []*dot.Param
	for ,  := range .Fields {
		 = append(, .DotParam()...)
	}
	return 
}

func ( paramObject) () string {
	 := make([]string, len(.Fields))
	for ,  := range .Fields {
		[] = .Param.String()
	}
	return strings.Join(, " ")
}

// getParamOrder returns the order(s) of a parameter type.
func getParamOrder( *graphHolder,  param) []int {
	var  []int
	switch p := .(type) {
	case paramSingle:
		 := .s.getAllValueProviders(.Name, .Type)
		for ,  := range  {
			 = append(, .Order(.s))
		}
	case paramGroupedSlice:
		// value group parameters have nodes of their own.
		// We can directly return that here.
		 = append(, .orders[.s])
	case paramObject:
		for ,  := range .Fields {
			 = append(, (, .Param)...)
		}
	}
	return 
}

// newParamObject builds an paramObject from the provided type. The type MUST
// be a dig.In struct.
func newParamObject( reflect.Type,  containerStore) (paramObject, error) {
	 := paramObject{Type: }

	// Check if the In type supports ignoring unexported fields.
	var  bool
	for  := 0;  < .NumField(); ++ {
		 := .Field()
		if .Type == _inType {
			var  error
			,  = isIgnoreUnexportedSet()
			if  != nil {
				return , 
			}
			break
		}
	}

	for  := 0;  < .NumField(); ++ {
		 := .Field()
		if .Type == _inType {
			// Skip over the dig.In embed.
			continue
		}
		if .PkgPath != "" &&  {
			// Skip over an unexported field if it is allowed.
			continue
		}
		,  := newParamObjectField(, , )
		if  != nil {
			return , newErrInvalidInput(
				fmt.Sprintf("bad field %q of %v", .Name, ), )
		}
		.Fields = append(.Fields, )
	}
	return , nil
}

func ( paramObject) ( containerStore) (reflect.Value, error) {
	 := reflect.New(.Type).Elem()
	// We have to build soft groups after all other fields, to avoid cases
	// when a field calls a provider for a soft value group, but the value is
	// not provided to it because the value group is declared before the field
	var  []paramObjectField
	var  []paramObjectField
	for ,  := range .Fields {
		if ,  := .Param.(paramGroupedSlice);  && .Soft {
			 = append(, )
			continue
		}
		 = append(, )
	}
	 = append(, ...)
	for ,  := range  {
		,  := .Build()
		if  != nil {
			return , 
		}
		.Field(.FieldIndex).Set()
	}
	return , nil
}

// paramObjectField is a single field of a dig.In struct.
type paramObjectField struct {
	// Name of the field in the struct.
	FieldName string

	// Index of this field in the target struct.
	//
	// We need to track this separately because not all fields of the
	// struct map to params.
	FieldIndex int

	// The dependency requested by this field.
	Param param
}

func ( paramObjectField) () []*dot.Param {
	return .Param.DotParam()
}

func newParamObjectField( int,  reflect.StructField,  containerStore) (paramObjectField, error) {
	 := paramObjectField{
		FieldName:  .Name,
		FieldIndex: ,
	}

	var  param
	switch {
	case .PkgPath != "":
		return , newErrInvalidInput(
			fmt.Sprintf("unexported fields not allowed in dig.In, did you mean to export %q (%v)?", .Name, .Type), nil)

	case .Tag.Get(_groupTag) != "":
		var  error
		,  = newParamGroupedSlice(, )
		if  != nil {
			return , 
		}

	default:
		var  error
		,  = newParam(.Type, )
		if  != nil {
			return , 
		}
	}

	if ,  := .(paramSingle);  {
		.Name = .Tag.Get(_nameTag)

		var  error
		.Optional,  = isFieldOptional()
		if  != nil {
			return , 
		}

		 = 
	}

	.Param = 
	return , nil
}

func ( paramObjectField) ( containerStore) (reflect.Value, error) {
	,  := .Param.Build()
	if  != nil {
		return , 
	}
	return , nil
}

// paramGroupedSlice is a param which produces a slice of values with the same
// group name.
type paramGroupedSlice struct {
	// Name of the group as specified in the `group:".."` tag.
	Group string

	// Type of the slice.
	Type reflect.Type

	// Soft is used to denote a soft dependency between this param and its
	// constructors, if it's true its constructors are only called if they
	// provide another value requested in the graph
	Soft bool

	orders map[*Scope]int
}

func ( paramGroupedSlice) () string {
	// io.Reader[group="foo"] refers to a group of io.Readers called 'foo'
	return fmt.Sprintf("%v[group=%q]", .Type.Elem(), .Group)
}

func ( paramGroupedSlice) () []*dot.Param {
	return []*dot.Param{
		{
			Node: &dot.Node{
				Type:  .Type,
				Group: .Group,
			},
		},
	}
}

// newParamGroupedSlice builds a paramGroupedSlice from the provided type with
// the given name.
//
// The type MUST be a slice type.
func newParamGroupedSlice( reflect.StructField,  containerStore) (paramGroupedSlice, error) {
	,  := parseGroupString(.Tag.Get(_groupTag))
	if  != nil {
		return paramGroupedSlice{}, 
	}
	 := paramGroupedSlice{
		Group:  .Name,
		Type:   .Type,
		orders: make(map[*Scope]int),
		Soft:   .Soft,
	}

	 := .Tag.Get(_nameTag)
	,  := isFieldOptional()
	switch {
	case .Type.Kind() != reflect.Slice:
		return , newErrInvalidInput(
			fmt.Sprintf("value groups may be consumed as slices only: field %q (%v) is not a slice", .Name, .Type), nil)
	case .Flatten:
		return , newErrInvalidInput(
			fmt.Sprintf("cannot use flatten in parameter value groups: field %q (%v) specifies flatten", .Name, .Type), nil)
	case  != "":
		return , newErrInvalidInput(
			fmt.Sprintf("cannot use named values with value groups: name:%q requested with group:%q", , .Group), nil)
	case :
		return , newErrInvalidInput("value groups cannot be optional", nil)
	}
	.newGraphNode(&, .orders)
	return , nil
}

// retrieves any decorated values that may be committed in this scope, or
// any of the parent Scopes. In the case where there are multiple scopes that
// are decorating the same type, the closest scope in effect will be replacing
// any decorated value groups provided in further scopes.
func ( paramGroupedSlice) ( containerStore) (reflect.Value, bool) {
	for ,  := range .storesToRoot() {
		if ,  := .getDecoratedValueGroup(.Group, .Type);  {
			return , true
		}
	}
	return _noValue, false
}

// search the given container and its parents for matching group decorators
// and call them to commit values. If any decorators return an error,
// that error is returned immediately. If all decorators succeeds, nil is returned.
// The order in which the decorators are invoked is from the top level scope to
// the current scope, to account for decorators that decorate values that were
// already decorated.
func ( paramGroupedSlice) ( containerStore) error {
	 := .storesToRoot()
	for  := len() - 1;  >= 0; -- {
		 := []
		if ,  := .getGroupDecorator(.Group, .Type.Elem());  {
			if .State() == decoratorOnStack {
				// This decorator is already being run. Avoid cycle
				// and look further.
				continue
			}
			if  := .Call();  != nil {
				return errParamGroupFailed{
					CtorID: .ID(),
					Key:    key{group: .Group, t: .Type.Elem()},
					Reason: ,
				}
			}
		}
	}
	return nil
}

// search the given container and its parent for matching group providers and
// call them to commit values. If an error is encountered, return the number
// of providers called and a non-nil error from the first provided.
func ( paramGroupedSlice) ( containerStore) (int, error) {
	 := 0
	for ,  := range .storesToRoot() {
		 := .getGroupProviders(.Group, .Type.Elem())
		 += len()
		for ,  := range  {
			if  := .Call(.OrigScope());  != nil {
				return 0, errParamGroupFailed{
					CtorID: .ID(),
					Key:    key{group: .Group, t: .Type.Elem()},
					Reason: ,
				}
			}
		}
	}
	return , nil
}

func ( paramGroupedSlice) ( containerStore) (reflect.Value, error) {
	// do not call this if we are already inside a decorator since
	// it will result in an infinite recursion. (i.e. decorate -> params.BuildList() -> Decorate -> params.BuildList...)
	// this is safe since a value can be decorated at most once in a given scope.
	if  := .callGroupDecorators();  != nil {
		return _noValue, 
	}

	// Check if we have decorated values
	if ,  := .getDecoratedValues();  {
		return , nil
	}

	// If we do not have any decorated values and the group isn't soft,
	// find the providers and call them.
	 := 0
	if !.Soft {
		var  error
		,  = .callGroupProviders()
		if  != nil {
			return _noValue, 
		}
	}

	 := .storesToRoot()
	 := reflect.MakeSlice(.Type, 0, )
	for ,  := range  {
		 = reflect.Append(, .getValueGroup(.Group, .Type.Elem())...)
	}
	return , nil
}

// Checks if ignoring unexported files in an In struct is allowed.
// The struct field MUST be an _inType.
func isIgnoreUnexportedSet( reflect.StructField) (bool, error) {
	 := .Tag.Get(_ignoreUnexportedTag)
	if  == "" {
		return false, nil
	}

	,  := strconv.ParseBool()
	if  != nil {
		 = newErrInvalidInput(
			fmt.Sprintf("invalid value %q for %q tag on field %v", , _ignoreUnexportedTag, .Name), )
	}

	return , 
}