git.sr.ht/~sircmpwn/gqlgen@v0.0.0-20200522192042-c84d29a1c940/codegen/interface.go (about) 1 package codegen 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/pkg/errors" 8 "github.com/vektah/gqlparser/v2/ast" 9 10 "git.sr.ht/~sircmpwn/gqlgen/codegen/config" 11 ) 12 13 type Interface struct { 14 *ast.Definition 15 Type types.Type 16 Implementors []InterfaceImplementor 17 InTypemap bool 18 } 19 20 type InterfaceImplementor struct { 21 *ast.Definition 22 23 Type types.Type 24 TakeRef bool 25 } 26 27 func (b *builder) buildInterface(typ *ast.Definition) (*Interface, error) { 28 obj, err := b.Binder.DefaultUserObject(typ.Name) 29 if err != nil { 30 panic(err) 31 } 32 33 i := &Interface{ 34 Definition: typ, 35 Type: obj, 36 InTypemap: b.Config.Models.UserDefined(typ.Name), 37 } 38 39 interfaceType, err := findGoInterface(i.Type) 40 if interfaceType == nil || err != nil { 41 return nil, fmt.Errorf("%s is not an interface", i.Type) 42 } 43 44 for _, implementor := range b.Schema.GetPossibleTypes(typ) { 45 obj, err := b.Binder.DefaultUserObject(implementor.Name) 46 if err != nil { 47 return nil, fmt.Errorf("%s has no backing go type", implementor.Name) 48 } 49 50 implementorType, err := findGoNamedType(obj) 51 if err != nil { 52 return nil, errors.Wrapf(err, "can not find backing go type %s", obj.String()) 53 } else if implementorType == nil { 54 return nil, fmt.Errorf("can not find backing go type %s", obj.String()) 55 } 56 57 anyValid := false 58 59 // first check if the value receiver can be nil, eg can we type switch on case Thing: 60 if types.Implements(implementorType, interfaceType) { 61 i.Implementors = append(i.Implementors, InterfaceImplementor{ 62 Definition: implementor, 63 Type: obj, 64 TakeRef: !types.IsInterface(obj), 65 }) 66 anyValid = true 67 } 68 69 // then check if the pointer receiver can be nil, eg can we type switch on case *Thing: 70 if types.Implements(types.NewPointer(implementorType), interfaceType) { 71 i.Implementors = append(i.Implementors, InterfaceImplementor{ 72 Definition: implementor, 73 Type: types.NewPointer(obj), 74 }) 75 anyValid = true 76 } 77 78 if !anyValid { 79 return nil, fmt.Errorf("%s does not satisfy the interface %s", implementorType.String(), i.Type.String()) 80 } 81 } 82 83 return i, nil 84 } 85 86 func (i *InterfaceImplementor) CanBeNil() bool { 87 return config.IsNilable(i.Type) 88 }