func (p *plannerConfiguration) isNestedPlanner() bool {
	for i := range p.paths {
		pathElements := strings.Count(p.paths[i].path, ".") + 1
		if pathElements == 2 {
			return false
		}
	}
	return true
}
isNestedPlanner returns true in case the planner is not directly attached to the Operation root
a nested planner should always build a Query
package plan
import (
	"bytes"
	"context"
	"encoding/json"
	"reflect"
	"regexp"
	"strings"
	"github.com/jensneuse/graphql-go-tools/pkg/ast"
	"github.com/jensneuse/graphql-go-tools/pkg/astimport"
	"github.com/jensneuse/graphql-go-tools/pkg/astvisitor"
	"github.com/jensneuse/graphql-go-tools/pkg/engine/resolve"
	"github.com/jensneuse/graphql-go-tools/pkg/lexer/literal"
	"github.com/jensneuse/graphql-go-tools/pkg/operationreport"
)
type Planner struct {
	config                Configuration
	configurationWalker   *astvisitor.Walker
	configurationVisitor  *configurationVisitor
	planningWalker        *astvisitor.Walker
	planningVisitor       *Visitor
	requiredFieldsWalker  *astvisitor.Walker
	requiredFieldsVisitor *requiredFieldsVisitor
}
type Configuration struct {
	DefaultFlushIntervalMillis int64
	DataSources                []DataSourceConfiguration
	Fields                     FieldConfigurations
}
type FieldConfigurations []FieldConfiguration
func (f FieldConfigurations) ForTypeField(typeName, fieldName string) *FieldConfiguration {
	for i := range f {
		if f[i].TypeName == typeName && f[i].FieldName == fieldName {
			return &f[i]
		}
	}
	return nil
}
type FieldConfiguration struct {
	TypeName              string
	FieldName             string
	DisableDefaultMapping bool
	Path                  []string
	Arguments             ArgumentsConfigurations
	RequiresFields        []string
}
type ArgumentsConfigurations []ArgumentConfiguration
func (a ArgumentsConfigurations) ForName(argName string) *ArgumentConfiguration {
	for i := range a {
		if a[i].Name == argName {
			return &a[i]
		}
	}
	return nil
}
type SourceType string
type ArgumentRenderConfig string
const (
	ObjectFieldSource            SourceType           = "object_field"
	FieldArgumentSource          SourceType           = "field_argument"
	RenderArgumentDefault        ArgumentRenderConfig = ""
	RenderArgumentAsArrayCSV     ArgumentRenderConfig = "render_argument_as_array_csv"
	RenderArgumentAsGraphQLValue ArgumentRenderConfig = "render_argument_as_graphql_value"
)
type ArgumentConfiguration struct {
	Name         string
	SourceType   SourceType
	SourcePath   []string
	RenderConfig ArgumentRenderConfig
}
type DataSourceConfiguration struct {
	RootNodes  []TypeField
	ChildNodes []TypeField
	Factory    PlannerFactory
	Custom     json.RawMessage
}
func (d *DataSourceConfiguration) HasRootNode(typeName, fieldName string) bool {
	for i := range d.RootNodes {
		if typeName != d.RootNodes[i].TypeName {
			continue
		}
		for j := range d.RootNodes[i].FieldNames {
			if fieldName == d.RootNodes[i].FieldNames[j] {
				return true
			}
		}
	}
	return false
}
type PlannerFactory interface {
	Planner(ctx context.Context) DataSourcePlanner
}
type TypeField struct {
	TypeName   string
	FieldNames []string
}
type FieldMapping struct {
	TypeName              string
	FieldName             string
	DisableDefaultMapping bool
	Path                  []string
}
func NewPlanner(ctx context.Context, config Configuration) *Planner {
	requiredFieldsWalker := astvisitor.NewWalker(48)
	requiredFieldsV := &requiredFieldsVisitor{
		walker: &requiredFieldsWalker,
	}
	requiredFieldsWalker.RegisterEnterDocumentVisitor(requiredFieldsV)
	requiredFieldsWalker.RegisterEnterOperationVisitor(requiredFieldsV)
	requiredFieldsWalker.RegisterEnterFieldVisitor(requiredFieldsV)
	configurationWalker := astvisitor.NewWalker(48)
	configVisitor := &configurationVisitor{
		walker: &configurationWalker,
		ctx:    ctx,
	}
	configurationWalker.RegisterEnterDocumentVisitor(configVisitor)
	configurationWalker.RegisterFieldVisitor(configVisitor)
	configurationWalker.RegisterEnterOperationVisitor(configVisitor)
	planningWalker := astvisitor.NewWalker(48)
	planningVisitor := &Visitor{
		Walker:       &planningWalker,
		fieldConfigs: map[int]*FieldConfiguration{},
	}
	p := &Planner{
		config:                config,
		configurationWalker:   &configurationWalker,
		configurationVisitor:  configVisitor,
		planningWalker:        &planningWalker,
		planningVisitor:       planningVisitor,
		requiredFieldsWalker:  &requiredFieldsWalker,
		requiredFieldsVisitor: requiredFieldsV,
	}
	return p
}
func (p *Planner) SetConfig(config Configuration) {
	p.config = config
}
func (p *Planner) Plan(operation, definition *ast.Document, operationName string, report *operationreport.Report) (plan Plan) {
	config := p.config
	p.selectOperation(operation, operationName, report)
	if report.HasErrors() {
		return
	}
	p.preProcessRequiredFields(&config, operation, definition, report)
	p.configurationVisitor.config = config
	p.configurationWalker.Walk(operation, definition, report)
	p.planningVisitor.planners = p.configurationVisitor.planners
	p.planningVisitor.Config = config
	p.planningVisitor.fetchConfigurations = p.configurationVisitor.fetches
	p.planningVisitor.fieldBuffers = p.configurationVisitor.fieldBuffers
	p.planningVisitor.skipFieldPaths = p.requiredFieldsVisitor.skipFieldPaths
	p.planningWalker.ResetVisitors()
	p.planningWalker.SetVisitorFilter(p.planningVisitor)
	p.planningWalker.RegisterDocumentVisitor(p.planningVisitor)
	p.planningWalker.RegisterEnterOperationVisitor(p.planningVisitor)
	p.planningWalker.RegisterFieldVisitor(p.planningVisitor)
	p.planningWalker.RegisterSelectionSetVisitor(p.planningVisitor)
	p.planningWalker.RegisterEnterDirectiveVisitor(p.planningVisitor)
	for key := range p.planningVisitor.planners {
		custom := p.planningVisitor.planners[key].dataSourceConfiguration.Custom
		isNested := p.planningVisitor.planners[key].isNestedPlanner()
		err := p.planningVisitor.planners[key].planner.Register(p.planningVisitor, custom, isNested)
		if err != nil {
			p.planningWalker.StopWithInternalErr(err)
		}
	}
	p.planningWalker.Walk(operation, definition, report)
	return p.planningVisitor.plan
}
func (p *Planner) selectOperation(operation *ast.Document, operationName string, report *operationreport.Report) {
	numOfOperations := operation.NumOfOperationDefinitions()
	operationName = strings.TrimSpace(operationName)
	if len(operationName) == 0 && numOfOperations > 1 {
		report.AddExternalError(operationreport.ErrRequiredOperationNameIsMissing())
		return
	}
	if len(operationName) == 0 && numOfOperations == 1 {
		operationName = operation.OperationDefinitionNameString(0)
	}
	if !operation.OperationNameExists(operationName) {
		report.AddExternalError(operationreport.ErrOperationWithProvidedOperationNameNotFound(operationName))
		return
	}
	p.requiredFieldsVisitor.operationName = operationName
	p.configurationVisitor.operationName = operationName
	p.planningVisitor.OperationName = operationName
}
func (p *Planner) preProcessRequiredFields(config *Configuration, operation, definition *ast.Document, report *operationreport.Report) {
	if !p.hasRequiredFields(config) {
		return
	}
	p.requiredFieldsVisitor.config = config
	p.requiredFieldsVisitor.operation = operation
	p.requiredFieldsVisitor.definition = definition
	p.requiredFieldsWalker.Walk(operation, definition, report)
}
func (p *Planner) hasRequiredFields(config *Configuration) bool {
	for i := range config.Fields {
		if len(config.Fields[i].RequiresFields) != 0 {
			return true
		}
	}
	return false
}
type Visitor struct {
	Operation, Definition *ast.Document
	Walker                *astvisitor.Walker
	Importer              astimport.Importer
	Config                Configuration
	plan                  Plan
	OperationName         string
	operationDefinition   int
	objects               []*resolve.Object
	currentFields         []objectFields
	currentField          *resolve.Field
	planners              []plannerConfiguration
	fetchConfigurations   []objectFetchConfiguration
	fieldBuffers          map[int]int
	skipFieldPaths        []string
	fieldConfigs          map[int]*FieldConfiguration
}
type objectFields struct {
	popOnField int
	fields     *[]*resolve.Field
}
type objectFetchConfiguration struct {
	object         *resolve.Object
	trigger        *resolve.GraphQLSubscriptionTrigger
	planner        DataSourcePlanner
	bufferID       int
	isSubscription bool
	fieldRef       int
}
func (v *Visitor) AllowVisitor(kind astvisitor.VisitorKind, ref int, visitor interface{}) bool {
	if visitor == v {
		return true
	}
	path := v.Walker.Path.DotDelimitedString()
	switch kind {
	case astvisitor.EnterField, astvisitor.LeaveField:
		fieldAliasOrName := v.Operation.FieldAliasOrNameString(ref)
		path = path + "." + fieldAliasOrName
	}
	if !strings.Contains(path, ".") {
		return true
	}
	for _, config := range v.planners {
		if config.planner == visitor && config.hasPath(path) {
			switch kind {
			case astvisitor.EnterSelectionSet, astvisitor.LeaveSelectionSet:
				return !config.isExitPath(path)
			default:
				return true
			}
		}
	}
	return false
}
func (v *Visitor) currentFullPath() string {
	path := v.Walker.Path.DotDelimitedString()
	if v.Walker.CurrentKind == ast.NodeKindField {
		fieldAliasOrName := v.Operation.FieldAliasOrNameString(v.Walker.CurrentRef)
		path = path + "." + fieldAliasOrName
	}
	return path
}
func (v *Visitor) EnterDirective(ref int) {
	directiveName := v.Operation.DirectiveNameString(ref)
	ancestor := v.Walker.Ancestors[len(v.Walker.Ancestors)-1]
	switch ancestor.Kind {
	case ast.NodeKindOperationDefinition:
		switch directiveName {
		case "flushInterval":
			if value, ok := v.Operation.DirectiveArgumentValueByName(ref, literal.MILLISECONDS); ok {
				if value.Kind == ast.ValueKindInteger {
					v.plan.SetFlushInterval(v.Operation.IntValueAsInt(value.Ref))
				}
			}
		}
	case ast.NodeKindField:
		switch directiveName {
		case "stream":
			initialBatchSize := 0
			if value, ok := v.Operation.DirectiveArgumentValueByName(ref, literal.INITIAL_BATCH_SIZE); ok {
				if value.Kind == ast.ValueKindInteger {
					initialBatchSize = int(v.Operation.IntValueAsInt(value.Ref))
				}
			}
			v.currentField.Stream = &resolve.StreamField{
				InitialBatchSize: initialBatchSize,
			}
		case "defer":
			v.currentField.Defer = &resolve.DeferField{}
		}
	}
}
func (v *Visitor) LeaveSelectionSet(ref int) {
}
func (v *Visitor) EnterSelectionSet(ref int) {
}
func (v *Visitor) EnterField(ref int) {
	if v.skipField(ref) {
		return
	}
	fieldName := v.Operation.FieldNameBytes(ref)
	fieldAliasOrName := v.Operation.FieldAliasOrNameBytes(ref)
	if bytes.Equal(fieldName, literal.TYPENAME) {
		v.currentField = &resolve.Field{
			Name: fieldAliasOrName,
			Value: &resolve.String{
				Nullable: false,
				Path:     v.resolveFieldPath(ref),
			},
			OnTypeName: v.resolveOnTypeName(),
			Position: resolve.Position{
				Line:   v.Operation.Fields[ref].Position.LineStart,
				Column: v.Operation.Fields[ref].Position.CharStart,
			},
		}
		*v.currentFields[len(v.currentFields)-1].fields = append(*v.currentFields[len(v.currentFields)-1].fields, v.currentField)
		return
	}
	fieldDefinition, ok := v.Walker.FieldDefinitionWithExists(ref)
	if !ok {
		return
	}
	var (
		hasFetchConfig bool
		i              int
	)
	for i = range v.fetchConfigurations {
		if ref == v.fetchConfigurations[i].fieldRef {
			hasFetchConfig = true
			break
		}
	}
	if hasFetchConfig {
		if v.fetchConfigurations[i].isSubscription {
			plan, ok := v.plan.(*SubscriptionResponsePlan)
			if ok {
				v.fetchConfigurations[i].trigger = &plan.Response.Trigger
			}
		} else {
			v.fetchConfigurations[i].object = v.objects[len(v.objects)-1]
		}
	}
	path := v.resolveFieldPath(ref)
	fieldDefinitionType := v.Definition.FieldDefinitionType(fieldDefinition)
	bufferID, hasBuffer := v.fieldBuffers[ref]
	v.currentField = &resolve.Field{
		Name:       fieldAliasOrName,
		Value:      v.resolveFieldValue(ref, fieldDefinitionType, true, path),
		HasBuffer:  hasBuffer,
		BufferID:   bufferID,
		OnTypeName: v.resolveOnTypeName(),
		Position: resolve.Position{
			Line:   v.Operation.Fields[ref].Position.LineStart,
			Column: v.Operation.Fields[ref].Position.CharStart,
		},
	}
	*v.currentFields[len(v.currentFields)-1].fields = append(*v.currentFields[len(v.currentFields)-1].fields, v.currentField)
	typeName := v.Walker.EnclosingTypeDefinition.NameString(v.Definition)
	fieldNameStr := v.Operation.FieldNameString(ref)
	fieldConfig := v.Config.Fields.ForTypeField(typeName, fieldNameStr)
	if fieldConfig == nil {
		return
	}
	v.fieldConfigs[ref] = fieldConfig
}
func (v *Visitor) resolveOnTypeName() []byte {
	if len(v.Walker.Ancestors) < 2 {
		return nil
	}
	inlineFragment := v.Walker.Ancestors[len(v.Walker.Ancestors)-2]
	if inlineFragment.Kind != ast.NodeKindInlineFragment {
		return nil
	}
	return v.Operation.InlineFragmentTypeConditionName(inlineFragment.Ref)
}
func (v *Visitor) LeaveField(ref int) {
	if v.currentFields[len(v.currentFields)-1].popOnField == ref {
		v.currentFields = v.currentFields[:len(v.currentFields)-1]
	}
	fieldDefinition, ok := v.Walker.FieldDefinitionWithExists(ref)
	if !ok {
		return
	}
	fieldDefinitionTypeNode := v.Definition.FieldDefinitionTypeNode(fieldDefinition)
	switch fieldDefinitionTypeNode.Kind {
	case ast.NodeKindObjectTypeDefinition, ast.NodeKindInterfaceTypeDefinition:
		v.objects = v.objects[:len(v.objects)-1]
	}
}
func (v *Visitor) skipField(ref int) bool {
	fullPath := v.Walker.Path.DotDelimitedString() + "." + v.Operation.FieldAliasOrNameString(ref)
	for i := range v.skipFieldPaths {
		if v.skipFieldPaths[i] == fullPath {
			return true
		}
	}
	return false
}
func (v *Visitor) resolveFieldValue(fieldRef, typeRef int, nullable bool, path []string) resolve.Node {
	ofType := v.Definition.Types[typeRef].OfType
	switch v.Definition.Types[typeRef].TypeKind {
	case ast.TypeKindNonNull:
		return v.resolveFieldValue(fieldRef, ofType, false, path)
	case ast.TypeKindList:
		listItem := v.resolveFieldValue(fieldRef, ofType, true, nil)
		return &resolve.Array{
			Nullable: nullable,
			Path:     path,
			Item:     listItem,
		}
	case ast.TypeKindNamed:
		typeName := v.Definition.ResolveTypeNameString(typeRef)
		typeDefinitionNode, ok := v.Definition.Index.FirstNodeByNameStr(typeName)
		if !ok {
			return &resolve.Null{}
		}
		switch typeDefinitionNode.Kind {
		case ast.NodeKindScalarTypeDefinition:
			switch typeName {
			case "String":
				return &resolve.String{
					Path:     path,
					Nullable: nullable,
				}
			case "Boolean":
				return &resolve.Boolean{
					Path:     path,
					Nullable: nullable,
				}
			case "Int":
				return &resolve.Integer{
					Path:     path,
					Nullable: nullable,
				}
			case "Float":
				return &resolve.Float{
					Path:     path,
					Nullable: nullable,
				}
			default:
				return &resolve.String{
					Path:     path,
					Nullable: nullable,
				}
			}
		case ast.NodeKindEnumTypeDefinition:
			return &resolve.String{
				Path:     path,
				Nullable: nullable,
			}
		case ast.NodeKindObjectTypeDefinition, ast.NodeKindInterfaceTypeDefinition, ast.NodeKindUnionTypeDefinition:
			object := &resolve.Object{
				Nullable: nullable,
				Path:     path,
				Fields:   []*resolve.Field{},
			}
			v.objects = append(v.objects, object)
			v.Walker.Defer(func() {
				v.currentFields = append(v.currentFields, objectFields{
					popOnField: fieldRef,
					fields:     &object.Fields,
				})
			})
			return object
		default:
			return &resolve.Null{}
		}
	default:
		return &resolve.Null{}
	}
}
func (v *Visitor) EnterOperationDefinition(ref int) {
	operationName := v.Operation.OperationDefinitionNameString(ref)
	if v.OperationName != operationName {
		v.Walker.SkipNode()
		return
	}
	v.operationDefinition = ref
	rootObject := &resolve.Object{
		Fields: []*resolve.Field{},
	}
	v.objects = append(v.objects, rootObject)
	v.currentFields = append(v.currentFields, objectFields{
		fields:     &rootObject.Fields,
		popOnField: -1,
	})
	isSubscription, _, err := AnalyzePlanKind(v.Operation, v.Definition, v.OperationName)
	if err != nil {
		v.Walker.StopWithInternalErr(err)
		return
	}
	graphQLResponse := &resolve.GraphQLResponse{
		Data: rootObject,
	}
	if isSubscription {
		v.plan = &SubscriptionResponsePlan{
			FlushInterval: v.Config.DefaultFlushIntervalMillis,
			Response: &resolve.GraphQLSubscription{
				Response: graphQLResponse,
			},
		}
		return
	}
	v.plan = &SynchronousResponsePlan{
		Response: graphQLResponse,
	}
}
func (v *Visitor) resolveFieldPath(ref int) []string {
	typeName := v.Walker.EnclosingTypeDefinition.NameString(v.Definition)
	fieldName := v.Operation.FieldNameUnsafeString(ref)
	config := v.currentOrParentPlannerConfiguration()
	aliasOverride := false
	if config.planner != nil {
		aliasOverride = config.planner.DataSourcePlanningBehavior().OverrideFieldPathFromAlias
	}
	for i := range v.Config.Fields {
		if v.Config.Fields[i].TypeName == typeName && v.Config.Fields[i].FieldName == fieldName {
			if aliasOverride {
				override, exists := config.planner.DownstreamResponseFieldAlias(ref)
				if exists {
					return []string{override}
				}
			}
			if aliasOverride && v.Operation.FieldAliasIsDefined(ref) {
				return []string{v.Operation.FieldAliasString(ref)}
			}
			if v.Config.Fields[i].DisableDefaultMapping {
				return nil
			}
			if len(v.Config.Fields[i].Path) != 0 {
				return v.Config.Fields[i].Path
			}
			return []string{fieldName}
		}
	}
	if aliasOverride {
		return []string{v.Operation.FieldAliasOrNameString(ref)}
	}
	return []string{fieldName}
}
func (v *Visitor) EnterDocument(operation, definition *ast.Document) {
	v.Operation, v.Definition = operation, definition
	v.fieldConfigs = map[int]*FieldConfiguration{}
}
func (v *Visitor) LeaveDocument(operation, definition *ast.Document) {
	for _, config := range v.fetchConfigurations {
		if config.isSubscription {
			v.configureSubscription(config)
		} else {
			v.configureObjectFetch(config)
		}
	}
}
var (
	templateRegex = regexp.MustCompile(`{{.*?}}`)
	selectorRegex = regexp.MustCompile(`{{\s*\.(.*?)\s*}}`)
)
func (v *Visitor) currentOrParentPlannerConfiguration() plannerConfiguration {
	const none = -1
	currentPath := v.currentFullPath()
	plannerIndex := none
	plannerPathDeepness := none
	for i := range v.planners {
		for _, plannerPath := range v.planners[i].paths {
			if v.isCurrentOrParentPath(currentPath, plannerPath.path) {
				currentPlannerPathDeepness := v.pathDeepness(plannerPath.path)
				if currentPlannerPathDeepness > plannerPathDeepness {
					plannerPathDeepness = currentPlannerPathDeepness
					plannerIndex = i
					break
				}
			}
		}
	}
	if plannerIndex != none {
		return v.planners[plannerIndex]
	}
	return plannerConfiguration{}
}
func (v *Visitor) isCurrentOrParentPath(currentPath string, parentPath string) bool {
	return strings.HasPrefix(currentPath, parentPath)
}
func (v *Visitor) pathDeepness(path string) int {
	return strings.Count(path, ".")
}
func (v *Visitor) resolveInputTemplates(config objectFetchConfiguration, input *string, variables *resolve.Variables) {
	*input = templateRegex.ReplaceAllStringFunc(*input, func(s string) string {
		selectors := selectorRegex.FindStringSubmatch(s)
		if len(selectors) != 2 {
			return s
		}
		selector := strings.TrimPrefix(selectors[1], ".")
		parts := strings.Split(selector, ".")
		if len(parts) < 2 {
			return s
		}
		path := parts[1:]
		var (
			variableName string
		)
		switch parts[0] {
		case "object":
			variable := &resolve.ObjectVariable{
				Path: path,
				RenderAsPlainValue: true,
			}
			variableName, _ = variables.AddVariable(variable)
		case "arguments":
			argumentName := path[0]
			arg, ok := v.Operation.FieldArgument(config.fieldRef, []byte(argumentName))
			if !ok {
				break
			}
			value := v.Operation.ArgumentValue(arg)
			if value.Kind != ast.ValueKindVariable {
				break
			}
			variableValue := v.Operation.VariableValueNameString(value.Ref)
			if !v.Operation.OperationDefinitionHasVariableDefinition(v.operationDefinition, variableValue) {
				break  
			}
			variable := &resolve.ContextVariable{
				Path: []string{variableValue},
			}
			variableDefinition, exists := v.Operation.VariableDefinitionByNameAndOperation(v.operationDefinition, v.Operation.VariableValueNameBytes(value.Ref))
			if !exists {
				break
			}
			variableTypeRef := v.Operation.VariableDefinitions[variableDefinition].Type
			variable.SetJsonValueType(v.Operation, v.Definition, variableTypeRef)
			variable.RenderAsPlainValue = true
			if fieldConfig, ok := v.fieldConfigs[config.fieldRef]; ok {
				if argumentConfig := fieldConfig.Arguments.ForName(argumentName); argumentConfig != nil {
					switch argumentConfig.RenderConfig {
					case RenderArgumentAsArrayCSV:
						variable.RenderAsArrayCSV = true
						variable.RenderAsPlainValue = false
					case RenderArgumentDefault:
						variable.RenderAsPlainValue = true
					case RenderArgumentAsGraphQLValue:
						variable.RenderAsGraphQLValue = true
						variable.RenderAsPlainValue = false
					}
				}
			}
			variableName, _ = variables.AddVariable(variable)
		case "request":
			if len(path) != 2 {
				break
			}
			switch path[0] {
			case "headers":
				key := path[1]
				variableName, _ = variables.AddVariable(&resolve.HeaderVariable{
					Path: []string{key},
				})
			}
		}
		return variableName
	})
}
func (v *Visitor) configureSubscription(config objectFetchConfiguration) {
	subscription := config.planner.ConfigureSubscription()
	config.trigger.Variables = subscription.Variables
	config.trigger.Source = subscription.DataSource
	v.resolveInputTemplates(config, &subscription.Input, &config.trigger.Variables)
	config.trigger.Input = []byte(subscription.Input)
}
func (v *Visitor) configureObjectFetch(config objectFetchConfiguration) {
	if config.object == nil {
		return
	}
	fetchConfig := config.planner.ConfigureFetch()
	fetch := v.configureSingleFetch(config, fetchConfig)
	v.resolveInputTemplates(config, &fetch.Input, &fetch.Variables)
	if config.object.Fetch == nil {
		config.object.Fetch = fetch
		return
	}
	switch existing := config.object.Fetch.(type) {
	case *resolve.SingleFetch:
		copyOfExisting := *existing
		parallel := &resolve.ParallelFetch{
			Fetches: []*resolve.SingleFetch{&copyOfExisting, fetch},
		}
		config.object.Fetch = parallel
	case *resolve.ParallelFetch:
		existing.Fetches = append(existing.Fetches, fetch)
	}
}
func (v *Visitor) configureSingleFetch(internal objectFetchConfiguration, external FetchConfiguration) *resolve.SingleFetch {
	dataSourceType := reflect.TypeOf(external.DataSource).String()
	dataSourceType = strings.TrimPrefix(dataSourceType, "*")
	return &resolve.SingleFetch{
		BufferId:              internal.bufferID,
		Input:                 external.Input,
		DataSource:            external.DataSource,
		Variables:             external.Variables,
		DisallowSingleFlight:  external.DisallowSingleFlight,
		DataSourceIdentifier:  []byte(dataSourceType),
		ProcessResponseConfig: external.ProcessResponseConfig,
	}
}
type Kind int
const (
	SynchronousResponseKind Kind = iota + 1
	StreamingResponseKind
	SubscriptionResponseKind
)
type Plan interface {
	PlanKind() Kind
	SetFlushInterval(interval int64)
}
type SynchronousResponsePlan struct {
	Response      *resolve.GraphQLResponse
	FlushInterval int64
}
func (s *SynchronousResponsePlan) SetFlushInterval(interval int64) {
	s.FlushInterval = interval
}
func (_ *SynchronousResponsePlan) PlanKind() Kind {
	return SynchronousResponseKind
}
type StreamingResponsePlan struct {
	Response      *resolve.GraphQLStreamingResponse
	FlushInterval int64
}
func (s *StreamingResponsePlan) SetFlushInterval(interval int64) {
	s.FlushInterval = interval
}
func (_ *StreamingResponsePlan) PlanKind() Kind {
	return StreamingResponseKind
}
type SubscriptionResponsePlan struct {
	Response      *resolve.GraphQLSubscription
	FlushInterval int64
}
func (s *SubscriptionResponsePlan) SetFlushInterval(interval int64) {
	s.FlushInterval = interval
}
func (_ *SubscriptionResponsePlan) PlanKind() Kind {
	return SubscriptionResponseKind
}
type DataSourcePlanningBehavior struct {
	MergeAliasedRootNodes bool
	OverrideFieldPathFromAlias bool
}
type DataSourcePlanner interface {
	Register(visitor *Visitor, customConfiguration json.RawMessage, isNested bool) error
	ConfigureFetch() FetchConfiguration
	ConfigureSubscription() SubscriptionConfiguration
	DataSourcePlanningBehavior() DataSourcePlanningBehavior
	DownstreamResponseFieldAlias(downstreamFieldRef int) (alias string, exists bool)
}
type SubscriptionConfiguration struct {
	Input      string
	Variables  resolve.Variables
	DataSource resolve.SubscriptionDataSource
}
type FetchConfiguration struct {
	Input                 string
	Variables             resolve.Variables
	DataSource            resolve.DataSource
	DisallowSingleFlight  bool
	ProcessResponseConfig resolve.ProcessResponseConfig
}
type configurationVisitor struct {
	operationName         string
	operation, definition *ast.Document
	walker                *astvisitor.Walker
	config                Configuration
	planners              []plannerConfiguration
	fetches               []objectFetchConfiguration
	currentBufferId       int
	fieldBuffers          map[int]int
	ctx context.Context
}
type plannerConfiguration struct {
	parentPath              string
	planner                 DataSourcePlanner
	paths                   []pathConfiguration
	dataSourceConfiguration DataSourceConfiguration
	bufferID                int
}