Files
ent/entc/plugin/plugin.go
facebook-github-bot 267e3c15bd Initial commit
fbshipit-source-id: c79a38536e3c128dce1b2948615b72ec9779ed22
2019-06-16 04:37:51 -07:00

110 lines
2.8 KiB
Go

// Package plugin provides a way to extend entc via plugins.
// Plugin can be either a Go plugin that is loaded and executed
// by entc runtime or a standalone program.
package plugin
import (
"os"
"path/filepath"
"plugin"
"fbc/ent/entc/gen"
"fbc/ent/entc/internal/build"
"github.com/pkg/errors"
)
// Symbol is the expected symbol name in the provided plugin.
// The plugin.Symbol need to be a type Generator. For example:
//
// package main
//
// var Gen = GeneratorFunc(func(graph *gen.Graph) error {
// return nil
// })
//
const Symbol = "Gen"
// Generator is the interface that wrap the Gen method executed by entc.
type Generator interface {
Gen(*gen.Graph) error
}
// The GeneratorFunc type is an adapter to allow the use of ordinary functions as Generator.
// If f is a function with the appropriate signature, GeneratorFunc(f) is a Generator that calls f.
type GeneratorFunc func(*gen.Graph) error
// Gen calls f(g).
func (f GeneratorFunc) Gen(g *gen.Graph) error { return f(g) }
// LoadGraph loads the given schema package from the given path
// and construct a *gen.Graph. The path can be either a package
// path (e.g github.com/a8m/x) or a filepath.
//
// This function used to create a standalone plugin programs that
// want to interact with the ent schemas. An example for usage:
//
// package main
//
// import (
// "log"
//
// "fbc/ent/entc/plugin"
// )
//
// func main() {
// graph, err := plugin.LoadGraph("./ent/schema")
// if err != nil {
// log.Fatal(err)
// }
// for _, node := range graph.Nodes {
// log.Println(node.Name)
// }
// }
//
func LoadGraph(path string) (*gen.Graph, error) {
plg, err := (&build.Config{Path: path}).Build()
if err != nil {
return nil, err
}
defer os.Remove(plg.Path)
schemas, err := plg.Load()
if err != nil {
return nil, err
}
return gen.NewGraph(gen.Config{Schema: plg.PkgPath, Package: filepath.Dir(plg.PkgPath)}, schemas...)
}
// MustLoadGraph is like LoadGraph but panics if LoadGraph returns an error.
// It simplifies safe initialization of global variables holding a *gen.Graph.
func MustLoadGraph(path string) *gen.Graph {
graph, err := LoadGraph(path)
if err != nil {
panic(err)
}
return graph
}
// Exec loads and executes the provided plugin with
// the provided *gen/Graph.
//
// It returns an error if the plugin is invalid or
// it's not fulfilling the entc/plugin interface.
func Exec(path string, graph *gen.Graph) error {
plg, err := plugin.Open(path)
if err != nil {
return errors.WithMessagef(err, "open plugin %s", path)
}
sym, err := plg.Lookup(Symbol)
if err != nil {
return errors.WithMessagef(err, "find symbol (%q) in plugin", Symbol)
}
g, ok := sym.(Generator)
if !ok {
return errors.Errorf("exported symbol %q does not implement the entc/plugin.Generator", Symbol)
}
return g.Gen(graph)
}