func removeUnusedDecls(p *pkg.Package) *pkg.Package {
	ts := make(typeSet, len(p.TypeDecls))
	reachDecls(p, func(_ typeContext, i *pkg.IdentType) bool {
		ret := !ts.Seen(*i)
		ts.Mark(*i)
		return ret
	})
	swept := make([]pkg.TypeDecl, 0, len(p.TypeDecls))
	for _, d := range p.TypeDecls {
		if ts.Seen(pkg.IdentType{Name: d.Name}) {
			swept = append(swept, d)
		}
	}
	p.TypeDecls = swept
	sweptIter := make([]pkg.Iter, 0, len(p.Iters))
	for _, i := range p.Iters {
		if ts.Seen(pkg.IdentType{Name: i.Name}) {
			sweptIter = append(sweptIter, i)
		}
	}
	p.Iters = sweptIter
	return p
}
removeUnusedDecls removes any type declarations that aren't referenced elsewhere
in the generated code.
package mutator
import (
	"github.com/jbowes/oag/pkg"
)
func Mutate(p *pkg.Package) *pkg.Package {
	mutators := []func(*pkg.Package) *pkg.Package{
		combineErrorsWithDefault,
		inlinePrimitiveTypes,
		inlineResponseStructs,
		hoistEmbeddedStuctFields,
		removeUnusedDecls,
	}
	for _, m := range mutators {
		p = m(p)
	}
	return p
}
func combineErrorsWithDefault(p *pkg.Package) *pkg.Package {
	for _, c := range p.Clients {
		for _, m := range c.Methods {
			det, ok := m.Errors[-1]
			if !ok {
				continue
			}
			for code, et := range m.Errors {
				if code == -1 {
					continue
				}
				if det.Equal(et) {
					delete(m.Errors, code)
				}
			}
		}
	}
	return p
}
func inlinePrimitiveTypes(p *pkg.Package) *pkg.Package {
	for _, d := range p.TypeDecls {
		if _, ok := d.Type.(*pkg.StructType); ok {
			continue
		}
		p = replaceType(p, &pkg.IdentType{Name: d.Name}, d.Type)
	}
	return p
}
func replaceType(p *pkg.Package, old, new pkg.Type) *pkg.Package {
	return walkTypes(p, func(t pkg.Type, _ typeContext) pkg.Type {
		if old.Equal(t) {
			return new
		}
		return t
	})
}
func inlineResponseStructs(p *pkg.Package) *pkg.Package {
	ctxs := make(map[pkg.IdentType]struct {
		c typeContext
		n int
	}, len(p.TypeDecls))
	reachDecls(p, func(c typeContext, i *pkg.IdentType) bool {
		v := ctxs[*i]
		v.c |= c
		v.n++
		ctxs[*i] = v
		return true
	})
	for i, c := range ctxs {
		if c.c&methodParam > 0 {
			delete(ctxs, i)
			continue
		}
	}
	for _, d := range p.TypeDecls {
		di := pkg.IdentType{Name: d.Name}
		if pc, ok := ctxs[di]; ok {
			d.Type = recurseType(d.Type, decl, func(t pkg.Type, c typeContext) pkg.Type {
				if t == d.Type {
					return t
				}
				if c&embeddedStruct > 0 {
					return t
				}
				if i, ok := t.(*pkg.IdentType); ok {
					if cc, ok := ctxs[*i]; ok && pc.n >= cc.n {
						return resolve(p, i)
					}
				}
				return t
			})
		}
	}
	return p
}
func hoistEmbeddedStuctFields(p *pkg.Package) *pkg.Package {
	ctxs := make(map[pkg.IdentType]struct {
		c typeContext
		n int
	}, len(p.TypeDecls))
	reachDecls(p, func(c typeContext, i *pkg.IdentType) bool {
		v := ctxs[*i]
		v.c |= c
		v.n++
		ctxs[*i] = v
		return true
	})
	for _, d := range p.TypeDecls {
		di := pkg.IdentType{Name: d.Name}
		if pc, ok := ctxs[di]; ok {
			d.Type = recurseType(d.Type, decl, func(t pkg.Type, c typeContext) pkg.Type {
				st, ok := t.(*pkg.StructType)
				if !ok {
					return t
				}
				var nf []pkg.Field
				for i := range st.Fields {
					f := st.Fields[i]
					if f.ID != "" {
						nf = append(nf, f)
						continue
					}
					var embedded *pkg.StructType
					switch ft := f.Type.(type) {
					case *pkg.IdentType:
						if cc, ok := ctxs[*ft]; !ok || pc.n < cc.n {
							nf = append(nf, f)
							continue
						}
						resolved := resolve(p, ft)
						if rst, ok := resolved.(*pkg.StructType); ok {
							embedded = rst
						} else {
							nf = append(nf, f)
							continue
						}
					case *pkg.StructType:
						embedded = ft
					default:
						nf = append(nf, f)
						continue
					}
					for j := range embedded.Fields {
						nf = append(nf, embedded.Fields[j])
					}
				}
				st.Fields = nf
				return st
			})
		}
	}
	return p
}
type typeSet map[pkg.IdentType]struct{}
func (t typeSet) Mark(i pkg.IdentType) {
	t[i] = struct{}{}
}
func (t typeSet) Seen(i pkg.IdentType) bool {
	_, ok := t[i]
	return ok
}