func (objectType *ObjectType) WithFunction(function Function) *ObjectType {
	result := objectType.copy()
	result.functions[function.Name()] = function
	return result
}
WithFunction creates a new ObjectType with a function (method) attached to it
package astmodel
import (
	"go/token"
	"sort"
	"strings"
	"github.com/Azure/k8s-infra/hack/generator/pkg/astbuilder"
	"github.com/dave/dst"
	"github.com/pkg/errors"
)
type ObjectType struct {
	embedded   map[TypeName]*PropertyDefinition
	properties map[PropertyName]*PropertyDefinition
	functions  map[string]Function
	testcases  map[string]TestCase
	InterfaceImplementer
}
var EmptyObjectType = NewObjectType()
var _ Type = &ObjectType{}
var _ PropertyContainer = &ObjectType{}
var _ FunctionContainer = &ObjectType{}
func NewObjectType() *ObjectType {
	return &ObjectType{
		embedded:             make(map[TypeName]*PropertyDefinition),
		properties:           make(map[PropertyName]*PropertyDefinition),
		functions:            make(map[string]Function),
		testcases:            make(map[string]TestCase),
		InterfaceImplementer: MakeInterfaceImplementer(),
	}
}
func (objectType *ObjectType) AsDeclarations(codeGenerationContext *CodeGenerationContext, declContext DeclarationContext) []dst.Decl {
	declaration := &dst.GenDecl{
		Decs: dst.GenDeclDecorations{
			NodeDecs: dst.NodeDecs{
				Before: dst.EmptyLine,
				After:  dst.EmptyLine,
			},
		},
		Tok: token.TYPE,
		Specs: []dst.Spec{
			&dst.TypeSpec{
				Name: dst.NewIdent(declContext.Name.Name()),
				Type: objectType.AsType(codeGenerationContext),
			},
		},
	}
	astbuilder.AddWrappedComments(&declaration.Decs.Start, declContext.Description, 200)
	AddValidationComments(&declaration.Decs.Start, declContext.Validations)
	result := []dst.Decl{declaration}
	result = append(result, objectType.InterfaceImplementer.AsDeclarations(codeGenerationContext, declContext.Name, nil)...)
	result = append(result, objectType.generateMethodDecls(codeGenerationContext, declContext.Name)...)
	return result
}
func (objectType *ObjectType) generateMethodDecls(codeGenerationContext *CodeGenerationContext, typeName TypeName) []dst.Decl {
	var result []dst.Decl
	for _, f := range objectType.Functions() {
		funcDef := f.AsFunc(codeGenerationContext, typeName)
		result = append(result, funcDef)
	}
	return result
}
func defineField(fieldName string, fieldType dst.Expr, tag string) *dst.Field {
	result := &dst.Field{
		Type: fieldType,
		Tag:  astbuilder.TextLiteral(tag),
	}
	if fieldName != "" {
		result.Names = []*dst.Ident{dst.NewIdent(fieldName)}
	}
	return result
}
func (objectType *ObjectType) Properties() []*PropertyDefinition {
	var result []*PropertyDefinition
	for _, property := range objectType.properties {
		result = append(result, property)
	}
	sort.Slice(result, func(left int, right int) bool {
		return result[left].propertyName < result[right].propertyName
	})
	return result
}
func (objectType *ObjectType) Property(name PropertyName) (*PropertyDefinition, bool) {
	prop, ok := objectType.properties[name]
	return prop, ok
}
func (objectType *ObjectType) EmbeddedProperties() []*PropertyDefinition {
	var result []*PropertyDefinition
	for _, embedded := range objectType.embedded {
		result = append(result, embedded)
	}
	sort.Slice(result, func(left int, right int) bool {
		lTypeName, err := extractEmbeddedTypeName(result[left].PropertyType())
		if err != nil {
			panic(err)
		}
		rTypeName, err := extractEmbeddedTypeName(result[right].PropertyType())
		if err != nil {
			panic(err)
		}
		return lTypeName.Name() < rTypeName.Name()
	})
	return result
}
func (objectType *ObjectType) Functions() []Function {
	var functions []Function
	for _, f := range objectType.functions {
		functions = append(functions, f)
	}
	sort.Slice(functions, func(i int, j int) bool {
		return functions[i].Name() < functions[j].Name()
	})
	return functions
}
func (objectType *ObjectType) HasFunctionWithName(name string) bool {
	_, ok := objectType.functions[name]
	return ok
}
func (objectType *ObjectType) AsType(codeGenerationContext *CodeGenerationContext) dst.Expr {
	var fields []*dst.Field
	for _, f := range objectType.EmbeddedProperties() {
		fields = append(fields, f.AsField(codeGenerationContext))
	}
	for _, f := range objectType.Properties() {
		fields = append(fields, f.AsField(codeGenerationContext))
	}
	if len(fields) > 0 {
		fields[0].Decs.Before = dst.NewLine
	}
	return &dst.StructType{
		Fields: &dst.FieldList{
			List: fields,
		},
	}
}
func (objectType *ObjectType) AsZero(_ Types, _ *CodeGenerationContext) dst.Expr {
	panic("cannot create a zero value for an object type")
}
func (objectType *ObjectType) RequiredPackageReferences() *PackageReferenceSet {
	result := NewPackageReferenceSet()
	for _, property := range objectType.embedded {
		propertyType := property.PropertyType()
		result.Merge(propertyType.RequiredPackageReferences())
	}
	for _, property := range objectType.properties {
		propertyType := property.PropertyType()
		result.Merge(propertyType.RequiredPackageReferences())
	}
	for _, function := range objectType.functions {
		result.Merge(function.RequiredPackageReferences())
	}
	result.Merge(objectType.InterfaceImplementer.RequiredPackageReferences())
	return result
}
func (objectType *ObjectType) References() TypeNameSet {
	results := NewTypeNameSet()
	for _, property := range objectType.properties {
		results = results.AddAll(property.PropertyType().References())
	}
	for _, property := range objectType.embedded {
		results = results.AddAll(property.PropertyType().References())
	}
	return results
}
func (objectType *ObjectType) Equals(t Type) bool {
	if objectType == t {
		return true
	}
	other, ok := t.(*ObjectType)
	if !ok {
		return false
	}
	if len(objectType.embedded) != len(other.embedded) {
		return false
	}
	for n, f := range other.embedded {
		ourProperty, ok := objectType.embedded[n]
		if !ok {
			return false
		}
		if !ourProperty.Equals(f) {
			return false
		}
	}
	if len(objectType.properties) != len(other.properties) {
		return false
	}
	for n, f := range other.properties {
		ourProperty, ok := objectType.properties[n]
		if !ok {
			return false
		}
		if !ourProperty.Equals(f) {
			return false
		}
	}
	if len(objectType.functions) != len(other.functions) {
		return false
	}
	for name, function := range other.functions {
		ourFunction, ok := objectType.functions[name]
		if !ok {
			return false
		}
		if !ourFunction.Equals(function) {
			return false
		}
	}
	if len(objectType.testcases) != len(other.testcases) {
		return false
	}
	for name, testcase := range other.testcases {
		ourCase, ok := objectType.testcases[name]
		if !ok {
			return false
		}
		if !ourCase.Equals(testcase) {
			return false
		}
	}
	if len(objectType.testcases) != len(other.testcases) {
		return false
	}
	for name, testcase := range other.testcases {
		ourCase, ok := objectType.testcases[name]
		if !ok {
			return false
		}
		if !ourCase.Equals(testcase) {
			return false
		}
	}
	return objectType.InterfaceImplementer.Equals(other.InterfaceImplementer)
}
func (objectType *ObjectType) WithProperty(property *PropertyDefinition) *ObjectType {
	result := objectType.copy()
	result.properties[property.propertyName] = property
	return result
}
func (objectType *ObjectType) WithProperties(properties ...*PropertyDefinition) *ObjectType {
	result := objectType.copy()
	for _, f := range properties {
		result.properties[f.propertyName] = f
	}
	return result
}
func (objectType *ObjectType) WithEmbeddedProperties(properties ...*PropertyDefinition) (*ObjectType, error) {
	result := objectType.copy()
	for _, p := range properties {
		err := objectType.checkEmbeddedProperty(p)
		if err != nil {
			return objectType, err
		}
		typeName, err := extractEmbeddedTypeName(p.PropertyType())
		if err != nil {
			return objectType, err
		}
		result.embedded[typeName] = p
	}
	return result, nil
}
func (objectType *ObjectType) WithoutProperties() *ObjectType {
	result := objectType.copy()
	result.properties = make(map[PropertyName]*PropertyDefinition)
	return result
}
func (objectType *ObjectType) WithoutProperty(name PropertyName) *ObjectType {
	result := objectType.copy()
	delete(result.properties, name)
	return result
}
func (objectType *ObjectType) WithoutEmbeddedProperty(name TypeName) *ObjectType {
	result := objectType.copy()
	delete(result.embedded, name)
	return result
}
func (objectType *ObjectType) WithoutEmbeddedProperties() *ObjectType {
	result := objectType.copy()
	result.embedded = make(map[TypeName]*PropertyDefinition)
	return result
}
func (objectType *ObjectType) checkEmbeddedProperty(property *PropertyDefinition) error {
	if property.PropertyName() != "" {
		return errors.Errorf("embedded property name must be empty, was: %s", property.PropertyName())
	}
	return nil
}
func (objectType *ObjectType) WithEmbeddedProperty(property *PropertyDefinition) (*ObjectType, error) {
	err := objectType.checkEmbeddedProperty(property)
	if err != nil {
		return objectType, err
	}
	typeName, err := extractEmbeddedTypeName(property.PropertyType())
	if err != nil {
		return objectType, err
	}
	result := objectType.copy()
	result.embedded[typeName] = property
	return result, nil
}