github.com/HaswinVidanage/gqlgen@v0.8.1-0.20220609041233-69528c1bf712/plugin/modelgen/models.go (about)

     1  package modelgen
     2  
     3  import (
     4  	"go/types"
     5  	"sort"
     6  
     7  	"github.com/HaswinVidanage/gqlgen/internal/code"
     8  
     9  	"github.com/HaswinVidanage/gqlgen/codegen/config"
    10  	"github.com/HaswinVidanage/gqlgen/codegen/templates"
    11  	"github.com/HaswinVidanage/gqlgen/plugin"
    12  	"github.com/vektah/gqlparser/ast"
    13  )
    14  
    15  type ModelBuild struct {
    16  	PackageName string
    17  	Interfaces  []*Interface
    18  	Models      []*Object
    19  	Enums       []*Enum
    20  }
    21  
    22  type Interface struct {
    23  	Description string
    24  	Name        string
    25  }
    26  
    27  type Object struct {
    28  	Description string
    29  	Name        string
    30  	Fields      []*Field
    31  	Implements  []string
    32  }
    33  
    34  type Field struct {
    35  	Description string
    36  	Name        string
    37  	Type        types.Type
    38  	Tag         string
    39  }
    40  
    41  type Enum struct {
    42  	Description string
    43  	Name        string
    44  	Raw         string
    45  	Values      []*EnumValue
    46  }
    47  
    48  type EnumValue struct {
    49  	Description string
    50  	Name        string
    51  	Value       string
    52  }
    53  
    54  func New() plugin.Plugin {
    55  	return &Plugin{}
    56  }
    57  
    58  type Plugin struct{}
    59  
    60  var _ plugin.ConfigMutator = &Plugin{}
    61  
    62  func (m *Plugin) Name() string {
    63  	return "modelgen"
    64  }
    65  
    66  func (m *Plugin) MutateConfig(cfg *config.Config) error {
    67  	if err := cfg.Check(); err != nil {
    68  		return err
    69  	}
    70  
    71  	schema, _, err := cfg.LoadSchema()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	cfg.InjectBuiltins(schema)
    77  
    78  	binder, err := cfg.NewBinder(schema)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	b := &ModelBuild{
    84  		PackageName: cfg.Model.Package,
    85  	}
    86  
    87  	for _, schemaType := range schema.Types {
    88  		if cfg.Models.UserDefined(schemaType.Name) {
    89  			continue
    90  		}
    91  
    92  		switch schemaType.Kind {
    93  		case ast.Interface, ast.Union:
    94  			it := &Interface{
    95  				Description: schemaType.Description,
    96  				Name:        templates.ToGo(schemaType.Name),
    97  			}
    98  
    99  			b.Interfaces = append(b.Interfaces, it)
   100  		case ast.Object, ast.InputObject:
   101  			if schemaType == schema.Query || schemaType == schema.Mutation || schemaType == schema.Subscription {
   102  				continue
   103  			}
   104  			it := &Object{
   105  				Description: schemaType.Description,
   106  				Name:        templates.ToGo(schemaType.Name),
   107  			}
   108  
   109  			for _, implementor := range schema.GetImplements(schemaType) {
   110  				it.Implements = append(it.Implements, templates.ToGo(implementor.Name))
   111  			}
   112  
   113  			for _, field := range schemaType.Fields {
   114  				var typ types.Type
   115  
   116  				if cfg.Models.UserDefined(field.Type.Name()) {
   117  					pkg, typeName := code.PkgAndType(cfg.Models[field.Type.Name()].Model[0])
   118  					typ, err = binder.FindType(pkg, typeName)
   119  					if err != nil {
   120  						return err
   121  					}
   122  				} else {
   123  					// no user defined model, must reference another generated model
   124  					typ = types.NewNamed(
   125  						types.NewTypeName(0, cfg.Model.Pkg(), templates.ToGo(field.Type.Name()), nil),
   126  						nil,
   127  						nil,
   128  					)
   129  				}
   130  
   131  				name := field.Name
   132  				if nameOveride := cfg.Models[schemaType.Name].Fields[field.Name].FieldName; nameOveride != "" {
   133  					name = nameOveride
   134  				}
   135  
   136  				fd := schema.Types[field.Type.Name()]
   137  				it.Fields = append(it.Fields, &Field{
   138  					Name:        templates.ToGo(name),
   139  					Type:        binder.CopyModifiersFromAst(field.Type, fd.Kind != ast.Interface, typ),
   140  					Description: field.Description,
   141  					Tag:         `json:"` + field.Name + `"`,
   142  				})
   143  			}
   144  
   145  			b.Models = append(b.Models, it)
   146  		case ast.Enum:
   147  			it := &Enum{
   148  				Name:        templates.ToGo(schemaType.Name),
   149  				Raw:         schemaType.Name,
   150  				Description: schemaType.Description,
   151  			}
   152  
   153  			for _, v := range schemaType.EnumValues {
   154  				it.Values = append(it.Values, &EnumValue{
   155  					Name:        templates.ToGo(v.Name),
   156  					Value:       v.Name,
   157  					Description: v.Description,
   158  				})
   159  			}
   160  
   161  			b.Enums = append(b.Enums, it)
   162  		}
   163  	}
   164  
   165  	sort.Slice(b.Enums, func(i, j int) bool { return b.Enums[i].Name < b.Enums[j].Name })
   166  	sort.Slice(b.Models, func(i, j int) bool { return b.Models[i].Name < b.Models[j].Name })
   167  	sort.Slice(b.Interfaces, func(i, j int) bool { return b.Interfaces[i].Name < b.Interfaces[j].Name })
   168  
   169  	for _, it := range b.Enums {
   170  		cfg.Models.Add(it.Raw, cfg.Model.ImportPath()+"."+it.Name)
   171  	}
   172  	for _, it := range b.Models {
   173  		cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+it.Name)
   174  	}
   175  	for _, it := range b.Interfaces {
   176  		cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+it.Name)
   177  	}
   178  
   179  	if len(b.Models) == 0 && len(b.Enums) == 0 {
   180  		return nil
   181  	}
   182  
   183  	return templates.Render(templates.Options{
   184  		PackageName:     cfg.Model.Package,
   185  		Filename:        cfg.Model.Filename,
   186  		Data:            b,
   187  		GeneratedHeader: true,
   188  	})
   189  }