Skip to content

Commit

Permalink
Merge pull request #4 from vektah/cleanup-type-binding
Browse files Browse the repository at this point in the history
Cleanup schema binding code
  • Loading branch information
vektah authored Feb 13, 2018
2 parents 030954a + c89a877 commit 51292db
Show file tree
Hide file tree
Showing 16 changed files with 684 additions and 607 deletions.
90 changes: 90 additions & 0 deletions codegen/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package codegen

import (
"go/build"
"os"
"path/filepath"

"github.com/vektah/gqlgen/neelance/schema"
"golang.org/x/tools/go/loader"
)

type Build struct {
PackageName string
Objects Objects
Interfaces []*Interface
Imports Imports
QueryRoot *Object
MutationRoot *Object
SchemaRaw string
}

// Bind a schema together with some code to generate a Build
func Bind(schema *schema.Schema, userTypes map[string]string, destDir string) (*Build, error) {
namedTypes := buildNamedTypes(schema, userTypes)

imports := buildImports(namedTypes, destDir)
prog, err := loadProgram(imports)
if err != nil {
return nil, err
}

b := &Build{
PackageName: filepath.Base(destDir),
Objects: buildObjects(namedTypes, schema, prog),
Interfaces: buildInterfaces(namedTypes, schema),
Imports: imports,
}

if qr, ok := schema.EntryPoints["query"]; ok {
b.QueryRoot = b.Objects.ByName(qr.TypeName())
}

if mr, ok := schema.EntryPoints["mutation"]; ok {
b.MutationRoot = b.Objects.ByName(mr.TypeName())
}

// Poke a few magic methods into query
q := b.Objects.ByName(b.QueryRoot.GQLType)
q.Fields = append(q.Fields, Field{
Type: &Type{namedTypes["__Schema"], []string{modPtr}},
GQLName: "__schema",
NoErr: true,
GoMethodName: "ec.introspectSchema",
Object: q,
})
q.Fields = append(q.Fields, Field{
Type: &Type{namedTypes["__Type"], []string{modPtr}},
GQLName: "__type",
NoErr: true,
GoMethodName: "ec.introspectType",
Args: []FieldArgument{
{GQLName: "name", Type: &Type{namedTypes["String"], []string{}}},
},
Object: q,
})

return b, nil
}

func loadProgram(imports Imports) (*loader.Program, error) {
var conf loader.Config
for _, imp := range imports {
if imp.Package != "" {
conf.Import(imp.Package)
}
}

return conf.Load()
}

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
}
16 changes: 16 additions & 0 deletions codegen/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package codegen

import (
"strconv"
)

type Import struct {
Name string
Package string
}

type Imports []*Import

func (i *Import) Write() string {
return i.Name + " " + strconv.Quote(i.Package)
}
77 changes: 77 additions & 0 deletions codegen/import_build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package codegen

import (
"path/filepath"
"strconv"
"strings"
)

func buildImports(types NamedTypes, destDir string) Imports {
imports := Imports{
{"context", "context"},
{"fmt", "fmt"},
{"io", "io"},
{"strconv", "strconv"},
{"time", "time"},
{"reflect", "reflect"},
{"strings", "strings"},
{"sync", "sync"},
{"mapstructure", "github.com/mitchellh/mapstructure"},
{"introspection", "github.com/vektah/gqlgen/neelance/introspection"},
{"errors", "github.com/vektah/gqlgen/neelance/errors"},
{"query", "github.com/vektah/gqlgen/neelance/query"},
{"schema", "github.com/vektah/gqlgen/neelance/schema"},
{"validation", "github.com/vektah/gqlgen/neelance/validation"},
{"jsonw", "github.com/vektah/gqlgen/jsonw"},
}

for _, t := range types {
if t.Package == "" {
continue
}

if existing := imports.findByPkg(t.Package); existing != nil {
t.Import = existing
continue
}

localName := ""
if !strings.HasSuffix(destDir, t.Package) {
localName = filepath.Base(t.Package)
i := 0
for imp := imports.findByName(localName); imp != nil && imp.Package != t.Package; localName = filepath.Base(t.Package) + strconv.Itoa(i) {
i++
if i > 10 {
panic("too many collisions")
}
}
}

imp := &Import{
Name: localName,
Package: t.Package,
}
t.Import = imp
imports = append(imports, imp)
}

return imports
}

func (i Imports) findByPkg(pkg string) *Import {
for _, imp := range i {
if imp.Package == pkg {
return imp
}
}
return nil
}

func (i Imports) findByName(name string) *Import {
for _, imp := range i {
if imp.Name == name {
return imp
}
}
return nil
}
7 changes: 7 additions & 0 deletions codegen/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package codegen

type Interface struct {
*NamedType

Implementors []*NamedType
}
40 changes: 40 additions & 0 deletions codegen/interface_build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package codegen

import (
"sort"
"strings"

"github.com/vektah/gqlgen/neelance/schema"
)

func buildInterfaces(types NamedTypes, s *schema.Schema) []*Interface {
var interfaces []*Interface
for _, typ := range s.Types {
switch typ := typ.(type) {

case *schema.Union:
i := &Interface{NamedType: types[typ.TypeName()]}

for _, implementor := range typ.PossibleTypes {
i.Implementors = append(i.Implementors, types[implementor.TypeName()])
}

interfaces = append(interfaces, i)

case *schema.Interface:
i := &Interface{NamedType: types[typ.TypeName()]}

for _, implementor := range typ.PossibleTypes {
i.Implementors = append(i.Implementors, types[implementor.TypeName()])
}

interfaces = append(interfaces, i)
}
}

sort.Slice(interfaces, func(i, j int) bool {
return strings.Compare(interfaces[i].GQLType, interfaces[j].GQLType) == -1
})

return interfaces
}
Loading

0 comments on commit 51292db

Please sign in to comment.