github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/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  	"github.com/99designs/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  }