func assignPrimitiveTypeFromEnumType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	if _, srcOpt := AsOptionalType(sourceEndpoint.Type()); srcOpt {
		return nil
	}
	if _, dstOpt := AsOptionalType(destinationEndpoint.Type()); dstOpt {
		return nil
	}
	_, srcType, ok := conversionContext.ResolveType(sourceEndpoint.Type())
	if !ok {
		return nil
	}
	srcEnum, srcIsEnum := AsEnumType(srcType)
	if !srcIsEnum {
		return nil
	}
	dstPrimitive, ok := AsPrimitiveType(destinationEndpoint.Type())
	if !ok {
		return nil
	}
	if !srcEnum.baseType.Equals(dstPrimitive) {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, ctx *CodeGenerationContext) []dst.Stmt {
		return writer(astbuilder.CallFunc(dstPrimitive.Name(), reader))
	}
}
assignPrimitiveTypeFromEnumType will generate a conversion from an enumeration if the
destination has the underlying base type of the enumeration and neither source nor destination
is optional

<local> = <baseType>(<source>)
<destination> = <enum>(<local>)
package astmodel
import (
	"go/token"
	"github.com/Azure/k8s-infra/hack/generator/pkg/astbuilder"
	"github.com/dave/dst"
	"github.com/pkg/errors"
)
type StorageTypeConversion func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt
type StorageTypeConversionFactory func(
	source *StorageConversionEndpoint,
	destination *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion
var typeConversionFactories []StorageTypeConversionFactory
func init() {
	typeConversionFactories = []StorageTypeConversionFactory{
		assignPrimitiveTypeFromPrimitiveType,
		assignAliasedPrimitiveTypeFromAliasedPrimitiveType,
		assignArrayFromArray,
		assignMapFromMap,
		assignEnumTypeFromEnumType,
		assignPrimitiveTypeFromEnumType,
		assignObjectTypeFromObjectType,
		assignKnownReferenceFromKnownReference,
		assignFromOptionalType,  
		assignToOptionalType,
		assignToEnumerationType,
		assignFromAliasedPrimitiveType,
		assignToAliasedPrimitiveType,
	}
}
func createTypeConversion(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) (StorageTypeConversion, error) {
	for _, f := range typeConversionFactories {
		result := f(sourceEndpoint, destinationEndpoint, conversionContext)
		if result != nil {
			return result, nil
		}
	}
	err := errors.Errorf(
		"no conversion found to assign %q from %q",
		destinationEndpoint.name,
		sourceEndpoint.name)
	return nil, err
}
func assignToOptionalType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	destinationOptional, destinationIsOptional := AsOptionalType(destinationEndpoint.Type())
	if !destinationIsOptional {
		return nil
	}
	unwrappedEndpoint := destinationEndpoint.WithType(destinationOptional.element)
	conversion, _ := createTypeConversion(sourceEndpoint, unwrappedEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	local := destinationEndpoint.CreateLocal("", "Temp")
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		addrOfWriter := func(expr dst.Expr) []dst.Stmt {
			if _, ok := expr.(*dst.Ident); ok {
				return writer(astbuilder.AddrOf(expr))
			}
			assignment := astbuilder.SimpleAssignment(
				dst.NewIdent(local),
				token.DEFINE,
				expr)
			writing := writer(astbuilder.AddrOf(dst.NewIdent(local)))
			return astbuilder.Statements(assignment, writing)
		}
		return conversion(reader, addrOfWriter, generationContext)
	}
}
func assignFromOptionalType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	sourceOptional, sourceIsOptional := AsOptionalType(sourceEndpoint.Type())
	if !sourceIsOptional {
		return nil
	}
	unwrappedEndpoint := sourceEndpoint.WithType(sourceOptional.element)
	conversion, _ := createTypeConversion(unwrappedEndpoint, destinationEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	local := sourceEndpoint.CreateLocal("", "Read")
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		var cacheOriginal dst.Stmt
		var actualReader dst.Expr
		switch reader.(type) {
		case *dst.Ident, *dst.SelectorExpr:
			cacheOriginal = nil
			actualReader = reader
		default:
			cacheOriginal = astbuilder.SimpleAssignment(
				dst.NewIdent(local),
				token.DEFINE,
				reader)
			actualReader = dst.NewIdent(local)
		}
		checkForNil := astbuilder.NotEqual(actualReader, dst.NewIdent("nil"))
		writeActualValue := conversion(
			astbuilder.Dereference(actualReader),
			writer,
			generationContext)
		writeZeroValue := writer(
			destinationEndpoint.Type().AsZero(conversionContext.types, generationContext))
		stmt := &dst.IfStmt{
			Cond: checkForNil,
			Body: astbuilder.StatementBlock(writeActualValue...),
			Else: astbuilder.StatementBlock(writeZeroValue...),
		}
		return astbuilder.Statements(cacheOriginal, stmt)
	}
}
func assignToEnumerationType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	_, dstIsOpt := AsOptionalType(destinationEndpoint.Type())
	if dstIsOpt {
		return nil
	}
	dstName, dstType, ok := conversionContext.ResolveType(destinationEndpoint.Type())
	if !ok {
		return nil
	}
	dstEnum, ok := AsEnumType(dstType)
	if !ok {
		return nil
	}
	dstEp := destinationEndpoint.WithType(dstEnum.baseType)
	conversion, _ := createTypeConversion(sourceEndpoint, dstEp, conversionContext)
	if conversion == nil {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		convertingWriter := func(expr dst.Expr) []dst.Stmt {
			cast := &dst.CallExpr{
				Fun:  dstName.AsType(generationContext),
				Args: []dst.Expr{expr},
			}
			return writer(cast)
		}
		return conversion(
			reader,
			convertingWriter,
			generationContext)
	}
}
func assignPrimitiveTypeFromPrimitiveType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	_ *StorageConversionContext) StorageTypeConversion {
	if _, sourceIsOptional := AsOptionalType(sourceEndpoint.Type()); sourceIsOptional {
		return nil
	}
	if _, destinationIsOptional := AsOptionalType(destinationEndpoint.Type()); destinationIsOptional {
		return nil
	}
	sourcePrimitive, sourceIsPrimitive := AsPrimitiveType(sourceEndpoint.Type())
	if !sourceIsPrimitive {
		return nil
	}
	destinationPrimitive, destinationIsPrimitive := AsPrimitiveType(destinationEndpoint.Type())
	if !destinationIsPrimitive {
		return nil
	}
	if !sourcePrimitive.Equals(destinationPrimitive) {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		return writer(reader)
	}
}
func assignAliasedPrimitiveTypeFromAliasedPrimitiveType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	if _, sourceIsOptional := AsOptionalType(sourceEndpoint.Type()); sourceIsOptional {
		return nil
	}
	if _, destinationIsOptional := AsOptionalType(destinationEndpoint.Type()); destinationIsOptional {
		return nil
	}
	_, sourceType, ok := conversionContext.ResolveType(sourceEndpoint.Type())
	if !ok {
		return nil
	}
	sourcePrimitive, sourceIsPrimitive := AsPrimitiveType(sourceType)
	if !sourceIsPrimitive {
		return nil
	}
	destinationName, destinationType, ok := conversionContext.ResolveType(destinationEndpoint.Type())
	if !ok {
		return nil
	}
	destinationPrimitive, destinationIsPrimitive := AsPrimitiveType(destinationType)
	if !destinationIsPrimitive {
		return nil
	}
	if !sourcePrimitive.Equals(destinationPrimitive) {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		return writer(&dst.CallExpr{
			Fun:  destinationName.AsType(generationContext),
			Args: []dst.Expr{reader},
		})
	}
}
func assignFromAliasedPrimitiveType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	if _, sourceIsOptional := AsOptionalType(sourceEndpoint.Type()); sourceIsOptional {
		return nil
	}
	_, sourceType, ok := conversionContext.ResolveType(sourceEndpoint.Type())
	if !ok {
		return nil
	}
	sourcePrimitive, sourceIsPrimitive := AsPrimitiveType(sourceType)
	if !sourceIsPrimitive {
		return nil
	}
	primitiveEndpoint := sourceEndpoint.WithType(sourcePrimitive)
	conversion, _ := createTypeConversion(primitiveEndpoint, destinationEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		actualReader := &dst.CallExpr{
			Fun:  sourcePrimitive.AsType(generationContext),
			Args: []dst.Expr{reader},
		}
		return conversion(actualReader, writer, generationContext)
	}
}
func assignToAliasedPrimitiveType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	if _, destinationIsOptional := AsOptionalType(destinationEndpoint.Type()); destinationIsOptional {
		return nil
	}
	destinationName, destinationType, ok := conversionContext.ResolveType(destinationEndpoint.Type())
	if !ok {
		return nil
	}
	destinationPrimitive, destinationIsPrimitive := AsPrimitiveType(destinationType)
	if !destinationIsPrimitive {
		return nil
	}
	primitiveEndpoint := sourceEndpoint.WithType(destinationPrimitive)
	conversion, _ := createTypeConversion(sourceEndpoint, primitiveEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		actualWriter := func(expr dst.Expr) []dst.Stmt {
			castToAlias := &dst.CallExpr{
				Fun:  destinationName.AsType(generationContext),
				Args: []dst.Expr{expr},
			}
			return writer(castToAlias)
		}
		return conversion(reader, actualWriter, generationContext)
	}
}
func assignArrayFromArray(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	sourceArray, sourceIsArray := AsArrayType(sourceEndpoint.Type())
	if !sourceIsArray {
		return nil
	}
	destinationArray, destinationIsArray := AsArrayType(destinationEndpoint.Type())
	if !destinationIsArray {
		return nil
	}
	unwrappedSourceEndpoint := sourceEndpoint.WithType(sourceArray.element)
	unwrappedDestinationEndpoint := destinationEndpoint.WithType(destinationArray.element)
	conversion, _ := createTypeConversion(unwrappedSourceEndpoint, unwrappedDestinationEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		itemId := sourceEndpoint.CreateLocal("Item")
		indexId := sourceEndpoint.CreateLocal("Index")
		tempId := sourceEndpoint.CreateLocal("List")
		declaration := astbuilder.SimpleAssignment(
			dst.NewIdent(tempId),
			token.DEFINE,
			astbuilder.MakeList(destinationArray.AsType(generationContext), astbuilder.CallFunc("len", reader)))
		writeToElement := func(expr dst.Expr) []dst.Stmt {
			return []dst.Stmt{
				astbuilder.SimpleAssignment(
					&dst.IndexExpr{
						X:     dst.NewIdent(tempId),
						Index: dst.NewIdent(indexId),
					},
					token.ASSIGN,
					expr),
			}
		}
		avoidAliasing := astbuilder.SimpleAssignment(dst.NewIdent(itemId), token.DEFINE, dst.NewIdent(itemId))
		avoidAliasing.Decs.Start.Append("// Shadow the loop variable to avoid aliasing")
		avoidAliasing.Decs.Before = dst.NewLine
		loopBody := astbuilder.Statements(
			avoidAliasing,
			conversion(dst.NewIdent(itemId), writeToElement, generationContext))
		assign := writer(dst.NewIdent(tempId))
		loop := astbuilder.IterateOverListWithIndex(indexId, itemId, reader, loopBody...)
		return astbuilder.Statements(declaration, loop, assign)
	}
}
func assignMapFromMap(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	sourceMap, sourceIsMap := AsMapType(sourceEndpoint.Type())
	if !sourceIsMap {
		return nil
	}
	destinationMap, destinationIsMap := AsMapType(destinationEndpoint.Type())
	if !destinationIsMap {
		return nil
	}
	if !sourceMap.key.Equals(destinationMap.key) {
		return nil
	}
	unwrappedSourceEndpoint := sourceEndpoint.WithType(sourceMap.value)
	unwrappedDestinationEndpoint := destinationEndpoint.WithType(destinationMap.value)
	conversion, _ := createTypeConversion(unwrappedSourceEndpoint, unwrappedDestinationEndpoint, conversionContext)
	if conversion == nil {
		return nil
	}
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, generationContext *CodeGenerationContext) []dst.Stmt {
		itemId := sourceEndpoint.CreateLocal("Value")
		keyId := sourceEndpoint.CreateLocal("Key")
		tempId := sourceEndpoint.CreateLocal("Map")
		declaration := astbuilder.SimpleAssignment(
			dst.NewIdent(tempId),
			token.DEFINE,
			astbuilder.MakeMap(destinationMap.key.AsType(generationContext), destinationMap.value.AsType(generationContext)))
		assignToItem := func(expr dst.Expr) []dst.Stmt {
			return []dst.Stmt{
				astbuilder.SimpleAssignment(
					&dst.IndexExpr{
						X:     dst.NewIdent(tempId),
						Index: dst.NewIdent(keyId),
					},
					token.ASSIGN,
					expr),
			}
		}
		avoidAliasing := astbuilder.SimpleAssignment(dst.NewIdent(itemId), token.DEFINE, dst.NewIdent(itemId))
		avoidAliasing.Decs.Start.Append("// Shadow the loop variable to avoid aliasing")
		avoidAliasing.Decs.Before = dst.NewLine
		loopBody := astbuilder.Statements(
			avoidAliasing,
			conversion(dst.NewIdent(itemId), assignToItem, generationContext))
		assign := writer(dst.NewIdent(tempId))
		loop := astbuilder.IterateOverMapWithValue(keyId, itemId, reader, loopBody...)
		return astbuilder.Statements(declaration, loop, assign)
	}
}
func assignEnumTypeFromEnumType(
	sourceEndpoint *StorageConversionEndpoint,
	destinationEndpoint *StorageConversionEndpoint,
	conversionContext *StorageConversionContext) StorageTypeConversion {
	if _, sourceIsOptional := AsOptionalType(sourceEndpoint.Type()); sourceIsOptional {
		return nil
	}
	if _, destinationIsOptional := AsOptionalType(destinationEndpoint.Type()); destinationIsOptional {
		return nil
	}
	_, sourceType, sourceFound := conversionContext.ResolveType(sourceEndpoint.Type())
	if !sourceFound {
		return nil
	}
	sourceEnum, sourceIsEnum := AsEnumType(sourceType)
	if !sourceIsEnum {
		return nil
	}
	destinationName, destinationType, destinationFound := conversionContext.ResolveType(destinationEndpoint.Type())
	if !destinationFound {
		return nil
	}
	destinationEnum, destinationIsEnum := AsEnumType(destinationType)
	if !destinationIsEnum {
		return nil
	}
	if !sourceEnum.baseType.Equals(destinationEnum.baseType) {
		return nil
	}
	local := destinationEndpoint.CreateLocal("", "As"+destinationName.Name(), "Value")
	return func(reader dst.Expr, writer func(dst.Expr) []dst.Stmt, ctx *CodeGenerationContext) []dst.Stmt {
		result := []dst.Stmt{
			astbuilder.SimpleAssignment(
				dst.NewIdent(local),
				token.DEFINE,
				astbuilder.CallFunc(destinationName.Name(), reader)),
		}
		result = append(result, writer(dst.NewIdent(local))...)
		return result
	}
}