// 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) }