package codegen import ( "fmt" "go/types" "os" "sort" "strings" "github.com/vektah/gqlgen/neelance/schema" "golang.org/x/tools/go/loader" ) func (cfg *Config) buildInterfaces(types NamedTypes, prog *loader.Program) []*Interface { var interfaces []*Interface for _, typ := range cfg.schema.Types { switch typ := typ.(type) { case *schema.Union, *schema.Interface: interfaces = append(interfaces, cfg.buildInterface(types, typ, prog)) default: continue } } sort.Slice(interfaces, func(i, j int) bool { return strings.Compare(interfaces[i].GQLType, interfaces[j].GQLType) == -1 }) return interfaces } func (cfg *Config) buildInterface(types NamedTypes, typ schema.NamedType, prog *loader.Program) *Interface { switch typ := typ.(type) { case *schema.Union: i := &Interface{NamedType: types[typ.TypeName()]} for _, implementor := range typ.PossibleTypes { t := types[implementor.TypeName()] i.Implementors = append(i.Implementors, InterfaceImplementor{ NamedType: t, ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog), }) } return i case *schema.Interface: i := &Interface{NamedType: types[typ.TypeName()]} for _, implementor := range typ.PossibleTypes { t := types[implementor.TypeName()] i.Implementors = append(i.Implementors, InterfaceImplementor{ NamedType: t, ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog), }) } return i default: panic(fmt.Errorf("unknown interface %#v", typ)) } } func (cfg *Config) isValueReceiver(intf *NamedType, implementor *NamedType, prog *loader.Program) bool { interfaceType, err := findGoInterface(prog, intf.Package, intf.GoType) if interfaceType == nil || err != nil { return true } implementorType, err := findGoNamedType(prog, implementor.Package, implementor.GoType) if implementorType == nil || err != nil { return true } for i := 0; i < interfaceType.NumMethods(); i++ { intfMethod := interfaceType.Method(i) implMethod := findMethod(implementorType, intfMethod.Name()) if implMethod == nil { fmt.Fprintf(os.Stderr, "missing method %s on %s\n", intfMethod.Name(), implementor.GoType) return false } sig := implMethod.Type().(*types.Signature) if _, isPtr := sig.Recv().Type().(*types.Pointer); isPtr { return false } } return true }