package codegen
import (
"fmt"
"go/build"
"go/types"
"os"
"github.com/pkg/errors"
"golang.org/x/tools/go/loader"
)
type Build struct {
PackageName string
Objects Objects
Inputs Objects
Interfaces []*Interface
QueryRoot *Object
MutationRoot *Object
SubscriptionRoot *Object
SchemaRaw map[string]string
SchemaFilename SchemaFilenames
Directives []*Directive
}
type ModelBuild struct {
PackageName string
Models []Model
Enums []Enum
}
type ResolverBuild struct {
PackageName string
ResolverType string
Objects Objects
ResolverFound bool
}
type ServerBuild struct {
PackageName string
ExecPackageName string
ResolverPackageName string
}
// Create a list of models that need to be generated
func (cfg *Config) models() (*ModelBuild, error) {
namedTypes := cfg.buildNamedTypes()
progLoader := cfg.newLoaderWithoutErrors()
prog, err := progLoader.Load()
if err != nil {
return nil, errors.Wrap(err, "loading failed")
}
cfg.bindTypes(namedTypes, cfg.Model.Dir(), prog)
models, err := cfg.buildModels(namedTypes, prog)
if err != nil {
return nil, err
}
return &ModelBuild{
PackageName: cfg.Model.Package,
Models: models,
Enums: cfg.buildEnums(namedTypes),
}, nil
}
// bind a schema together with some code to generate a Build
func (cfg *Config) resolver() (*ResolverBuild, error) {
progLoader := cfg.newLoaderWithoutErrors()
progLoader.Import(cfg.Resolver.ImportPath())
prog, err := progLoader.Load()
if err != nil {
return nil, err
}
destDir := cfg.Resolver.Dir()
namedTypes := cfg.buildNamedTypes()
cfg.bindTypes(namedTypes, destDir, prog)
objects, err := cfg.buildObjects(namedTypes, prog)
if err != nil {
return nil, err
}
def, _ := findGoType(prog, cfg.Resolver.ImportPath(), cfg.Resolver.Type)
resolverFound := def != nil
return &ResolverBuild{
PackageName: cfg.Resolver.Package,
Objects: objects,
ResolverType: cfg.Resolver.Type,
ResolverFound: resolverFound,
}, nil
}
func (cfg *Config) server(destDir string) *ServerBuild {
return &ServerBuild{
PackageName: cfg.Resolver.Package,
ExecPackageName: cfg.Exec.ImportPath(),
ResolverPackageName: cfg.Resolver.ImportPath(),
}
}
// bind a schema together with some code to generate a Build
func (cfg *Config) bind() (*Build, error) {
namedTypes := cfg.buildNamedTypes()
progLoader := cfg.newLoaderWithoutErrors()
prog, err := progLoader.Load()
if err != nil {
return nil, errors.Wrap(err, "loading failed")
}
cfg.bindTypes(namedTypes, cfg.Exec.Dir(), prog)
objects, err := cfg.buildObjects(namedTypes, prog)
if err != nil {
return nil, err
}
inputs, err := cfg.buildInputs(namedTypes, prog)
if err != nil {
return nil, err
}
directives, err := cfg.buildDirectives(namedTypes)
if err != nil {
return nil, err
}
b := &Build{
PackageName: cfg.Exec.Package,
Objects: objects,
Interfaces: cfg.buildInterfaces(namedTypes, prog),
Inputs: inputs,
SchemaRaw: cfg.SchemaStr,
SchemaFilename: cfg.SchemaFilename,
Directives: directives,
}
if cfg.schema.Query != nil {
b.QueryRoot = b.Objects.ByName(cfg.schema.Query.Name)
} else {
return b, fmt.Errorf("query entry point missing")
}
if cfg.schema.Mutation != nil {
b.MutationRoot = b.Objects.ByName(cfg.schema.Mutation.Name)
}
if cfg.schema.Subscription != nil {
b.SubscriptionRoot = b.Objects.ByName(cfg.schema.Subscription.Name)
}
return b, nil
}
func (cfg *Config) validate() error {
progLoader := cfg.newLoaderWithErrors()
_, err := progLoader.Load()
return err
}
func (cfg *Config) newLoaderWithErrors() loader.Config {
conf := loader.Config{}
for _, pkg := range cfg.Models.referencedPackages() {
conf.Import(pkg)
}
return conf
}
func (cfg *Config) newLoaderWithoutErrors() loader.Config {
conf := cfg.newLoaderWithErrors()
conf.AllowErrors = true
conf.TypeChecker = types.Config{
Error: func(e error) {},
}
return conf
}
func resolvePkg(pkgName string) (string, error) {
cwd, _ := os.Getwd()
pkg, err := build.Default.Import(pkgName, cwd, build.FindOnly)
if err != nil {
return "", err
}
return pkg.ImportPath, nil
}