github.com/operandinc/gqlgen@v0.16.1/codegen/interface.go (about)

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