func IdentityShouldRemoveCycle(_ TypeDefinition, _ interface{}) (bool, error) {
	return false, nil
}
IdentityShouldRemoveCycle is the default cycle removal behavior. It preserves all cycles unmodified.
package astmodel
import (
	"fmt"
	"github.com/pkg/errors"
)
var typeWalkerRemoveType = MakeTypeName(LocalPackageReference{}, "TypeWalkerRemoveProperty")
type TypeWalker struct {
	allTypes Types
	visitor  TypeVisitor
	MakeContext func(it TypeName, ctx interface{}) (interface{}, error)
	AfterVisit func(original TypeDefinition, updated TypeDefinition, ctx interface{}) (TypeDefinition, error)
	ShouldRemoveCycle func(def TypeDefinition, ctx interface{}) (bool, error)
	state                   typeWalkerState
	originalVisitTypeName   func(this *TypeVisitor, it TypeName, ctx interface{}) (Type, error)
	originalVisitObjectType func(this *TypeVisitor, it *ObjectType, ctx interface{}) (Type, error)
}
type typeWalkerState struct {
	result     Types
	processing map[TypeName]struct{}
}
func NewTypeWalker(allTypes Types, visitor TypeVisitor) *TypeWalker {
	typeWalker := TypeWalker{
		allTypes:                allTypes,
		originalVisitTypeName:   visitor.visitTypeName,
		originalVisitObjectType: visitor.visitObjectType,
		AfterVisit:              IdentityAfterVisit,
		MakeContext:             IdentityMakeContext,
		ShouldRemoveCycle:       IdentityShouldRemoveCycle,
	}
	visitor.visitTypeName = typeWalker.visitTypeName
	visitor.visitObjectType = typeWalker.visitObjectType
	typeWalker.visitor = visitor
	return &typeWalker
}
func (t *TypeWalker) visitTypeName(this *TypeVisitor, it TypeName, ctx interface{}) (Type, error) {
	updatedCtx, err := t.MakeContext(it, ctx)
	if err != nil {
		return nil, errors.Wrapf(err, "MakeContext failed for name %q", it)
	}
	visitedTypeName, err := t.originalVisitTypeName(this, it, updatedCtx)
	if err != nil {
		return nil, errors.Wrapf(err, "visitTypeName failed for name %q", it)
	}
	it, ok := visitedTypeName.(TypeName)
	if !ok {
		panic(fmt.Sprintf("TypeWalker visitor visitTypeName must return a TypeName, instead returned %T", visitedTypeName))
	}
	def, ok := t.allTypes[it]
	if !ok {
		return nil, errors.Errorf("couldn't find type %q", it)
	}
	if _, ok := t.state.processing[def.Name()]; ok {
		remove, cycleErr := t.ShouldRemoveCycle(def, updatedCtx)
		if cycleErr != nil {
			return nil, errors.Wrapf(cycleErr, "ShouldRemoveCycle failed for def %q", def.Name())
		}
		if remove {
			return typeWalkerRemoveType, nil
		} else {
			return def.Name(), nil
		}
	}
	t.state.processing[def.Name()] = struct{}{}
	updatedType, err := this.Visit(def.Type(), updatedCtx)
	if err != nil {
		return nil, errors.Wrapf(err, "error visiting type %q", def.Name())
	}
	updatedDef, err := t.AfterVisit(def, def.WithType(updatedType), updatedCtx)
	if err != nil {
		return nil, errors.Wrapf(err, "AfterVisit failed for def %q", def.Name())
	}
	delete(t.state.processing, def.Name())
	err = t.state.result.AddAllowDuplicates(updatedDef)
	if err != nil {
		return nil, err
	}
	return updatedDef.Name(), nil
}
func (t *TypeWalker) visitObjectType(this *TypeVisitor, it *ObjectType, ctx interface{}) (Type, error) {
	result, err := t.originalVisitObjectType(this, it, ctx)
	if err != nil {
		return nil, err
	}
	ot, ok := result.(*ObjectType)
	if !ok {
		panic(fmt.Sprintf("TypeWalker visitor visitObjectType must return a *ObjectType, instead returned %T", result))
	}
	for _, prop := range ot.Properties() {
		shouldRemove := shouldRemove(prop.PropertyType())
		if shouldRemove {
			ot = ot.WithoutProperty(prop.PropertyName())
		}
	}
	return ot, nil
}
func (t *TypeWalker) Walk(def TypeDefinition) (Types, error) {
	t.state = typeWalkerState{
		result:     make(Types),
		processing: make(map[TypeName]struct{}),
	}
	_, err := t.visitor.Visit(def.Name(), nil)
	if err != nil {
		return nil, err
	}
	return t.state.result, nil
}
func IdentityAfterVisit(_ TypeDefinition, updated TypeDefinition, _ interface{}) (TypeDefinition, error) {
	return updated, nil
}
func IdentityMakeContext(_ TypeName, ctx interface{}) (interface{}, error) {
	return ctx, nil
}