func defaultTypeHandlers() map[SchemaType]TypeHandler {
	return map[SchemaType]TypeHandler{
		Array:  arrayHandler,
		OneOf:  oneOfHandler,
		AnyOf:  anyOfHandler,
		AllOf:  allOfHandler,
		Ref:    refHandler,
		Object: objectHandler,
		Enum:   enumHandler,
		String: stringHandler,
		Int:    intHandler,
		Number: numberHandler,
		Bool:   boolHandler,
	}
}
defaultTypeHandlers will create a default map of JSONType to AST transformers
package jsonast
import (
	"context"
	"fmt"
	"math"
	"math/big"
	"net/url"
	"strings"
	"github.com/devigned/tab"
	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"github.com/Azure/k8s-infra/hack/generator/pkg/astmodel"
	"github.com/Azure/k8s-infra/hack/generator/pkg/config"
)
type (
	TypeHandler func(ctx context.Context, scanner *SchemaScanner, schema Schema) (astmodel.Type, error)
	UnknownSchemaError struct {
		Schema  Schema
		Filters []string
	}
	SchemaScanner struct {
		definitions   map[astmodel.TypeName]*astmodel.TypeDefinition
		TypeHandlers  map[SchemaType]TypeHandler
		configuration *config.Configuration
		idFactory     astmodel.IdentifierFactory
	}
)
func (scanner *SchemaScanner) findTypeDefinition(name astmodel.TypeName) (*astmodel.TypeDefinition, bool) {
	result, ok := scanner.definitions[name]
	return result, ok
}
func (scanner *SchemaScanner) addTypeDefinition(def astmodel.TypeDefinition) {
	if existing, ok := scanner.definitions[def.Name()]; ok && existing != nil {
		panic(fmt.Sprintf("overwriting existing definition for %v", def.Name()))
	}
	scanner.definitions[def.Name()] = &def
}
func (scanner *SchemaScanner) addEmptyTypeDefinition(name astmodel.TypeName) {
	scanner.definitions[name] = nil
}
func (scanner *SchemaScanner) removeTypeDefinition(name astmodel.TypeName) {
	delete(scanner.definitions, name)
}
func (use *UnknownSchemaError) Error() string {
	if use.Schema == nil || use.Schema.url() == nil {
		return "unable to determine schema type for nil schema or one without a URL"
	}
	return fmt.Sprintf("unable to determine the schema type for %s", use.Schema.url().String())
}
func NewSchemaScanner(idFactory astmodel.IdentifierFactory, configuration *config.Configuration) *SchemaScanner {
	return &SchemaScanner{
		definitions:   make(map[astmodel.TypeName]*astmodel.TypeDefinition),
		TypeHandlers:  defaultTypeHandlers(),
		configuration: configuration,
		idFactory:     idFactory,
	}
}
func (scanner *SchemaScanner) AddTypeHandler(schemaType SchemaType, handler TypeHandler) {
	scanner.TypeHandlers[schemaType] = handler
}
func (scanner *SchemaScanner) RunHandler(ctx context.Context, schemaType SchemaType, schema Schema) (astmodel.Type, error) {
	if ctx.Err() != nil {  
		return nil, ctx.Err()
	}
	handler := scanner.TypeHandlers[schemaType]
	return handler(ctx, scanner, schema)
}
func (scanner *SchemaScanner) RunHandlerForSchema(ctx context.Context, schema Schema) (astmodel.Type, error) {
	schemaType, err := getSubSchemaType(schema)
	if err != nil {
		return nil, err
	}
	return scanner.RunHandler(ctx, schemaType, schema)
}
func (scanner *SchemaScanner) GenerateDefinitionsFromDeploymentTemplate(ctx context.Context, root Schema) (astmodel.Types, error) {
	ctx, span := tab.StartSpan(ctx, "GenerateDefinitionsFromDeploymentTemplate")
	defer span.End()
	resourcesProp, ok := root.properties()["resources"]
	if !ok {
		return nil, errors.Errorf("unable to find 'resources' property in deployment template")
	}
	resourcesTypes, err := scanner.RunHandlerForSchema(ctx, resourcesProp)
	if err != nil {
		return nil, err
	}
	resourcesArray, ok := resourcesTypes.(*astmodel.ArrayType)
	if !ok {
		return nil, errors.Errorf("expected 'resources' property to be an array")
	}
	resourcesOneOf, ok := resourcesArray.Element().(*astmodel.OneOfType)
	if !ok {
		return nil, errors.Errorf("expected 'resources' property to be an array containing oneOf")
	}
	err = resourcesOneOf.Types().ForEachError(func(oneType astmodel.Type, _ int) error {
		allOf, ok := oneType.(*astmodel.AllOfType)
		if !ok {
			return errors.Errorf("unexpected resource shape: not an allOf")
		}
		var resourceRef astmodel.TypeName
		var objectBase astmodel.TypeName
		found := 0
		allOf.Types().ForEach(func(t astmodel.Type, _ int) {
			if typeName, match := t.(astmodel.TypeName); match {
				if !strings.Contains(strings.ToLower(typeName.Name()), "resourcebase") {
					resourceRef = typeName
				} else {
					objectBase = typeName
				}
				found++
			}
		})
		if found != 2 {
			return errors.Errorf("unexpected resource shape: expected a ref to base and ref to object")
		}
		resourceDef, ok := scanner.findTypeDefinition(resourceRef)
		if !ok {
			return errors.Errorf("unable to resolve resource definition for %v", resourceRef)
		}
		resourceType, ok := resourceDef.Type().(*astmodel.ResourceType)
		if !ok {
			return errors.Errorf("resource reference %v in deployment template did not resolve to resource type", resourceRef)
		}
		specType := astmodel.BuildAllOfType(objectBase, resourceType.SpecType())
		scanner.removeTypeDefinition(resourceRef)
		scanner.addTypeDefinition(resourceDef.WithType(astmodel.NewAzureResourceType(specType, nil, resourceDef.Name())))
		return nil
	})
	if err != nil {
		return nil, err
	}
	return scanner.Definitions(), nil
}
func (scanner *SchemaScanner) GenerateAllDefinitions(ctx context.Context, schema Schema) (astmodel.Types, error) {
	title := schema.title()
	if title == nil {
		return nil, errors.New("given schema has no title")
	}
	rootName := *title
	rootURL := schema.url()
	rootGroup, err := groupOf(rootURL)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to extract group for schema")
	}
	rootVersion := versionOf(rootURL)
	rootPackage := scanner.configuration.MakeLocalPackageReference(
		scanner.idFactory.CreateGroupName(rootGroup),
		astmodel.CreateLocalPackageNameFromVersion(rootVersion))
	rootTypeName := astmodel.MakeTypeName(rootPackage, rootName)
	_, err = generateDefinitionsFor(ctx, scanner, rootTypeName, schema)
	if err != nil {
		return nil, err
	}
	return scanner.Definitions(), nil
}
func (scanner *SchemaScanner) Definitions() astmodel.Types {
	defs := make(astmodel.Types)
	for defName, def := range scanner.definitions {
		if def == nil {
			panic(fmt.Sprintf("%v was nil", defName))
		}
		if defName != def.Name() {
			panic(fmt.Sprintf("mismatched typenames: %v != %v", defName, def.Name()))
		}
		defs.Add(*def)
	}
	return defs
}