Add new package bindings option (#169)

Add new package bindings option

This commit adds list of packages for which genqlient should automatically
generate bindings; it's equivalent to adding all the exported types in the
package to `bindings` explicitly. This can be useful when you're both a client
and a server of the same schema and want to share types. We don't recommend
doing things that way, but the feature isn't too invasive and may be useful for
other purposes.

Co-authored-by: Ben Kraft <[email protected]>
This commit is contained in:
NuVivo314
2022-06-17 11:42:49 -07:00
committed by GitHub
co-authored by Ben Kraft
parent 1f44dc6db3
commit 093054ef32
5 changed files with 127 additions and 2 deletions
+46
View File
@@ -2,10 +2,12 @@ package generate
import (
_ "embed"
"fmt"
"go/token"
"os"
"path/filepath"
"golang.org/x/tools/go/packages"
"gopkg.in/yaml.v2"
)
@@ -26,6 +28,7 @@ type Config struct {
ContextType string `yaml:"context_type"`
ClientGetter string `yaml:"client_getter"`
Bindings map[string]*TypeBinding `yaml:"bindings"`
PackageBindings []*PackageBinding `yaml:"package_bindings"`
Optional string `yaml:"optional"`
StructReferences bool `yaml:"use_struct_references"`
Extensions bool `yaml:"use_extensions"`
@@ -52,6 +55,13 @@ type TypeBinding struct {
Unmarshaler string `yaml:"unmarshaler"`
}
// A PackageBinding represents a Go package for which genqlient will
// automatically generate TypeBindings, and is documented further at:
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
type PackageBinding struct {
Package string `yaml:"package"`
}
// pathJoin is like filepath.Join but 1) it only takes two argsuments,
// and b) if the second argument is an absolute path the first argument
// is ignored (similar to how python's os.path.join() works).
@@ -98,6 +108,42 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error {
c.Package = base
}
if len(c.PackageBindings) > 0 {
for _, binding := range c.PackageBindings {
mode := packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes
pkgs, err := packages.Load(&packages.Config{
Mode: mode,
}, binding.Package)
if err != nil {
return err
}
if c.Bindings == nil {
c.Bindings = map[string]*TypeBinding{}
}
for _, pkg := range pkgs {
p := pkg.Types
if p == nil || p.Scope() == nil {
return errorf(nil, "unable to bind package %s: no types found", binding.Package)
}
for _, typ := range p.Scope().Names() {
if token.IsExported(typ) {
// Check if type is manual bindings
_, exist := c.Bindings[typ]
if !exist {
pathType := fmt.Sprintf("%s.%s", p.Path(), typ)
c.Bindings[typ] = &TypeBinding{
Type: pathType,
}
}
}
}
}
}
}
return nil
}