From 2c8b5a65b7950075f76075fa917a898bed971093 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Thu, 1 Aug 2019 05:02:46 -0700 Subject: [PATCH] entc: abandon plugins Summary: Go plugin is not a good solution for both internal and external usage. It's hard to manage and maintain matching versions (both Go and external libraries), and it does not support Windows. Reviewed By: alexsn Differential Revision: D16582217 fbshipit-source-id: 81876d2c6f30bbfc16ecf9e5000f0670f2e62484 --- edge/edge.go | 3 - edge/edge_test.go | 44 +- ent.go | 3 +- entc/cmd/entc/entc.go | 28 +- entc/gen/bindata.go | 54 +- entc/gen/graph.go | 65 +- entc/gen/graph_test.go | 127 ++-- entc/gen/template/meta.tmpl | 16 +- entc/gen/type.go | 59 +- entc/gen/type_test.go | 40 +- entc/integration/ent/file/file.go | 2 + entc/integration/ent/file_create.go | 11 +- entc/integration/ent/file_update.go | 16 + entc/integration/ent/group/group.go | 8 +- entc/integration/ent/groupinfo/groupinfo.go | 12 +- entc/integration/ent/schema/file.go | 3 + entc/integration/ent/user/user.go | 12 +- entc/integration/generate.go | 1 - entc/integration/plugin/README.md | 8 - entc/integration/plugin/ent/boring.go | 124 ---- entc/integration/plugin/ent/boring/boring.go | 18 - entc/integration/plugin/ent/boring/where.go | 194 ------ entc/integration/plugin/ent/boring_create.go | 89 --- entc/integration/plugin/ent/boring_delete.go | 87 --- entc/integration/plugin/ent/boring_query.go | 618 ------------------ entc/integration/plugin/ent/boring_update.go | 224 ------- entc/integration/plugin/ent/client.go | 99 --- entc/integration/plugin/ent/config.go | 51 -- entc/integration/plugin/ent/context.go | 20 - entc/integration/plugin/ent/ent.go | 329 ---------- entc/integration/plugin/ent/example_test.go | 40 -- .../integration/plugin/ent/migrate/migrate.go | 29 - entc/integration/plugin/ent/migrate/schema.go | 30 - .../plugin/ent/predicate/predicate.go | 27 - entc/integration/plugin/ent/schema/boring.go | 18 - entc/integration/plugin/ent/tx.go | 93 --- entc/integration/plugin/plugin_test.go | 38 -- entc/integration/plugin/testdata/printer.go | 16 - entc/internal/build/bindata.go | 235 ------- entc/internal/build/build_test.go | 40 -- entc/internal/build/template/build.tmpl | 18 - entc/internal/build/testdata/valid/schema.go | 18 - entc/load/bindata.go | 258 ++++++++ .../{internal/build/build.go => load/load.go} | 121 ++-- entc/load/load_test.go | 38 ++ entc/load/schema.go | 97 +++ entc/load/schema_test.go | 85 +++ entc/load/template/main.tmpl | 40 ++ .../build => load}/testdata/invalid/schema.go | 0 entc/load/testdata/valid/schema.go | 41 ++ entc/plugin/plugin.go | 111 ---- entc/plugin/plugin_test.go | 45 -- entc/plugin/testdata/invalid/invalid.go | 4 - entc/plugin/testdata/valid/valid.go | 17 - 54 files changed, 915 insertions(+), 2909 deletions(-) delete mode 100644 entc/integration/plugin/README.md delete mode 100644 entc/integration/plugin/ent/boring.go delete mode 100644 entc/integration/plugin/ent/boring/boring.go delete mode 100644 entc/integration/plugin/ent/boring/where.go delete mode 100644 entc/integration/plugin/ent/boring_create.go delete mode 100644 entc/integration/plugin/ent/boring_delete.go delete mode 100644 entc/integration/plugin/ent/boring_query.go delete mode 100644 entc/integration/plugin/ent/boring_update.go delete mode 100644 entc/integration/plugin/ent/client.go delete mode 100644 entc/integration/plugin/ent/config.go delete mode 100644 entc/integration/plugin/ent/context.go delete mode 100644 entc/integration/plugin/ent/ent.go delete mode 100644 entc/integration/plugin/ent/example_test.go delete mode 100644 entc/integration/plugin/ent/migrate/migrate.go delete mode 100644 entc/integration/plugin/ent/migrate/schema.go delete mode 100644 entc/integration/plugin/ent/predicate/predicate.go delete mode 100644 entc/integration/plugin/ent/schema/boring.go delete mode 100644 entc/integration/plugin/ent/tx.go delete mode 100644 entc/integration/plugin/plugin_test.go delete mode 100644 entc/integration/plugin/testdata/printer.go delete mode 100644 entc/internal/build/bindata.go delete mode 100644 entc/internal/build/build_test.go delete mode 100644 entc/internal/build/template/build.tmpl delete mode 100644 entc/internal/build/testdata/valid/schema.go create mode 100644 entc/load/bindata.go rename entc/{internal/build/build.go => load/load.go} (51%) create mode 100644 entc/load/load_test.go create mode 100644 entc/load/schema.go create mode 100644 entc/load/schema_test.go create mode 100644 entc/load/template/main.tmpl rename entc/{internal/build => load}/testdata/invalid/schema.go (100%) create mode 100644 entc/load/testdata/valid/schema.go delete mode 100644 entc/plugin/plugin.go delete mode 100644 entc/plugin/plugin_test.go delete mode 100644 entc/plugin/testdata/invalid/invalid.go delete mode 100644 entc/plugin/testdata/valid/valid.go diff --git a/edge/edge.go b/edge/edge.go index c68fa326f..8335d85fe 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -38,9 +38,6 @@ func (e Edge) IsUnique() bool { return e.unique } // AssocName returns the edge name. func (e Edge) Name() string { return e.name } -// IsAssoc returns is the edge is assoc type. -func (e Edge) IsAssoc() bool { return !e.inverse } - // IsInverse returns is the edge is inverse type. func (e Edge) IsInverse() bool { return e.inverse } diff --git a/edge/edge_test.go b/edge/edge_test.go index dee56bf2b..67b1d2656 100644 --- a/edge/edge_test.go +++ b/edge/edge_test.go @@ -13,45 +13,45 @@ func TestEdge(t *testing.T) { assert := assert.New(t) type User struct{ ent.Schema } e := edge.To("friends", User.Type).Required() - assert.True(e.IsAssoc()) + assert.False(e.IsInverse()) assert.Equal("User", e.Type()) assert.Equal("friends", e.Name()) assert.True(e.IsRequired()) type Node struct{ ent.Schema } e = edge.To("parent", Node.Type).Unique() - assert.True(e.IsAssoc()) + assert.False(e.IsInverse()) assert.True(e.IsUnique()) assert.Equal("Node", e.Type()) assert.Equal("parent", e.Name()) assert.False(e.IsRequired()) t.Log("m2m relation of the same type") - From := edge.To("following", User.Type).From("followers") - assert.False(From.IsAssoc()) - assert.True(From.IsInverse()) - assert.False(From.IsUnique()) - assert.Equal("followers", From.Name()) - assert.NotNil(From.Assoc()) - assert.Equal("following", From.Assoc().Name()) - assert.False(From.Assoc().IsUnique()) + from := edge.To("following", User.Type).From("followers") + + assert.True(from.IsInverse()) + assert.False(from.IsUnique()) + assert.Equal("followers", from.Name()) + assert.NotNil(from.Assoc()) + assert.Equal("following", from.Assoc().Name()) + assert.False(from.Assoc().IsUnique()) t.Log("o2m relation of the same type") - From = edge.To("following", User.Type).Unique().From("followers") - assert.False(From.IsUnique()) - assert.True(From.Assoc().IsUnique()) - From = edge.To("following", User.Type).From("followers").Unique() - assert.True(From.IsUnique()) - assert.False(From.Assoc().IsUnique()) + from = edge.To("following", User.Type).Unique().From("followers") + assert.False(from.IsUnique()) + assert.True(from.Assoc().IsUnique()) + from = edge.To("following", User.Type).From("followers").Unique() + assert.True(from.IsUnique()) + assert.False(from.Assoc().IsUnique()) t.Log("o2o relation of the same type") - From = edge.To("following", User.Type).Unique().From("followers").Unique() - assert.True(From.IsUnique()) - assert.True(From.Assoc().IsUnique()) + from = edge.To("following", User.Type).Unique().From("followers").Unique() + assert.True(from.IsUnique()) + assert.True(from.Assoc().IsUnique()) e = edge.To("user", User.Type).StructTag(`json:"user_name,omitempty"`) assert.Equal(`json:"user_name,omitempty"`, e.Tag()) - From = edge.To("following", User.Type).StructTag("following").From("followers").StructTag("followers") - assert.Equal("followers", From.Tag()) - assert.Equal("following", From.Assoc().Tag()) + from = edge.To("following", User.Type).StructTag("following").From("followers").StructTag("followers") + assert.Equal("followers", from.Tag()) + assert.Equal("following", from.Assoc().Tag()) } diff --git a/ent.go b/ent.go index 1284353b8..7b7dd5f3b 100644 --- a/ent.go +++ b/ent.go @@ -33,11 +33,10 @@ type ( Type() string Name() string RefName() string - IsAssoc() bool + Assoc() *edge.Edge IsUnique() bool IsInverse() bool IsRequired() bool - Assoc() *edge.Edge } ) diff --git a/entc/cmd/entc/entc.go b/entc/cmd/entc/entc.go index 7b1cbc622..fad830eea 100644 --- a/entc/cmd/entc/entc.go +++ b/entc/cmd/entc/entc.go @@ -11,7 +11,7 @@ import ( "unicode" "fbc/ent/entc/gen" - "fbc/ent/entc/plugin" + "fbc/ent/entc/load" "github.com/spf13/cobra" ) @@ -59,7 +59,7 @@ func main() { ), Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, path []string) { - graph, err := plugin.LoadGraph(path[0], gen.Config{}) + graph, err := loadGraph(path[0], gen.Config{}) failOnErr(err) graph.Describe(os.Stdout) }, @@ -68,7 +68,6 @@ func main() { var ( cfg gen.Config storage []string - plugins []string cmd = &cobra.Command{ Use: "generate [flags] path", Short: "generate go code for the schema directory", @@ -88,20 +87,14 @@ func main() { failOnErr(err) cfg.Storage = append(cfg.Storage, sr) } - graph, err := plugin.LoadGraph(path[0], cfg) + graph, err := loadGraph(path[0], cfg) failOnErr(err) failOnErr(graph.Gen()) - - // execute additional plugins. - for _, plg := range plugins { - failOnErr(plugin.Exec(plg, graph)) - } }, } ) cmd.Flags().StringVar(&cfg.Header, "header", "", "override codegen header") cmd.Flags().StringVar(&cfg.Target, "target", "", "target directory for codegen") - cmd.Flags().StringSliceVarP(&plugins, "plugin", "", nil, "specifies additional plugin to execute") cmd.Flags().StringSliceVarP(&storage, "storage", "", []string{"sql"}, "list of storage drivers to support") return cmd }(), @@ -109,6 +102,21 @@ func main() { cmd.Execute() } +// 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. +// +// The second argument is an optional config for the graph creation. +func loadGraph(path string, cfg gen.Config) (*gen.Graph, error) { + spec, err := (&load.Config{Path: path}).Load() + if err != nil { + return nil, err + } + cfg.Schema = spec.PkgPath + cfg.Package = filepath.Dir(spec.PkgPath) + return gen.NewGraph(cfg, spec.Schemas...) +} + // schema template for the "init" command. var tmpl = template.Must(template.New("schema"). Parse(`package schema diff --git a/entc/gen/bindata.go b/entc/gen/bindata.go index b40406a62..878993862 100644 --- a/entc/gen/bindata.go +++ b/entc/gen/bindata.go @@ -119,7 +119,7 @@ func templateBaseTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/base.tmpl", size: 5024, mode: os.FileMode(420), modTime: time.Unix(1564435014, 0)} + info := bindataFileInfo{name: "template/base.tmpl", size: 5024, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -139,7 +139,7 @@ func templateBuilderCreateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/create.tmpl", size: 2706, mode: os.FileMode(420), modTime: time.Unix(1564164430, 0)} + info := bindataFileInfo{name: "template/builder/create.tmpl", size: 2706, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -159,7 +159,7 @@ func templateBuilderDeleteTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/delete.tmpl", size: 1921, mode: os.FileMode(420), modTime: time.Unix(1564193299, 0)} + info := bindataFileInfo{name: "template/builder/delete.tmpl", size: 1921, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -179,7 +179,7 @@ func templateBuilderQueryTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/query.tmpl", size: 12221, mode: os.FileMode(420), modTime: time.Unix(1564244317, 0)} + info := bindataFileInfo{name: "template/builder/query.tmpl", size: 12221, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -199,7 +199,7 @@ func templateBuilderSetterTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/setter.tmpl", size: 2911, mode: os.FileMode(420), modTime: time.Unix(1555862705, 0)} + info := bindataFileInfo{name: "template/builder/setter.tmpl", size: 2911, mode: os.FileMode(420), modTime: time.Unix(1564496459, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -219,7 +219,7 @@ func templateBuilderUpdateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/update.tmpl", size: 7086, mode: os.FileMode(420), modTime: time.Unix(1564232317, 0)} + info := bindataFileInfo{name: "template/builder/update.tmpl", size: 7086, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -239,7 +239,7 @@ func templateClientTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/client.tmpl", size: 3958, mode: os.FileMode(420), modTime: time.Unix(1564245006, 0)} + info := bindataFileInfo{name: "template/client.tmpl", size: 3958, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -299,7 +299,7 @@ func templateDialectGremlinByTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/by.tmpl", size: 1679, mode: os.FileMode(420), modTime: time.Unix(1564430374, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/by.tmpl", size: 1679, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -339,7 +339,7 @@ func templateDialectGremlinDecodeTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/decode.tmpl", size: 1924, mode: os.FileMode(420), modTime: time.Unix(1564245606, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/decode.tmpl", size: 1924, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -379,7 +379,7 @@ func templateDialectGremlinErrorsTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/errors.tmpl", size: 1608, mode: os.FileMode(420), modTime: time.Unix(1564431021, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/errors.tmpl", size: 1608, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -419,7 +419,7 @@ func templateDialectGremlinMetaTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/meta.tmpl", size: 508, mode: os.FileMode(420), modTime: time.Unix(1564435028, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/meta.tmpl", size: 508, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -439,7 +439,7 @@ func templateDialectGremlinPredicateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/predicate.tmpl", size: 2640, mode: os.FileMode(420), modTime: time.Unix(1564400962, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/predicate.tmpl", size: 2640, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -459,7 +459,7 @@ func templateDialectGremlinQueryTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/query.tmpl", size: 3733, mode: os.FileMode(420), modTime: time.Unix(1564425528, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/query.tmpl", size: 3733, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -479,7 +479,7 @@ func templateDialectGremlinUpdateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/update.tmpl", size: 4741, mode: os.FileMode(420), modTime: time.Unix(1564232679, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/update.tmpl", size: 4741, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -499,7 +499,7 @@ func templateDialectSqlByTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/by.tmpl", size: 753, mode: os.FileMode(420), modTime: time.Unix(1564430114, 0)} + info := bindataFileInfo{name: "template/dialect/sql/by.tmpl", size: 753, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -539,7 +539,7 @@ func templateDialectSqlDecodeTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/decode.tmpl", size: 1991, mode: os.FileMode(420), modTime: time.Unix(1564245687, 0)} + info := bindataFileInfo{name: "template/dialect/sql/decode.tmpl", size: 1991, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -579,7 +579,7 @@ func templateDialectSqlErrorsTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/errors.tmpl", size: 771, mode: os.FileMode(420), modTime: time.Unix(1564430869, 0)} + info := bindataFileInfo{name: "template/dialect/sql/errors.tmpl", size: 771, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -619,7 +619,7 @@ func templateDialectSqlMetaTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/meta.tmpl", size: 1586, mode: os.FileMode(420), modTime: time.Unix(1564391955, 0)} + info := bindataFileInfo{name: "template/dialect/sql/meta.tmpl", size: 1586, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -639,7 +639,7 @@ func templateDialectSqlPredicateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/predicate.tmpl", size: 4051, mode: os.FileMode(420), modTime: time.Unix(1564423643, 0)} + info := bindataFileInfo{name: "template/dialect/sql/predicate.tmpl", size: 4051, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -659,7 +659,7 @@ func templateDialectSqlQueryTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/query.tmpl", size: 6186, mode: os.FileMode(420), modTime: time.Unix(1564425510, 0)} + info := bindataFileInfo{name: "template/dialect/sql/query.tmpl", size: 6186, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -699,7 +699,7 @@ func templateEntTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/ent.tmpl", size: 3372, mode: os.FileMode(420), modTime: time.Unix(1564245932, 0)} + info := bindataFileInfo{name: "template/ent.tmpl", size: 3372, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -719,7 +719,7 @@ func templateExampleTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/example.tmpl", size: 2206, mode: os.FileMode(420), modTime: time.Unix(1564434704, 0)} + info := bindataFileInfo{name: "template/example.tmpl", size: 2206, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -759,12 +759,12 @@ func templateImportTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/import.tmpl", size: 506, mode: os.FileMode(420), modTime: time.Unix(1564432120, 0)} + info := bindataFileInfo{name: "template/import.tmpl", size: 506, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _templateMetaTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x54\x5d\x6b\xdb\x3c\x14\xbe\x76\x7e\xc5\x79\xfd\xba\x60\x43\xea\x6c\xb7\x1d\xb9\x5a\x19\x2b\x8c\x32\x58\xd9\x4d\x29\x45\xb1\x8f\x1a\xb5\x8a\xec\x49\x4a\xda\x60\xf4\xdf\x87\xbe\x1c\x39\x4d\xd7\x5e\xd9\xd2\xf9\x78\x9e\xe7\x7c\x68\x18\xa0\x45\xca\x04\x42\xbe\x41\x4d\x72\x30\x66\x36\x1b\x06\xd0\xb8\xe9\x39\xd1\x08\xf9\x1a\x49\x8b\x32\x87\xa2\xfe\x49\x9a\x27\xf2\x80\xaf\x5d\xd8\xa6\xef\xa4\xce\xa1\x70\xa6\xa6\x13\x4a\x43\x39\xcb\x16\x0b\xf8\x41\x56\xc8\x61\xdd\xf1\x56\x81\x5e\x23\x28\x2d\x99\x78\x00\xee\xae\x5b\x14\x9d\xb6\x47\x6b\x19\x06\xe0\xdd\x33\x4a\x28\xea\x6b\xb2\xb1\x20\xa0\xf7\x3d\x02\x13\xce\xdc\x12\x4d\x56\x44\x61\x3d\xcb\x7c\xce\x25\xe4\xc3\x00\x45\xed\x4f\xc6\xe4\x0e\xcf\x5d\x5d\x5d\xd6\x5f\x2d\x07\x22\xb4\x4d\xf3\x0a\x7d\x82\xcb\x5a\xa0\x0c\x79\x7b\x02\xe8\x54\x32\x0f\xab\x04\x79\x42\x6f\x0c\x64\x73\xe7\x2e\x89\x78\x40\x28\xee\xe7\x50\x50\xb8\x58\x42\x51\x7f\xb3\xb9\x15\x9c\x1b\x63\xb3\x79\x24\x6b\xa0\x87\xac\xe7\xc6\xcc\xb2\x48\xde\x7b\xbc\xcb\xfa\x50\x2d\x3a\x96\x6b\x87\x52\xe3\x0b\xf4\xb2\xeb\x51\xea\xfd\x09\x41\xd9\x04\x61\x22\x85\x26\x42\xac\x1b\xa3\xf6\xee\x3b\x51\x97\x48\xc9\x96\x5b\xed\xce\x70\x0e\x45\x1b\x6e\xbc\x8c\x60\x3f\xad\xa6\x1d\x83\x13\x3d\xf1\x72\x47\xf8\x16\x81\x76\x32\x2a\x4a\xb4\x38\x92\x91\x71\x92\x65\x64\x76\xb3\xef\xb1\xbe\xde\x6e\x50\xb2\x26\x18\xc2\x6d\x38\xa1\x08\x22\x87\x01\x7a\xc9\x84\xa6\x90\x9f\xfd\xbf\xcb\x13\xce\x51\x93\xf3\x75\xbc\x8f\xfe\x93\x76\x2a\xdd\x49\x3b\xfb\xae\xa9\xbf\xc2\x21\xc6\x17\x7a\xd3\x73\x6b\x8a\x40\x2d\x23\x1c\x1b\xbd\x38\x53\x0b\xbb\x55\x8b\x26\x54\x47\xe5\x87\x4c\x31\xf8\x65\xdc\x23\x9f\xc6\x2d\x51\x36\x2a\x98\x55\x6e\xd9\x3e\x42\xe5\x23\x4c\x76\x44\x32\xb2\xe2\x78\xcc\xc4\x17\x76\x4d\xd4\xcd\x94\xcd\x47\x59\x1e\xfe\x66\xa1\x47\x76\x78\x7e\x13\xce\x5a\xa2\x3b\xa9\xac\x65\x47\xa4\x7d\x16\xa8\x5f\x09\xd7\x19\x3b\x98\x56\x45\xb3\xc6\x0d\x01\x63\x6a\xb7\x73\x61\x0a\x06\x13\xd6\xa7\xac\xd2\x76\xb0\x53\xdb\xe5\x59\x3e\x33\xbd\xb6\x0d\x4e\x70\xbd\xcd\xd5\x46\xd8\xb4\x7e\x6a\x47\x87\xd4\xee\xde\x9b\x8b\x25\x8c\xc5\xa3\x5b\xd1\x40\x79\xa6\x2a\x40\x29\x3b\x99\x8f\x23\x16\x82\xc2\x94\x8b\x30\xb4\x4c\x01\xb1\x53\x1d\x32\xc7\xc9\xce\x27\xa3\x9d\x87\xd9\x86\x2b\x6d\x03\x1a\xc2\x39\xb6\xb0\xda\x3b\xd7\xd5\x96\xf1\x16\xa5\x82\x15\xd2\x4e\x22\x28\xb2\x73\x7b\x1b\xda\x83\x7f\x8e\xc4\x7d\x8e\x4c\xb2\x94\xc7\xd2\x43\xa8\x5b\x7b\xc9\xc0\x98\xbb\x24\xa6\xac\x6e\x3f\xdd\xd5\xe5\xa8\xd7\x98\x2a\x00\x20\x57\xf8\x56\xbe\xad\x68\xca\x0a\x92\x20\x18\x9c\x5b\xb6\x3b\x90\xb9\x78\x07\xd7\x07\x50\xe1\x3c\x6f\xeb\xba\xbe\x73\x69\x27\x9b\x1b\x2a\x1d\xb3\xbb\x07\x27\xb4\xfd\x71\x0e\x85\xb0\xa1\x2f\xe1\x62\x52\x89\x40\x7b\xc2\xc8\xf1\x78\x74\x3c\xca\x37\xa1\xaa\x79\x02\x15\x46\xd8\x9d\xc3\x47\xa2\xde\x4a\x01\x49\x7c\x7c\xa3\xfe\x49\xdc\x36\xff\x7e\x0e\xd4\x31\xf6\x84\xad\xf2\x68\xce\x6c\x37\xa5\x74\x35\x13\xd3\xbc\xd5\x17\x67\xf9\x6f\x09\x82\xf1\x43\x40\x24\x82\x52\xc6\xab\x28\x39\x7e\x83\x87\x60\x3c\x55\x60\xca\xb1\xc1\xf1\x69\x7b\xeb\xcd\xab\x8e\xd6\xd8\xff\xfe\x0d\x00\x00\xff\xff\xce\x02\xbd\xcc\x23\x08\x00\x00") +var _templateMetaTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x55\x4f\x6b\xdb\x4e\x10\x3d\xcb\x9f\x62\x7e\x42\x01\x09\x6c\xf9\xd7\x6b\x8a\x4f\x0d\xa5\x81\x52\x0a\x0d\xb9\x84\x10\xd6\xd2\x6c\xbc\xc9\x7a\xe5\xee\xae\x9d\x04\xb1\xdf\xbd\xcc\xfe\xb1\x25\xc7\x69\xd2\x93\xb5\x3b\x33\x6f\xde\x9b\x79\x92\xfb\x1e\x5a\xe4\x42\x21\xe4\x6b\xb4\x2c\x07\xe7\x26\x93\xbe\x07\x8b\xeb\x8d\x64\x16\x21\x5f\x21\x6b\x51\xe7\x50\xd4\x3f\x59\xf3\xc8\xee\xf1\x75\x8a\x58\x6f\x3a\x6d\x73\x28\x7c\xa8\xe9\x94\xb1\x50\x4e\xb2\xf9\x1c\xbe\xb3\x25\x4a\x58\x75\xb2\x35\x60\x57\x08\xc6\x6a\xa1\xee\x41\xfa\xeb\x16\x55\x67\xe9\x48\x91\xbe\x07\xd9\x3d\xa1\x86\xa2\xfe\xc1\xd6\xd4\x04\xec\xcb\x06\x41\x28\x1f\x6e\x99\x65\x4b\x66\xb0\x9e\x64\x01\x73\x01\x79\xdf\x43\x51\x87\x93\x73\xb9\xef\xe7\xaf\x2e\x2f\xea\x2f\xc4\x81\x29\x4b\x30\xaf\xba\x8f\xfa\x8a\x16\xb8\x40\xd9\x9e\x68\x74\x0a\x2c\xb4\x35\x8a\x3d\x62\x08\x46\xb2\xb9\x4f\xd7\x4c\xdd\x23\x14\x77\x53\x28\x38\x9c\x2f\xa0\xa8\xbf\x12\xb6\x81\x99\x73\x84\x16\x3a\x51\x80\x1f\x50\x67\xce\x4d\xb2\x44\x3e\x64\xbc\xcb\xfa\x30\x2d\xbe\x1f\xd7\x0e\xb5\xc5\x67\xd8\xe8\x6e\x83\xda\xbe\x9c\x10\x94\x8d\x3a\x8c\xa4\xf0\xb1\x10\x54\x6d\xe0\x35\x16\x65\x6c\xa7\xc9\x01\x5e\xda\xaf\x78\xf0\xf4\x09\xd9\xae\x37\x92\x42\x1b\x2d\x94\xe5\x90\xb7\x82\x49\x6c\xec\xfc\xcc\xcc\xc9\x5b\xf3\x26\x2a\x36\xf9\x01\x29\x15\x3f\xef\xdd\x14\x60\xbc\x95\x12\x13\xe7\x26\x95\xb7\xdc\x47\xa8\x7c\x84\xc9\x8e\x69\xc1\x96\x12\x8f\x99\xf4\x3d\x08\x0e\x2b\x66\xae\xc6\x6c\x3e\xca\xf2\xf0\x34\x09\x50\x1d\xf9\xf9\x1b\x33\x17\xc8\xd9\x56\xda\x70\xb8\x66\x52\xb4\xcc\x76\xda\xa4\xcc\x7f\x2e\xda\x31\x4d\x6f\x18\x0f\xee\x5a\x90\x1f\x68\xc7\x34\x8a\x66\x85\x6b\x06\xce\xd5\xde\xbe\x71\xab\xbd\x8b\x4e\x2c\xab\xe1\x4e\xc5\x29\xa3\x06\xa9\x82\x93\x29\x06\x2c\x7c\x20\xeb\xfb\x19\x14\x6d\xbc\x0a\x46\x8e\x09\x47\x7e\x4e\x86\x6e\xf7\xe5\x03\x4b\xa7\xcb\x1d\x93\x5b\x04\xde\xe9\x64\xea\x81\x9d\xbd\xb6\x3a\xf4\x1c\xc1\x2c\x42\xc8\xdc\xd0\xbd\x00\xe7\x6e\xeb\x6b\xc2\x29\xab\xba\x0c\x10\x57\xf4\xe1\x70\xae\x0a\x4a\xf6\x5e\xa6\xc3\x93\xb0\x2b\x4a\x19\x8c\x73\x96\x94\x41\xa1\xa8\x77\x50\xb5\x4f\x18\xc6\xfd\x17\xe9\x7c\x01\x7b\x63\xf1\xad\x6a\xa0\x3c\x33\x15\xa0\xd6\x1d\x7d\x25\x63\xf7\xf1\x10\x54\xd4\x24\x0c\x30\x12\x1d\x91\x93\xf0\x7c\xa4\x3c\x8f\xd2\xe1\xd2\x52\x41\xc3\xa4\xc4\x16\x96\x2f\x3e\x75\xb9\x15\xb2\x45\x6d\x60\x89\xbc\xd3\x08\x86\xed\x30\x0d\x49\x70\xc0\xdf\x47\xe2\x3e\x25\x26\xd9\x90\xc7\xe9\x11\xc6\x9a\xb2\xba\xf9\xff\x36\x8c\xd2\x1e\x06\xe9\x27\x29\x0d\xbe\x85\xb7\x55\x4d\x59\xc1\xa0\x08\x7a\x9f\x96\xed\x0e\x64\xce\xdf\xe9\x1b\x0a\xb8\xf2\x99\x37\x75\x5d\xdf\x7a\xd8\xf1\x52\xc3\xa4\x13\xba\x37\x64\x74\xf3\xc3\x14\x0a\x45\xa5\xcf\xf1\x62\x34\x89\x48\x7b\xc4\xc8\xf3\x78\xf0\x3c\xca\x37\x5b\x55\xd3\x41\xab\xf8\xa6\xfa\x73\xfc\xd1\x68\xb7\x5a\xc1\xa0\x3e\x59\xf8\xaf\xc4\x69\xf9\x77\x53\xe0\x9e\x71\x20\x4c\xca\x53\x38\xa3\x6d\x6a\xed\x67\xa6\xc6\xb8\xd5\x67\x1f\xf9\x6f\x01\x4a\xc8\x43\x41\x22\x82\x5a\xa7\xab\x24\x39\xfd\xc6\x0c\x25\xe4\x50\x81\x2b\xf7\x0b\x1e\xbe\x2a\xc3\xff\x80\xf4\x5c\x1d\x7d\xad\xc2\xe3\x9f\x00\x00\x00\xff\xff\xc3\x9a\x56\xd2\x45\x08\x00\x00") func templateMetaTmplBytes() ([]byte, error) { return bindataRead( @@ -779,7 +779,7 @@ func templateMetaTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/meta.tmpl", size: 2083, mode: os.FileMode(420), modTime: time.Unix(1564435072, 0)} + info := bindataFileInfo{name: "template/meta.tmpl", size: 2117, mode: os.FileMode(420), modTime: time.Unix(1564496470, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -839,7 +839,7 @@ func templatePredicateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/predicate.tmpl", size: 922, mode: os.FileMode(420), modTime: time.Unix(1564434863, 0)} + info := bindataFileInfo{name: "template/predicate.tmpl", size: 922, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -879,7 +879,7 @@ func templateWhereTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/where.tmpl", size: 4195, mode: os.FileMode(420), modTime: time.Unix(1564425091, 0)} + info := bindataFileInfo{name: "template/where.tmpl", size: 4195, mode: os.FileMode(420), modTime: time.Unix(1564480239, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/entc/gen/graph.go b/entc/gen/graph.go index 7ab85a099..e4dcc0610 100644 --- a/entc/gen/graph.go +++ b/entc/gen/graph.go @@ -10,11 +10,10 @@ import ( "os" "os/exec" "path/filepath" - "reflect" "strconv" - "fbc/ent" "fbc/ent/dialect/sql/schema" + "fbc/ent/entc/load" "fbc/ent/field" ) @@ -41,13 +40,13 @@ type ( // Nodes are list of Go types that mapped to the types in the loaded schema. Nodes []*Type // Schemas holds the raw interfaces for the loaded schemas. - Schemas []ent.Schema + Schemas []*load.Schema } ) // NewGraph creates a new Graph for the code generation from the given schema definitions. // It fails if one of the schemas is invalid. -func NewGraph(c Config, schemas ...ent.Schema) (g *Graph, err error) { +func NewGraph(c Config, schemas ...*load.Schema) (g *Graph, err error) { defer catch(&err) c.imports = imports() g = &Graph{c, make([]*Type, 0, len(schemas)), schemas} @@ -100,58 +99,58 @@ func (g *Graph) Describe(w io.Writer) { } // addNode creates a new Type/Node/Ent to the graph. -func (g *Graph) addNode(schema ent.Schema) { +func (g *Graph) addNode(schema *load.Schema) { t, err := NewType(g.Config, schema) check(err, "create type") g.Nodes = append(g.Nodes, t) } // addEdges adds the node edges to the graph. -func (g *Graph) addEdges(schema ent.Schema) { - t, _ := g.typ(reflect.TypeOf(schema).Name()) - for _, e := range schema.Edges() { - typ, ok := g.typ(e.Type()) - expect(ok, "type %q does not exist for edge", e.Type()) +func (g *Graph) addEdges(schema *load.Schema) { + t, _ := g.typ(schema.Name) + for _, e := range schema.Edges { + typ, ok := g.typ(e.Type) + expect(ok, "type %q does not exist for edge", e.Type) switch { // assoc only. - case e.IsAssoc(): + case !e.Inverse: t.Edges = append(t.Edges, &Edge{ Type: typ, - Name: e.Name(), + Name: e.Name, Owner: t, - Unique: e.IsUnique(), - Optional: !e.IsRequired(), + Unique: e.Unique, + Optional: !e.Required, }) // inverse only. - case e.IsInverse() && e.Assoc() == nil: - expect(!e.IsRequired(), "inverse edge can not be required: \"%s.%s\"", t.Name, e.Name()) - expect(e.RefName() != "", "missing reference name for inverse edge: \"%s.%s\"", t.Name, e.Name()) + case e.Inverse && e.Ref == nil: + expect(!e.Required, `inverse edge can not be required: %s.%s`, t.Name, e.Name) + expect(e.RefName != "", `missing reference name for inverse edge: %s.%s`, t.Name, e.Name) t.Edges = append(t.Edges, &Edge{ Type: typ, - Name: e.Name(), + Name: e.Name, Owner: typ, - Inverse: e.RefName(), - Unique: e.IsUnique(), - Optional: !e.IsRequired(), + Inverse: e.RefName, + Unique: e.Unique, + Optional: !e.Required, }) // inverse and assoc. - case e.IsInverse(): - ref := e.Assoc() - expect(e.RefName() == "", "reference name is derived from the assoc name: \"%s.%s\" <-> \"%s.%s\"", t.Name, ref.Name(), t.Name, e.Name()) - expect(ref.Type() == t.Name, "assoc-inverse edge allowed only as o2o relation of the same type") + case e.Inverse: + ref := e.Ref + expect(e.RefName == "", `reference name is derived from the assoc name: %s.%s <-> %s.%s`, t.Name, ref.Name, t.Name, e.Name) + expect(ref.Type == t.Name, "assoc-inverse edge allowed only as o2o relation of the same type") t.Edges = append(t.Edges, &Edge{ Type: typ, - Name: e.Name(), + Name: e.Name, Owner: t, - Inverse: ref.Name(), - Unique: e.IsUnique(), - Optional: !e.IsRequired(), + Inverse: ref.Name, + Unique: e.Unique, + Optional: !e.Required, }, &Edge{ Type: typ, Owner: t, - Name: ref.Name(), - Unique: ref.IsUnique(), - Optional: !ref.IsRequired(), + Name: ref.Name, + Unique: ref.Unique, + Optional: !ref.Required, }) default: panic(graphError{"edge must be either an assoc or inverse edge"}) @@ -187,7 +186,7 @@ func (g *Graph) resolve(t *Type) error { case e.IsInverse(): ref, ok := e.Type.HasAssoc(e.Inverse) if !ok { - return fmt.Errorf("assoc is missing for inverse edge: \"%s.%s\"", e.Type.Name, e.Name) + return fmt.Errorf(`assoc is missing for inverse edge: %s.%s`, e.Type.Name, e.Name) } table := t.Table() // The name of the column is how we identify the other side. For example "A Parent has Children" diff --git a/entc/gen/graph_test.go b/entc/gen/graph_test.go index e62d77725..3fa8594ff 100644 --- a/entc/gen/graph_test.go +++ b/entc/gen/graph_test.go @@ -6,74 +6,62 @@ import ( "path/filepath" "testing" - "fbc/ent" - "fbc/ent/edge" + "fbc/ent/entc/load" "fbc/ent/field" "github.com/stretchr/testify/require" ) -type T1 struct { - ent.Schema -} - -func (T1) Fields() []ent.Field { - return []ent.Field{ - field.Int("age").Optional(), - field.Time("expired_at").Nullable(), - field.String("name").Default("hello"), +var ( + T1 = &load.Schema{ + Name: "T1", + Fields: []*load.Field{ + {Name: "age", Type: field.TypeInt, Optional: true}, + {Name: "expired_at", Type: field.TypeTime, Nullable: true}, + {Name: "name", Type: field.TypeString, Default: true}, + }, + Edges: []*load.Edge{ + {Name: "t2", Type: "T2", Required: true}, + {Name: "t1", Type: "T1", Unique: true}, + // Bidirectional unique edge (unique/"has-a" in both sides). + {Name: "t2_o2o", Type: "T2", Unique: true}, + // Unidirectional non-unique edge ("has-many"). The reference is on the "many" side. + // For example: A user "has-many" books, but a book "has-an" owner (and only one). + {Name: "o2m", Type: "T2"}, + // Unidirectional unique edge ("has-one"). + // For example: A user "has-an" address (and only one), but an address "has-many" users. + {Name: "m2o", Type: "T2", Unique: true}, + // Bidirectional unique edge ("has-one" in T1 side, and "has-many" in T2 side). + {Name: "t2_m2o", Type: "T2", Unique: true}, + // Bidirectional non-unique edge ("has-many" in T1 side, and "has-one" in T2 side). + {Name: "t2_o2m", Type: "T2"}, + // Bidirectional non-unique edge ("has-many" in both side). + {Name: "t2_m2m", Type: "T2"}, + // Unidirectional non-unique edge for the same type. + {Name: "t1_m2m", Type: "T1"}, + }, } -} - -func (T1) Edges() []ent.Edge { - return []ent.Edge{ - edge.To("t2", T2.Type).Required(), - edge.To("t1", T1.Type).Unique(), - // Bidirectional unique edge (unique/"has-a" in both sides). - edge.To("t2_o2o", T2.Type).Unique(), - // Unidirectional non-unique edge ("has-many"). The reference is on the "many" side. - // For example: A user "has-many" books, but a book "has-an" owner (and only one). - edge.To("o2m", T2.Type), - // Unidirectional unique edge ("has-one"). - // For example: A user "has-an" address (and only one), but an address "has-many" users. - edge.To("m2o", T2.Type).Unique(), - // Bidirectional unique edge ("has-one" in T1 side, and "has-many" in T2 side). - edge.To("t2_m2o", T2.Type).Unique(), - // Bidirectional non-unique edge ("has-many" in T1 side, and "has-one" in T2 side). - edge.To("t2_o2m", T2.Type), - // Bidirectional non-unique edge ("has-many" in both side). - edge.To("t2_m2m", T2.Type), - // Unidirectional non-unique edge for the same type. - edge.To("t1_m2m", T1.Type), + T2 = &load.Schema{ + Name: "T2", + Fields: []*load.Field{ + {Name: "active", Type: field.TypeBool}, + }, + Edges: []*load.Edge{ + {Name: "t1", Type: "T1", RefName: "t2", Inverse: true}, + {Name: "t1_o2o", Type: "T1", RefName: "t2_o2o", Unique: true, Inverse: true}, + {Name: "t1_o2m", Type: "T1", RefName: "t2_m2o", Inverse: true}, + {Name: "t1_m2o", Type: "T1", RefName: "t2_o2m", Unique: true, Inverse: true}, + {Name: "t1_m2m", Type: "T1", RefName: "t2_m2m", Inverse: true}, + }, } -} - -type T2 struct { - ent.Schema -} - -func (T2) Fields() []ent.Field { - return []ent.Field{ - field.Bool("active"), - } -} - -func (T2) Edges() []ent.Edge { - return []ent.Edge{ - edge.From("t1", T1.Type).Ref("t2"), - edge.From("t1_o2o", T1.Type).Unique().Ref("t2_o2o"), - edge.From("t1_o2m", T1.Type).Ref("t2_m2o"), - edge.From("t1_m2o", T1.Type).Ref("t2_o2m").Unique(), - edge.From("t1_m2m", T1.Type).Ref("t2_m2m"), - } -} +) func TestNewGraph(t *testing.T) { require := require.New(t) - _, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1{}) + _, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1) require.Error(err, "should fail due to missing types") - graph, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1{}, T2{}) + graph, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1, T2) require.NoError(err) require.NotNil(graph) require.Len(graph.Nodes, 2) @@ -95,9 +83,8 @@ func TestNewGraph(t *testing.T) { for i, nullable := range []bool{false, true, false} { require.Equal(nullable, t1.Fields[i].Nullable) } - for i, value := range []interface{}{nil, nil, "hello"} { - require.Equal(value, t1.Fields[i].Default) - require.Equal(value != nil, t1.Fields[i].HasDefault()) + for i, value := range []bool{false, false, true} { + require.Equal(value, t1.Fields[i].HasDefault) } // check edges. @@ -130,10 +117,10 @@ func TestNewGraph(t *testing.T) { func TestRelation(t *testing.T) { require := require.New(t) - _, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1{}) + _, err := NewGraph(Config{Package: "entc/gen", Storage: drivers}, T1) require.Error(err, "should fail due to missing types") - graph, err := NewGraph(Config{Package: "entc/gen"}, T1{}, T2{}) + graph, err := NewGraph(Config{Package: "entc/gen"}, T1, T2) require.NoError(err) require.NotNil(graph) require.Len(graph.Nodes, 2) @@ -166,7 +153,17 @@ func TestGraph_Gen(t *testing.T) { target := filepath.Join(os.TempDir(), "ent") require.NoError(os.MkdirAll(target, os.ModePerm), "creating tmpdir") defer os.Remove(target) - graph, err := NewGraph(Config{Package: "entc/gen", Target: target, Storage: drivers}, T1{}, T2{}) + graph, err := NewGraph(Config{Package: "entc/gen", Target: target, Storage: drivers}, &load.Schema{ + Name: "T1", + Fields: []*load.Field{ + {Name: "age", Type: field.TypeInt, Optional: true}, + {Name: "expired_at", Type: field.TypeTime, Nullable: true}, + {Name: "name", Type: field.TypeString}, + }, + Edges: []*load.Edge{ + {Name: "t1", Type: "T1", Unique: true}, + }, + }) require.NoError(err) require.NotNil(graph) require.NoError(graph.Gen()) @@ -177,9 +174,7 @@ func TestGraph_Gen(t *testing.T) { } // ensure entity files were generated. for _, format := range []string{"%s", "%s_create", "%s_update", "%s_delete", "%s_query"} { - for _, name := range []string{"t1", "t2"} { - _, err := os.Stat(fmt.Sprintf(fmt.Sprintf("%s/%s.go", target, format), name)) - require.NoError(err) - } + _, err := os.Stat(fmt.Sprintf(fmt.Sprintf("%s/%s.go", target, format), "t1")) + require.NoError(err) } } diff --git a/entc/gen/template/meta.tmpl b/entc/gen/template/meta.tmpl index bc1f87138..2bfe73595 100644 --- a/entc/gen/template/meta.tmpl +++ b/entc/gen/template/meta.tmpl @@ -12,11 +12,6 @@ const ( {{ range $_, $f := $.Fields -}}{{ $field := $f.Constant -}} // {{ $field }} holds the string denoting the {{ lower $f.Name }} vertex property in the database. {{ $field }} = "{{ snake $f.Name }}" - {{ if $f.HasDefault }} - {{- $default := $f.DefaultConstant -}} - // {{ $default }} holds the default value for the {{ $f.Name }} field. - {{ $default }} {{ if $f.Type.Numeric }} {{ $f.Type }} {{ end }} = {{ printf "%#v" $f.Default }} - {{ end -}} {{ end -}} {{ range $_, $storage := $.Storage }} {{ $tmpl := printf "dialect/%s/meta/constants" $storage }} @@ -31,10 +26,19 @@ const ( {{ end }} {{ end }} -{{ if $.HasValidators }} +{{ if or $.HasDefault $.HasValidators }} + +{{ end }} + +{{ if or $.HasDefault $.HasValidators }} var ( fields = {{ base $.Schema }}.{{ $.Name }}{}.Fields() {{ range $i, $f := $.Fields -}} + {{ if $f.HasDefault }} + {{- $default := $f.DefaultConstant -}} + // {{ $default }} holds the default value for the {{ $f.Name }} field. + {{ $default }} = fields[{{ $i }}].Value().({{ $f.Type }}) + {{ end -}} {{ with $f.Validators -}} {{ $name := $f.Validator -}} {{ $type := printf "func (%s) error" $f.Type -}} diff --git a/entc/gen/type.go b/entc/gen/type.go index ea64515d9..fcf15fd14 100644 --- a/entc/gen/type.go +++ b/entc/gen/type.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "fbc/ent" "fbc/ent/dialect/sql/schema" + "fbc/ent/entc/load" "fbc/ent/field" "github.com/olekukonko/tablewriter" @@ -32,7 +32,7 @@ type ( // Field holds the information of a type field used for the templates. Field struct { // field definition. - def ent.Field + def *load.Field // Name is the name of this field in the database schema. Name string // Type holds the type information of the field. @@ -43,8 +43,8 @@ type ( Optional bool // Nullable indicates that this field can be null. Nullable bool - // Default holds the default value of this field on creation. - Default interface{} + // HasDefault indicates if this field a default value. + HasDefault bool // StructTag of the field. default to "json". StructTag string // Validators holds the number of validators this field have. @@ -95,30 +95,30 @@ type ( ) // NewType creates a new type and its fields from the given schema. -func NewType(c Config, schema ent.Schema) (*Type, error) { +func NewType(c Config, schema *load.Schema) (*Type, error) { typ := &Type{ Config: c, - Name: reflect.TypeOf(schema).Name(), + Name: schema.Name, ID: &Field{ Name: "id", Type: field.TypeString, StructTag: `json:"id,omitempty"`, }, } - for _, f := range schema.Fields() { - if !f.Type().Valid() { - return nil, fmt.Errorf("invalid type for field %s", f.Name()) + for _, f := range schema.Fields { + if !f.Type.Valid() { + return nil, fmt.Errorf("invalid type for field %s", f.Name) } typ.Fields = append(typ.Fields, &Field{ def: f, - Name: f.Name(), - Type: f.Type(), - Unique: f.IsUnique(), - Default: f.Value(), - Nullable: f.IsNullable(), - Optional: f.IsOptional(), - StructTag: structTag(f.Name(), f.Tag()), - Validators: len(f.Validators()), + Name: f.Name, + Type: f.Type, + Unique: f.Unique, + Nullable: f.Nullable, + Optional: f.Optional, + HasDefault: f.Default, + StructTag: structTag(f.Name, f.Tag), + Validators: f.Validators, }) } return typ, nil @@ -165,7 +165,7 @@ func (t Type) HasAssoc(name string) (*Edge, bool) { return nil, false } -// HasValidators indicates if any of this field has validators. +// HasValidators indicates if any of the type's field has validators. func (t Type) HasValidators() bool { for _, f := range t.Fields { if f.Validators > 0 { @@ -175,6 +175,16 @@ func (t Type) HasValidators() bool { return false } +// HasDefault indicates if any of this type's fields has default value. +func (t Type) HasDefault() bool { + for _, f := range t.Fields { + if f.HasDefault { + return true + } + } + return false +} + // NumConstraint returns the type's constraint count. Used for slice allocation. func (t Type) NumConstraint() int { var n int @@ -214,7 +224,7 @@ func (t Type) Describe(w io.Writer) { b.WriteString(t.Name + ":\n") table := tablewriter.NewWriter(b) table.SetAutoFormatHeaders(false) - table.SetHeader([]string{"Field", "Type", "Unique", "Optional", "Nullable", "Default", "StructTag", "Validators"}) + table.SetHeader([]string{"Field", "Type", "Unique", "Optional", "Nullable", "HasDefault", "StructTag", "Validators"}) for _, f := range append([]*Field{t.ID}, t.Fields...) { v := reflect.ValueOf(*f) row := make([]string, v.NumField()-1) @@ -244,9 +254,6 @@ func (t Type) Describe(w io.Writer) { io.WriteString(w, strings.ReplaceAll(b.String(), "\n", "\n\t")+"\n") } -// HasDefault returns if this field has a default value. -func (f Field) HasDefault() bool { return f.Default != nil } - // Constant returns the constant name of the field. func (f Field) Constant() string { return "Field" + pascal(f.Name) } @@ -310,11 +317,11 @@ func (f Field) Column() *schema.Column { c.Type = field.TypeInt c.Increment = true } - if cs, ok := f.def.(field.Sizer); ok { - c.Size = cs.Size() + if f.def.Size != nil { + c.Size = *f.def.Size } - if cs, ok := f.def.(field.Charseter); ok { - c.Charset = cs.Charset() + if f.def.Charset != nil { + c.Charset = *f.def.Charset } return c } diff --git a/entc/gen/type_test.go b/entc/gen/type_test.go index 8813a10a8..3dcd09558 100644 --- a/entc/gen/type_test.go +++ b/entc/gen/type_test.go @@ -12,7 +12,7 @@ import ( func TestType(t *testing.T) { require := require.New(t) - typ, err := NewType(Config{Package: "entc/gen"}, T1{}) + typ, err := NewType(Config{Package: "entc/gen"}, T1) require.NoError(err) require.NotNil(typ) require.Equal("T1", typ.Name) @@ -165,13 +165,13 @@ func TestType_Describe(t *testing.T) { }, out: ` User: - +-------+--------+--------+----------+----------+---------+-----------+------------+ - | Field | Type | Unique | Optional | Nullable | Default | StructTag | Validators | - +-------+--------+--------+----------+----------+---------+-----------+------------+ - | id | int | false | false | false | | | 0 | - | name | string | false | false | false | | | 1 | - | age | int | false | false | true | | | 0 | - +-------+--------+--------+----------+----------+---------+-----------+------------+ + +-------+--------+--------+----------+----------+------------+-----------+------------+ + | Field | Type | Unique | Optional | Nullable | HasDefault | StructTag | Validators | + +-------+--------+--------+----------+----------+------------+-----------+------------+ + | id | int | false | false | false | false | | 0 | + | name | string | false | false | false | false | | 1 | + | age | int | false | false | true | false | | 0 | + +-------+--------+--------+----------+----------+------------+-----------+------------+ `, }, @@ -186,11 +186,11 @@ User: }, out: ` User: - +-------+------+--------+----------+----------+---------+-----------+------------+ - | Field | Type | Unique | Optional | Nullable | Default | StructTag | Validators | - +-------+------+--------+----------+----------+---------+-----------+------------+ - | id | int | false | false | false | | | 0 | - +-------+------+--------+----------+----------+---------+-----------+------------+ + +-------+------+--------+----------+----------+------------+-----------+------------+ + | Field | Type | Unique | Optional | Nullable | HasDefault | StructTag | Validators | + +-------+------+--------+----------+----------+------------+-----------+------------+ + | id | int | false | false | false | false | | 0 | + +-------+------+--------+----------+----------+------------+-----------+------------+ +--------+-------+---------+---------+----------+--------+----------+ | Edge | Type | Inverse | BackRef | Relation | Unique | Optional | +--------+-------+---------+---------+----------+--------+----------+ @@ -215,13 +215,13 @@ User: }, out: ` User: - +-------+--------+--------+----------+----------+---------+-----------+------------+ - | Field | Type | Unique | Optional | Nullable | Default | StructTag | Validators | - +-------+--------+--------+----------+----------+---------+-----------+------------+ - | id | int | false | false | false | | | 0 | - | name | string | false | false | false | | | 1 | - | age | int | false | false | true | | | 0 | - +-------+--------+--------+----------+----------+---------+-----------+------------+ + +-------+--------+--------+----------+----------+------------+-----------+------------+ + | Field | Type | Unique | Optional | Nullable | HasDefault | StructTag | Validators | + +-------+--------+--------+----------+----------+------------+-----------+------------+ + | id | int | false | false | false | false | | 0 | + | name | string | false | false | false | false | | 1 | + | age | int | false | false | true | false | | 0 | + +-------+--------+--------+----------+----------+------------+-----------+------------+ +--------+-------+---------+---------+----------+--------+----------+ | Edge | Type | Inverse | BackRef | Relation | Unique | Optional | +--------+-------+---------+---------+----------+--------+----------+ diff --git a/entc/integration/ent/file/file.go b/entc/integration/ent/file/file.go index 3f5b8266e..89ec88473 100644 --- a/entc/integration/ent/file/file.go +++ b/entc/integration/ent/file/file.go @@ -29,6 +29,8 @@ var Columns = []string{ var ( fields = schema.File{}.Fields() + // DefaultSize holds the default value for the size field. + DefaultSize = fields[0].Value().(int) // SizeValidator is a validator for the "size" field. It is called by the builders before save. SizeValidator = fields[0].Validators()[0].(func(int) error) ) diff --git a/entc/integration/ent/file_create.go b/entc/integration/ent/file_create.go index c34d1f0b7..1dc462541 100644 --- a/entc/integration/ent/file_create.go +++ b/entc/integration/ent/file_create.go @@ -30,6 +30,14 @@ func (fc *FileCreate) SetSize(i int) *FileCreate { return fc } +// SetNillableSize sets the size field if the given value is not nil. +func (fc *FileCreate) SetNillableSize(i *int) *FileCreate { + if i != nil { + fc.SetSize(*i) + } + return fc +} + // SetName sets the name field. func (fc *FileCreate) SetName(s string) *FileCreate { fc.name = &s @@ -39,7 +47,8 @@ func (fc *FileCreate) SetName(s string) *FileCreate { // Save creates the File in the database. func (fc *FileCreate) Save(ctx context.Context) (*File, error) { if fc.size == nil { - return nil, errors.New("ent: missing required field \"size\"") + v := file.DefaultSize + fc.size = &v } if err := file.SizeValidator(*fc.size); err != nil { return nil, fmt.Errorf("ent: validator failed for field \"size\": %v", err) diff --git a/entc/integration/ent/file_update.go b/entc/integration/ent/file_update.go index 81f988391..33a20a3a2 100644 --- a/entc/integration/ent/file_update.go +++ b/entc/integration/ent/file_update.go @@ -37,6 +37,14 @@ func (fu *FileUpdate) SetSize(i int) *FileUpdate { return fu } +// SetNillableSize sets the size field if the given value is not nil. +func (fu *FileUpdate) SetNillableSize(i *int) *FileUpdate { + if i != nil { + fu.SetSize(*i) + } + return fu +} + // SetName sets the name field. func (fu *FileUpdate) SetName(s string) *FileUpdate { fu.name = &s @@ -179,6 +187,14 @@ func (fuo *FileUpdateOne) SetSize(i int) *FileUpdateOne { return fuo } +// SetNillableSize sets the size field if the given value is not nil. +func (fuo *FileUpdateOne) SetNillableSize(i *int) *FileUpdateOne { + if i != nil { + fuo.SetSize(*i) + } + return fuo +} + // SetName sets the name field. func (fuo *FileUpdateOne) SetName(s string) *FileUpdateOne { fuo.name = &s diff --git a/entc/integration/ent/group/group.go b/entc/integration/ent/group/group.go index 8de580dd6..a6396bc6a 100644 --- a/entc/integration/ent/group/group.go +++ b/entc/integration/ent/group/group.go @@ -13,16 +13,12 @@ const ( FieldID = "id" // FieldActive holds the string denoting the active vertex property in the database. FieldActive = "active" - // DefaultActive holds the default value for the active field. - DefaultActive = true // FieldExpire holds the string denoting the expire vertex property in the database. FieldExpire = "expire" // FieldType holds the string denoting the type vertex property in the database. FieldType = "type" // FieldMaxUsers holds the string denoting the max_users vertex property in the database. FieldMaxUsers = "max_users" - // DefaultMaxUsers holds the default value for the max_users field. - DefaultMaxUsers int = 10 // FieldName holds the string denoting the name vertex property in the database. FieldName = "name" @@ -83,8 +79,12 @@ var ( var ( fields = schema.Group{}.Fields() + // DefaultActive holds the default value for the active field. + DefaultActive = fields[0].Value().(bool) // TypeValidator is a validator for the "type" field. It is called by the builders before save. TypeValidator = fields[2].Validators()[0].(func(string) error) + // DefaultMaxUsers holds the default value for the max_users field. + DefaultMaxUsers = fields[3].Value().(int) // MaxUsersValidator is a validator for the "max_users" field. It is called by the builders before save. MaxUsersValidator = fields[3].Validators()[0].(func(int) error) // NameValidator is a validator for the "name" field. It is called by the builders before save. diff --git a/entc/integration/ent/groupinfo/groupinfo.go b/entc/integration/ent/groupinfo/groupinfo.go index f7251ce77..1f4ab94cf 100644 --- a/entc/integration/ent/groupinfo/groupinfo.go +++ b/entc/integration/ent/groupinfo/groupinfo.go @@ -2,6 +2,10 @@ package groupinfo +import ( + "fbc/ent/entc/integration/ent/schema" +) + const ( // Label holds the string label denoting the groupinfo type in the database. Label = "group_info" @@ -11,8 +15,6 @@ const ( FieldDesc = "desc" // FieldMaxUsers holds the string denoting the max_users vertex property in the database. FieldMaxUsers = "max_users" - // DefaultMaxUsers holds the default value for the max_users field. - DefaultMaxUsers int = 10000 // Table holds the table name of the groupinfo in the database. Table = "group_infos" @@ -34,3 +36,9 @@ var Columns = []string{ FieldDesc, FieldMaxUsers, } + +var ( + fields = schema.GroupInfo{}.Fields() + // DefaultMaxUsers holds the default value for the max_users field. + DefaultMaxUsers = fields[1].Value().(int) +) diff --git a/entc/integration/ent/schema/file.go b/entc/integration/ent/schema/file.go index 326252a49..8fb0903a5 100644 --- a/entc/integration/ent/schema/file.go +++ b/entc/integration/ent/schema/file.go @@ -1,6 +1,8 @@ package schema import ( + "math" + "fbc/ent" "fbc/ent/field" ) @@ -14,6 +16,7 @@ type File struct { func (File) Fields() []ent.Field { return []ent.Field{ field.Int("size"). + Default(math.MaxInt32). Positive(), field.String("name"), } diff --git a/entc/integration/ent/user/user.go b/entc/integration/ent/user/user.go index 01c88827d..c5e9697f0 100644 --- a/entc/integration/ent/user/user.go +++ b/entc/integration/ent/user/user.go @@ -2,6 +2,10 @@ package user +import ( + "fbc/ent/entc/integration/ent/schema" +) + const ( // Label holds the string label denoting the user type in the database. Label = "user" @@ -13,8 +17,6 @@ const ( FieldName = "name" // FieldLast holds the string denoting the last vertex property in the database. FieldLast = "last" - // DefaultLast holds the default value for the last field. - DefaultLast = "unknown" // FieldNickname holds the string denoting the nickname vertex property in the database. FieldNickname = "nickname" // FieldPhone holds the string denoting the phone vertex property in the database. @@ -122,3 +124,9 @@ var ( // primary key for the following relation (M2M). FollowingPrimaryKey = []string{"user_id", "follower_id"} ) + +var ( + fields = schema.User{}.Fields() + // DefaultLast holds the default value for the last field. + DefaultLast = fields[2].Value().(string) +) diff --git a/entc/integration/generate.go b/entc/integration/generate.go index 55fdfebbd..fcb13afb5 100644 --- a/entc/integration/generate.go +++ b/entc/integration/generate.go @@ -1,6 +1,5 @@ package integration //go:generate go run ../cmd/entc/entc.go generate --storage=sql,gremlin ./ent/schema -//go:generate go run ../cmd/entc/entc.go generate --storage=sql,gremlin ./plugin/ent/schema //go:generate go run ../cmd/entc/entc.go generate ./migrate/entv1/schema //go:generate go run ../cmd/entc/entc.go generate ./migrate/entv2/schema diff --git a/entc/integration/plugin/README.md b/entc/integration/plugin/README.md deleted file mode 100644 index a1ac311dd..000000000 --- a/entc/integration/plugin/README.md +++ /dev/null @@ -1,8 +0,0 @@ -### Example plugins for testing purpose - -#### Generating new assets for plugin tests - -From `plugin` directory, run: -``` -go run ../../cmd/entc/entc.go generate ./ent/schema -``` \ No newline at end of file diff --git a/entc/integration/plugin/ent/boring.go b/entc/integration/plugin/ent/boring.go deleted file mode 100644 index 9ac940997..000000000 --- a/entc/integration/plugin/ent/boring.go +++ /dev/null @@ -1,124 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "bytes" - "fmt" - "strconv" - - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/sql" -) - -// Boring is the model entity for the Boring schema. -type Boring struct { - config - // ID of the ent. - ID string `json:"id,omitempty"` -} - -// FromRows scans the sql response data into Boring. -func (b *Boring) FromRows(rows *sql.Rows) error { - var vb struct { - ID int - } - // the order here should be the same as in the `boring.Columns`. - if err := rows.Scan( - &vb.ID, - ); err != nil { - return err - } - b.ID = strconv.Itoa(vb.ID) - return nil -} - -// FromResponse scans the gremlin response data into Boring. -func (b *Boring) FromResponse(res *gremlin.Response) error { - vmap, err := res.ReadValueMap() - if err != nil { - return err - } - var vb struct { - ID string `json:"id,omitempty"` - } - if err := vmap.Decode(&vb); err != nil { - return err - } - b.ID = vb.ID - return nil -} - -// Update returns a builder for updating this Boring. -// Note that, you need to call Boring.Unwrap() before calling this method, if this Boring -// was returned from a transaction, and the transaction was committed or rolled back. -func (b *Boring) Update() *BoringUpdateOne { - return (&BoringClient{b.config}).UpdateOne(b) -} - -// Unwrap unwraps the entity that was returned from a transaction after it was closed, -// so that all next queries will be executed through the driver which created the transaction. -func (b *Boring) Unwrap() *Boring { - tx, ok := b.config.driver.(*txDriver) - if !ok { - panic("ent: Boring is not a transactional entity") - } - b.config.driver = tx.drv - return b -} - -// String implements the fmt.Stringer. -func (b *Boring) String() string { - buf := bytes.NewBuffer(nil) - buf.WriteString("Boring(") - buf.WriteString(fmt.Sprintf("id=%v", b.ID)) - buf.WriteString(")") - return buf.String() -} - -// id returns the int representation of the ID field. -func (b *Boring) id() int { - id, _ := strconv.Atoi(b.ID) - return id -} - -// Borings is a parsable slice of Boring. -type Borings []*Boring - -// FromRows scans the sql response data into Borings. -func (b *Borings) FromRows(rows *sql.Rows) error { - for rows.Next() { - vb := &Boring{} - if err := vb.FromRows(rows); err != nil { - return err - } - *b = append(*b, vb) - } - return nil -} - -// FromResponse scans the gremlin response data into Borings. -func (b *Borings) FromResponse(res *gremlin.Response) error { - vmap, err := res.ReadValueMap() - if err != nil { - return err - } - var vb []struct { - ID string `json:"id,omitempty"` - } - if err := vmap.Decode(&vb); err != nil { - return err - } - for _, v := range vb { - *b = append(*b, &Boring{ - ID: v.ID, - }) - } - return nil -} - -func (b Borings) config(cfg config) { - for i := range b { - b[i].config = cfg - } -} diff --git a/entc/integration/plugin/ent/boring/boring.go b/entc/integration/plugin/ent/boring/boring.go deleted file mode 100644 index 47e00fb07..000000000 --- a/entc/integration/plugin/ent/boring/boring.go +++ /dev/null @@ -1,18 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package boring - -const ( - // Label holds the string label denoting the boring type in the database. - Label = "boring" - // FieldID holds the string denoting the id field in the database. - FieldID = "id" - - // Table holds the table name of the boring in the database. - Table = "borings" -) - -// Columns holds all SQL columns are boring fields. -var Columns = []string{ - FieldID, -} diff --git a/entc/integration/plugin/ent/boring/where.go b/entc/integration/plugin/ent/boring/where.go deleted file mode 100644 index f56e5f159..000000000 --- a/entc/integration/plugin/ent/boring/where.go +++ /dev/null @@ -1,194 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package boring - -import ( - "strconv" - - "fbc/ent/entc/integration/plugin/ent/predicate" - - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/__" - "fbc/ent/dialect/gremlin/graph/dsl/p" - "fbc/ent/dialect/sql" -) - -// ID filters vertices based on their identifier. -func ID(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - id, _ := strconv.Atoi(id) - s.Where(sql.EQ(s.C(FieldID), id)) - }, - func(t *dsl.Traversal) { - t.HasID(id) - }, - ) -} - -// IDEQ applies the EQ predicate on the ID field. -func IDEQ(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.EQ(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.EQ(id)) - }, - ) -} - -// IDNEQ applies the NEQ predicate on the ID field. -func IDNEQ(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.NEQ(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.NEQ(id)) - }, - ) -} - -// IDGT applies the GT predicate on the ID field. -func IDGT(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.GT(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.GT(id)) - }, - ) -} - -// IDGTE applies the GTE predicate on the ID field. -func IDGTE(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.GTE(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.GTE(id)) - }, - ) -} - -// IDLT applies the LT predicate on the ID field. -func IDLT(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.LT(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.LT(id)) - }, - ) -} - -// IDLTE applies the LTE predicate on the ID field. -func IDLTE(id string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - v, _ := strconv.Atoi(id) - s.Where(sql.LTE(s.C(FieldID), v)) - }, - func(t *dsl.Traversal) { - t.HasID(p.LTE(id)) - }, - ) -} - -// IDIn applies the In predicate on the ID field. -func IDIn(ids ...string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - // if not arguments were provided, append the FALSE constants, - // since we can't apply "IN ()". This will make this predicate falsy. - if len(ids) == 0 { - s.Where(sql.False()) - return - } - v := make([]interface{}, len(ids)) - for i := range v { - v[i], _ = strconv.Atoi(ids[i]) - } - s.Where(sql.In(s.C(FieldID), v...)) - }, - func(t *dsl.Traversal) { - v := make([]interface{}, len(ids)) - for i := range v { - v[i] = ids[i] - } - t.HasID(p.Within(v...)) - }, - ) -} - -// IDNotIn applies the NotIn predicate on the ID field. -func IDNotIn(ids ...string) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - // if not arguments were provided, append the FALSE constants, - // since we can't apply "IN ()". This will make this predicate falsy. - if len(ids) == 0 { - s.Where(sql.False()) - return - } - v := make([]interface{}, len(ids)) - for i := range v { - v[i], _ = strconv.Atoi(ids[i]) - } - s.Where(sql.NotIn(s.C(FieldID), v...)) - }, - func(t *dsl.Traversal) { - v := make([]interface{}, len(ids)) - for i := range v { - v[i] = ids[i] - } - t.HasID(p.Without(v...)) - }, - ) -} - -// Or groups list of predicates with the or operator between them. -func Or(predicates ...predicate.Boring) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - for i, p := range predicates { - if i > 0 { - s.Or() - } - p(s) - } - }, - func(tr *dsl.Traversal) { - trs := make([]interface{}, 0, len(predicates)) - for _, p := range predicates { - t := __.New() - p(t) - trs = append(trs, t) - } - tr.Where(__.Or(trs...)) - }, - ) -} - -// Not applies the not operator on the given predicate. -func Not(p predicate.Boring) predicate.Boring { - return predicate.BoringPerDialect( - func(s *sql.Selector) { - p(s.Not()) - }, - func(tr *dsl.Traversal) { - t := __.New() - p(t) - tr.Where(__.Not(t)) - }, - ) -} diff --git a/entc/integration/plugin/ent/boring_create.go b/entc/integration/plugin/ent/boring_create.go deleted file mode 100644 index fbfdb590a..000000000 --- a/entc/integration/plugin/ent/boring_create.go +++ /dev/null @@ -1,89 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - "strconv" - - "fbc/ent/entc/integration/plugin/ent/boring" - - "fbc/ent/dialect" - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/g" - "fbc/ent/dialect/sql" -) - -// BoringCreate is the builder for creating a Boring entity. -type BoringCreate struct { - config -} - -// Save creates the Boring in the database. -func (bc *BoringCreate) Save(ctx context.Context) (*Boring, error) { - switch bc.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bc.sqlSave(ctx) - case dialect.Neptune: - return bc.gremlinSave(ctx) - default: - return nil, errors.New("ent: unsupported dialect") - } -} - -// SaveX calls Save and panics if Save returns an error. -func (bc *BoringCreate) SaveX(ctx context.Context) *Boring { - v, err := bc.Save(ctx) - if err != nil { - panic(err) - } - return v -} - -func (bc *BoringCreate) sqlSave(ctx context.Context) (*Boring, error) { - var ( - res sql.Result - b = &Boring{config: bc.config} - ) - tx, err := bc.driver.Tx(ctx) - if err != nil { - return nil, err - } - builder := sql.Insert(boring.Table).Default(bc.driver.Dialect()) - query, args := builder.Query() - if err := tx.Exec(ctx, query, args, &res); err != nil { - return nil, rollback(tx, err) - } - id, err := res.LastInsertId() - if err != nil { - return nil, rollback(tx, err) - } - b.ID = strconv.FormatInt(id, 10) - if err := tx.Commit(); err != nil { - return nil, err - } - return b, nil -} - -func (bc *BoringCreate) gremlinSave(ctx context.Context) (*Boring, error) { - res := &gremlin.Response{} - query, bindings := bc.gremlin().Query() - if err := bc.driver.Exec(ctx, query, bindings, res); err != nil { - return nil, err - } - if err, ok := isConstantError(res); ok { - return nil, err - } - b := &Boring{config: bc.config} - if err := b.FromResponse(res); err != nil { - return nil, err - } - return b, nil -} - -func (bc *BoringCreate) gremlin() *dsl.Traversal { - v := g.AddV(boring.Label) - return v.ValueMap(true) -} diff --git a/entc/integration/plugin/ent/boring_delete.go b/entc/integration/plugin/ent/boring_delete.go deleted file mode 100644 index afc88828b..000000000 --- a/entc/integration/plugin/ent/boring_delete.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - - "fbc/ent/entc/integration/plugin/ent/boring" - "fbc/ent/entc/integration/plugin/ent/predicate" - - "fbc/ent/dialect" - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/g" - "fbc/ent/dialect/sql" -) - -// BoringDelete is the builder for deleting a Boring entity. -type BoringDelete struct { - config - predicates []predicate.Boring -} - -// Where adds a new predicate for the builder. -func (bd *BoringDelete) Where(ps ...predicate.Boring) *BoringDelete { - bd.predicates = append(bd.predicates, ps...) - return bd -} - -// Exec executes the deletion query. -func (bd *BoringDelete) Exec(ctx context.Context) error { - switch bd.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bd.sqlExec(ctx) - case dialect.Neptune: - return bd.gremlinExec(ctx) - default: - return errors.New("ent: unsupported dialect") - } -} - -// ExecX is like Exec, but panics if an error occurs. -func (bd *BoringDelete) ExecX(ctx context.Context) { - if err := bd.Exec(ctx); err != nil { - panic(err) - } -} - -func (bd *BoringDelete) sqlExec(ctx context.Context) error { - var res sql.Result - selector := sql.Select().From(sql.Table(boring.Table)) - for _, p := range bd.predicates { - p(selector) - } - query, args := sql.Delete(boring.Table).FromSelect(selector).Query() - return bd.driver.Exec(ctx, query, args, &res) -} - -func (bd *BoringDelete) gremlinExec(ctx context.Context) error { - res := &gremlin.Response{} - query, bindings := bd.gremlin().Query() - return bd.driver.Exec(ctx, query, bindings, res) -} - -func (bd *BoringDelete) gremlin() *dsl.Traversal { - t := g.V().HasLabel(boring.Label) - for _, p := range bd.predicates { - p(t) - } - return t.Drop() -} - -// BoringDeleteOne is the builder for deleting a single Boring entity. -type BoringDeleteOne struct { - bd *BoringDelete -} - -// Exec executes the deletion query. -func (bdo *BoringDeleteOne) Exec(ctx context.Context) error { - return bdo.bd.Exec(ctx) -} - -// ExecX is like Exec, but panics if an error occurs. -func (bdo *BoringDeleteOne) ExecX(ctx context.Context) { - bdo.bd.ExecX(ctx) -} diff --git a/entc/integration/plugin/ent/boring_query.go b/entc/integration/plugin/ent/boring_query.go deleted file mode 100644 index ccc7272cc..000000000 --- a/entc/integration/plugin/ent/boring_query.go +++ /dev/null @@ -1,618 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - "fmt" - "math" - - "fbc/ent/entc/integration/plugin/ent/boring" - "fbc/ent/entc/integration/plugin/ent/predicate" - - "fbc/ent/dialect" - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/__" - "fbc/ent/dialect/gremlin/graph/dsl/g" - "fbc/ent/dialect/sql" -) - -// BoringQuery is the builder for querying Boring entities. -type BoringQuery struct { - config - limit *int - offset *int - order []Order - unique []string - predicates []predicate.Boring - // intermediate queries. - sql *sql.Selector - gremlin *dsl.Traversal -} - -// Where adds a new predicate for the builder. -func (bq *BoringQuery) Where(ps ...predicate.Boring) *BoringQuery { - bq.predicates = append(bq.predicates, ps...) - return bq -} - -// Limit adds a limit step to the query. -func (bq *BoringQuery) Limit(limit int) *BoringQuery { - bq.limit = &limit - return bq -} - -// Offset adds an offset step to the query. -func (bq *BoringQuery) Offset(offset int) *BoringQuery { - bq.offset = &offset - return bq -} - -// Order adds an order step to the query. -func (bq *BoringQuery) Order(o ...Order) *BoringQuery { - bq.order = append(bq.order, o...) - return bq -} - -// Get returns a Boring entity by its id. -func (bq *BoringQuery) Get(ctx context.Context, id string) (*Boring, error) { - return bq.Where(boring.ID(id)).Only(ctx) -} - -// GetX is like Get, but panics if an error occurs. -func (bq *BoringQuery) GetX(ctx context.Context, id string) *Boring { - b, err := bq.Get(ctx, id) - if err != nil { - panic(err) - } - return b -} - -// First returns the first Boring entity in the query. Returns *ErrNotFound when no boring was found. -func (bq *BoringQuery) First(ctx context.Context) (*Boring, error) { - bs, err := bq.Limit(1).All(ctx) - if err != nil { - return nil, err - } - if len(bs) == 0 { - return nil, &ErrNotFound{boring.Label} - } - return bs[0], nil -} - -// FirstX is like First, but panics if an error occurs. -func (bq *BoringQuery) FirstX(ctx context.Context) *Boring { - b, err := bq.First(ctx) - if err != nil && !IsNotFound(err) { - panic(err) - } - return b -} - -// FirstID returns the first Boring id in the query. Returns *ErrNotFound when no id was found. -func (bq *BoringQuery) FirstID(ctx context.Context) (id string, err error) { - var ids []string - if ids, err = bq.Limit(1).IDs(ctx); err != nil { - return - } - if len(ids) == 0 { - err = &ErrNotFound{boring.Label} - return - } - return ids[0], nil -} - -// FirstXID is like FirstID, but panics if an error occurs. -func (bq *BoringQuery) FirstXID(ctx context.Context) string { - id, err := bq.FirstID(ctx) - if err != nil && !IsNotFound(err) { - panic(err) - } - return id -} - -// Only returns the only Boring entity in the query, returns an error if not exactly one entity was returned. -func (bq *BoringQuery) Only(ctx context.Context) (*Boring, error) { - bs, err := bq.Limit(2).All(ctx) - if err != nil { - return nil, err - } - switch len(bs) { - case 1: - return bs[0], nil - case 0: - return nil, &ErrNotFound{boring.Label} - default: - return nil, &ErrNotSingular{boring.Label} - } -} - -// OnlyX is like Only, but panics if an error occurs. -func (bq *BoringQuery) OnlyX(ctx context.Context) *Boring { - b, err := bq.Only(ctx) - if err != nil { - panic(err) - } - return b -} - -// OnlyID returns the only Boring id in the query, returns an error if not exactly one id was returned. -func (bq *BoringQuery) OnlyID(ctx context.Context) (id string, err error) { - var ids []string - if ids, err = bq.Limit(2).IDs(ctx); err != nil { - return - } - switch len(ids) { - case 1: - id = ids[0] - case 0: - err = &ErrNotFound{boring.Label} - default: - err = &ErrNotSingular{boring.Label} - } - return -} - -// OnlyXID is like OnlyID, but panics if an error occurs. -func (bq *BoringQuery) OnlyXID(ctx context.Context) string { - id, err := bq.OnlyID(ctx) - if err != nil { - panic(err) - } - return id -} - -// All executes the query and returns a list of Borings. -func (bq *BoringQuery) All(ctx context.Context) ([]*Boring, error) { - switch bq.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bq.sqlAll(ctx) - case dialect.Neptune: - return bq.gremlinAll(ctx) - default: - return nil, errors.New("ent: unsupported dialect") - } -} - -// AllX is like All, but panics if an error occurs. -func (bq *BoringQuery) AllX(ctx context.Context) []*Boring { - bs, err := bq.All(ctx) - if err != nil { - panic(err) - } - return bs -} - -// IDs executes the query and returns a list of Boring ids. -func (bq *BoringQuery) IDs(ctx context.Context) ([]string, error) { - switch bq.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bq.sqlIDs(ctx) - case dialect.Neptune: - return bq.gremlinIDs(ctx) - default: - return nil, errors.New("ent: unsupported dialect") - } -} - -// IDsX is like IDs, but panics if an error occurs. -func (bq *BoringQuery) IDsX(ctx context.Context) []string { - ids, err := bq.IDs(ctx) - if err != nil { - panic(err) - } - return ids -} - -// Count returns the count of the given query. -func (bq *BoringQuery) Count(ctx context.Context) (int, error) { - switch bq.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bq.sqlCount(ctx) - case dialect.Neptune: - return bq.gremlinCount(ctx) - default: - return 0, errors.New("ent: unsupported dialect") - } -} - -// CountX is like Count, but panics if an error occurs. -func (bq *BoringQuery) CountX(ctx context.Context) int { - count, err := bq.Count(ctx) - if err != nil { - panic(err) - } - return count -} - -// Exist returns true if the query has elements in the graph. -func (bq *BoringQuery) Exist(ctx context.Context) (bool, error) { - switch bq.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bq.sqlExist(ctx) - case dialect.Neptune: - return bq.gremlinExist(ctx) - default: - return false, errors.New("ent: unsupported dialect") - } -} - -// ExistX is like Exist, but panics if an error occurs. -func (bq *BoringQuery) ExistX(ctx context.Context) bool { - exist, err := bq.Exist(ctx) - if err != nil { - panic(err) - } - return exist -} - -// Clone returns a duplicate of the query builder, including all associated steps. It can be -// used to prepare common query builders and use them differently after the clone is made. -func (bq *BoringQuery) Clone() *BoringQuery { - return &BoringQuery{ - config: bq.config, - limit: bq.limit, - offset: bq.offset, - order: append([]Order{}, bq.order...), - unique: append([]string{}, bq.unique...), - predicates: append([]predicate.Boring{}, bq.predicates...), - // clone intermediate queries. - sql: bq.sql.Clone(), - gremlin: bq.gremlin.Clone(), - } -} - -// GroupBy used to group vertices by one or more fields/columns. -// It is often used with aggregate functions, like: count, max, mean, min, sum. -func (bq *BoringQuery) GroupBy(field string, fields ...string) *BoringGroupBy { - group := &BoringGroupBy{config: bq.config} - group.fields = append([]string{field}, fields...) - switch bq.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - group.sql = bq.sqlQuery() - case dialect.Neptune: - group.gremlin = bq.gremlinQuery() - } - return group -} - -func (bq *BoringQuery) sqlAll(ctx context.Context) ([]*Boring, error) { - rows := &sql.Rows{} - selector := bq.sqlQuery() - if unique := bq.unique; len(unique) == 0 { - selector.Distinct() - } - query, args := selector.Query() - if err := bq.driver.Query(ctx, query, args, rows); err != nil { - return nil, err - } - defer rows.Close() - var bs Borings - if err := bs.FromRows(rows); err != nil { - return nil, err - } - bs.config(bq.config) - return bs, nil -} - -func (bq *BoringQuery) sqlCount(ctx context.Context) (int, error) { - rows := &sql.Rows{} - selector := bq.sqlQuery() - unique := []string{boring.FieldID} - if len(bq.unique) > 0 { - unique = bq.unique - } - selector.Count(sql.Distinct(selector.Columns(unique...)...)) - query, args := selector.Query() - if err := bq.driver.Query(ctx, query, args, rows); err != nil { - return 0, err - } - defer rows.Close() - if !rows.Next() { - return 0, errors.New("ent: no rows found") - } - var n int - if err := rows.Scan(&n); err != nil { - return 0, fmt.Errorf("ent: failed reading count: %v", err) - } - return n, nil -} - -func (bq *BoringQuery) sqlExist(ctx context.Context) (bool, error) { - n, err := bq.sqlCount(ctx) - if err != nil { - return false, fmt.Errorf("ent: check existence: %v", err) - } - return n > 0, nil -} - -func (bq *BoringQuery) sqlIDs(ctx context.Context) ([]string, error) { - vs, err := bq.sqlAll(ctx) - if err != nil { - return nil, err - } - var ids []string - for _, v := range vs { - ids = append(ids, v.ID) - } - return ids, nil -} - -func (bq *BoringQuery) sqlQuery() *sql.Selector { - t1 := sql.Table(boring.Table) - selector := sql.Select(t1.Columns(boring.Columns...)...).From(t1) - if bq.sql != nil { - selector = bq.sql - selector.Select(selector.Columns(boring.Columns...)...) - } - for _, p := range bq.predicates { - p(selector) - } - for _, p := range bq.order { - p(selector) - } - if offset := bq.offset; offset != nil { - // limit is mandatory for offset clause. We start - // with default value, and override it below if needed. - selector.Offset(*offset).Limit(math.MaxInt64) - } - if limit := bq.limit; limit != nil { - selector.Limit(*limit) - } - return selector -} - -func (bq *BoringQuery) gremlinIDs(ctx context.Context) ([]string, error) { - res := &gremlin.Response{} - query, bindings := bq.gremlinQuery().Query() - if err := bq.driver.Exec(ctx, query, bindings, res); err != nil { - return nil, err - } - vertices, err := res.ReadVertices() - if err != nil { - return nil, err - } - ids := make([]string, 0, len(vertices)) - for _, vertex := range vertices { - ids = append(ids, vertex.ID.(string)) - } - return ids, nil -} - -func (bq *BoringQuery) gremlinAll(ctx context.Context) ([]*Boring, error) { - res := &gremlin.Response{} - query, bindings := bq.gremlinQuery().ValueMap(true).Query() - if err := bq.driver.Exec(ctx, query, bindings, res); err != nil { - return nil, err - } - var bs Borings - if err := bs.FromResponse(res); err != nil { - return nil, err - } - bs.config(bq.config) - return bs, nil -} - -func (bq *BoringQuery) gremlinCount(ctx context.Context) (int, error) { - res := &gremlin.Response{} - query, bindings := bq.gremlinQuery().Count().Query() - if err := bq.driver.Exec(ctx, query, bindings, res); err != nil { - return 0, err - } - return res.ReadInt() -} - -func (bq *BoringQuery) gremlinExist(ctx context.Context) (bool, error) { - res := &gremlin.Response{} - query, bindings := bq.gremlinQuery().HasNext().Query() - if err := bq.driver.Exec(ctx, query, bindings, res); err != nil { - return false, err - } - return res.ReadBool() -} - -func (bq *BoringQuery) gremlinQuery() *dsl.Traversal { - v := g.V().HasLabel(boring.Label) - if bq.gremlin != nil { - v = bq.gremlin.Clone() - } - for _, p := range bq.predicates { - p(v) - } - if len(bq.order) > 0 { - v.Order() - for _, p := range bq.order { - p(v) - } - } - switch limit, offset := bq.limit, bq.offset; { - case limit != nil && offset != nil: - v.Range(*offset, *offset+*limit) - case offset != nil: - v.Range(*offset, math.MaxInt64) - case limit != nil: - v.Limit(*limit) - } - if unique := bq.unique; len(unique) == 0 { - v.Dedup() - } - return v -} - -// BoringQuery is the builder for group-by Boring entities. -type BoringGroupBy struct { - config - fields []string - fns []Aggregate - // intermediate queries. - sql *sql.Selector - gremlin *dsl.Traversal -} - -// Aggregate adds the given aggregation functions to the group-by query. -func (bgb *BoringGroupBy) Aggregate(fns ...Aggregate) *BoringGroupBy { - bgb.fns = append(bgb.fns, fns...) - return bgb -} - -// Scan applies the group-by query and scan the result into the given value. -func (bgb *BoringGroupBy) Scan(ctx context.Context, v interface{}) error { - switch bgb.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bgb.sqlScan(ctx, v) - case dialect.Neptune: - return bgb.gremlinScan(ctx, v) - default: - return errors.New("bgb: unsupported dialect") - } -} - -// ScanX is like Scan, but panics if an error occurs. -func (bgb *BoringGroupBy) ScanX(ctx context.Context, v interface{}) { - if err := bgb.Scan(ctx, v); err != nil { - panic(err) - } -} - -// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field. -func (bgb *BoringGroupBy) Strings(ctx context.Context) ([]string, error) { - if len(bgb.fields) > 1 { - return nil, errors.New("ent: BoringGroupBy.Strings is not achievable when grouping more than 1 field") - } - var v []string - if err := bgb.Scan(ctx, &v); err != nil { - return nil, err - } - return v, nil -} - -// StringsX is like Strings, but panics if an error occurs. -func (bgb *BoringGroupBy) StringsX(ctx context.Context) []string { - v, err := bgb.Strings(ctx) - if err != nil { - panic(err) - } - return v -} - -// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field. -func (bgb *BoringGroupBy) Ints(ctx context.Context) ([]int, error) { - if len(bgb.fields) > 1 { - return nil, errors.New("ent: BoringGroupBy.Ints is not achievable when grouping more than 1 field") - } - var v []int - if err := bgb.Scan(ctx, &v); err != nil { - return nil, err - } - return v, nil -} - -// IntsX is like Ints, but panics if an error occurs. -func (bgb *BoringGroupBy) IntsX(ctx context.Context) []int { - v, err := bgb.Ints(ctx) - if err != nil { - panic(err) - } - return v -} - -// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field. -func (bgb *BoringGroupBy) Float64s(ctx context.Context) ([]float64, error) { - if len(bgb.fields) > 1 { - return nil, errors.New("ent: BoringGroupBy.Float64s is not achievable when grouping more than 1 field") - } - var v []float64 - if err := bgb.Scan(ctx, &v); err != nil { - return nil, err - } - return v, nil -} - -// Float64sX is like Float64s, but panics if an error occurs. -func (bgb *BoringGroupBy) Float64sX(ctx context.Context) []float64 { - v, err := bgb.Float64s(ctx) - if err != nil { - panic(err) - } - return v -} - -// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field. -func (bgb *BoringGroupBy) Bools(ctx context.Context) ([]bool, error) { - if len(bgb.fields) > 1 { - return nil, errors.New("ent: BoringGroupBy.Bools is not achievable when grouping more than 1 field") - } - var v []bool - if err := bgb.Scan(ctx, &v); err != nil { - return nil, err - } - return v, nil -} - -// BoolsX is like Bools, but panics if an error occurs. -func (bgb *BoringGroupBy) BoolsX(ctx context.Context) []bool { - v, err := bgb.Bools(ctx) - if err != nil { - panic(err) - } - return v -} - -func (bgb *BoringGroupBy) sqlScan(ctx context.Context, v interface{}) error { - rows := &sql.Rows{} - query, args := bgb.sqlQuery().Query() - if err := bgb.driver.Query(ctx, query, args, rows); err != nil { - return err - } - defer rows.Close() - return sql.ScanSlice(rows, v) -} - -func (bgb *BoringGroupBy) sqlQuery() *sql.Selector { - selector := bgb.sql - columns := make([]string, 0, len(bgb.fields)+len(bgb.fns)) - columns = append(columns, bgb.fields...) - for _, fn := range bgb.fns { - columns = append(columns, fn.SQL(selector)) - } - return selector.Select(columns...).GroupBy(bgb.fields...) -} - -func (bgb *BoringGroupBy) gremlinScan(ctx context.Context, v interface{}) error { - res := &gremlin.Response{} - query, bindings := bgb.gremlinQuery().Query() - if err := bgb.driver.Exec(ctx, query, bindings, res); err != nil { - return err - } - if len(bgb.fields)+len(bgb.fns) == 1 { - return res.ReadVal(v) - } - vm, err := res.ReadValueMap() - if err != nil { - return err - } - return vm.Decode(v) -} - -func (bgb *BoringGroupBy) gremlinQuery() *dsl.Traversal { - var ( - trs []interface{} - names []interface{} - ) - for _, fn := range bgb.fns { - name, tr := fn.Gremlin("p", "") - trs = append(trs, tr) - names = append(names, name) - } - for _, f := range bgb.fields { - names = append(names, f) - trs = append(trs, __.As("p").Unfold().Values(f).As(f)) - } - return bgb.gremlin.Group(). - By(__.Values(bgb.fields...).Fold()). - By(__.Fold().Match(trs...).Select(names...)). - Select(dsl.Values). - Next() -} diff --git a/entc/integration/plugin/ent/boring_update.go b/entc/integration/plugin/ent/boring_update.go deleted file mode 100644 index f244620cd..000000000 --- a/entc/integration/plugin/ent/boring_update.go +++ /dev/null @@ -1,224 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "errors" - "fmt" - - "fbc/ent/entc/integration/plugin/ent/boring" - "fbc/ent/entc/integration/plugin/ent/predicate" - - "fbc/ent/dialect" - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/g" - "fbc/ent/dialect/sql" -) - -// BoringUpdate is the builder for updating Boring entities. -type BoringUpdate struct { - config - predicates []predicate.Boring -} - -// Where adds a new predicate for the builder. -func (bu *BoringUpdate) Where(ps ...predicate.Boring) *BoringUpdate { - bu.predicates = append(bu.predicates, ps...) - return bu -} - -// Save executes the query and returns the number of rows/vertices matched by this operation. -func (bu *BoringUpdate) Save(ctx context.Context) (int, error) { - switch bu.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return bu.sqlSave(ctx) - case dialect.Neptune: - return bu.gremlinSave(ctx) - default: - return 0, errors.New("ent: unsupported dialect") - } -} - -// SaveX is like Save, but panics if an error occurs. -func (bu *BoringUpdate) SaveX(ctx context.Context) int { - affected, err := bu.Save(ctx) - if err != nil { - panic(err) - } - return affected -} - -// Exec executes the query. -func (bu *BoringUpdate) Exec(ctx context.Context) error { - _, err := bu.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (bu *BoringUpdate) ExecX(ctx context.Context) { - if err := bu.Exec(ctx); err != nil { - panic(err) - } -} - -func (bu *BoringUpdate) sqlSave(ctx context.Context) (n int, err error) { - selector := sql.Select(boring.FieldID).From(sql.Table(boring.Table)) - for _, p := range bu.predicates { - p(selector) - } - rows := &sql.Rows{} - query, args := selector.Query() - if err = bu.driver.Query(ctx, query, args, rows); err != nil { - return 0, err - } - defer rows.Close() - var ids []int - for rows.Next() { - var id int - if err := rows.Scan(&id); err != nil { - return 0, fmt.Errorf("ent: failed reading id: %v", err) - } - ids = append(ids, id) - } - if len(ids) == 0 { - return 0, nil - } - - tx, err := bu.driver.Tx(ctx) - if err != nil { - return 0, err - } - if err = tx.Commit(); err != nil { - return 0, err - } - return len(ids), nil -} - -func (bu *BoringUpdate) gremlinSave(ctx context.Context) (int, error) { - res := &gremlin.Response{} - query, bindings := bu.gremlin().Query() - if err := bu.driver.Exec(ctx, query, bindings, res); err != nil { - return 0, err - } - if err, ok := isConstantError(res); ok { - return 0, err - } - return res.ReadInt() -} - -func (bu *BoringUpdate) gremlin() *dsl.Traversal { - v := g.V().HasLabel(boring.Label) - for _, p := range bu.predicates { - p(v) - } - var ( - trs []*dsl.Traversal - ) - v.Count() - trs = append(trs, v) - return dsl.Join(trs...) -} - -// BoringUpdateOne is the builder for updating a single Boring entity. -type BoringUpdateOne struct { - config - id string -} - -// Save executes the query and returns the updated entity. -func (buo *BoringUpdateOne) Save(ctx context.Context) (*Boring, error) { - switch buo.driver.Dialect() { - case dialect.MySQL, dialect.SQLite: - return buo.sqlSave(ctx) - case dialect.Neptune: - return buo.gremlinSave(ctx) - default: - return nil, errors.New("ent: unsupported dialect") - } -} - -// SaveX is like Save, but panics if an error occurs. -func (buo *BoringUpdateOne) SaveX(ctx context.Context) *Boring { - b, err := buo.Save(ctx) - if err != nil { - panic(err) - } - return b -} - -// Exec executes the query on the entity. -func (buo *BoringUpdateOne) Exec(ctx context.Context) error { - _, err := buo.Save(ctx) - return err -} - -// ExecX is like Exec, but panics if an error occurs. -func (buo *BoringUpdateOne) ExecX(ctx context.Context) { - if err := buo.Exec(ctx); err != nil { - panic(err) - } -} - -func (buo *BoringUpdateOne) sqlSave(ctx context.Context) (b *Boring, err error) { - selector := sql.Select(boring.Columns...).From(sql.Table(boring.Table)) - boring.ID(buo.id)(selector) - rows := &sql.Rows{} - query, args := selector.Query() - if err = buo.driver.Query(ctx, query, args, rows); err != nil { - return nil, err - } - defer rows.Close() - var ids []int - for rows.Next() { - var id int - b = &Boring{config: buo.config} - if err := b.FromRows(rows); err != nil { - return nil, fmt.Errorf("ent: failed scanning row into Boring: %v", err) - } - id = b.id() - ids = append(ids, id) - } - switch n := len(ids); { - case n == 0: - return nil, fmt.Errorf("ent: Boring not found with id: %v", buo.id) - case n > 1: - return nil, fmt.Errorf("ent: more than one Boring with the same id: %v", buo.id) - } - - tx, err := buo.driver.Tx(ctx) - if err != nil { - return nil, err - } - if err = tx.Commit(); err != nil { - return nil, err - } - return b, nil -} - -func (buo *BoringUpdateOne) gremlinSave(ctx context.Context) (*Boring, error) { - res := &gremlin.Response{} - query, bindings := buo.gremlin(buo.id).Query() - if err := buo.driver.Exec(ctx, query, bindings, res); err != nil { - return nil, err - } - if err, ok := isConstantError(res); ok { - return nil, err - } - b := &Boring{config: buo.config} - if err := b.FromResponse(res); err != nil { - return nil, err - } - return b, nil -} - -func (buo *BoringUpdateOne) gremlin(id string) *dsl.Traversal { - v := g.V(id) - var ( - trs []*dsl.Traversal - ) - v.ValueMap(true) - trs = append(trs, v) - return dsl.Join(trs...) -} diff --git a/entc/integration/plugin/ent/client.go b/entc/integration/plugin/ent/client.go deleted file mode 100644 index e37385dea..000000000 --- a/entc/integration/plugin/ent/client.go +++ /dev/null @@ -1,99 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "fmt" - "log" - - "fbc/ent/entc/integration/plugin/ent/migrate" - - "fbc/ent/entc/integration/plugin/ent/boring" -) - -// Client is the client that holds all ent builders. -type Client struct { - config - // Schema is the client for creating, migrating and dropping schema. - Schema *migrate.Schema - // Boring is the client for interacting with the Boring builders. - Boring *BoringClient -} - -// NewClient creates a new client configured with the given options. -func NewClient(opts ...Option) *Client { - c := config{log: log.Println} - c.options(opts...) - return &Client{ - config: c, - Schema: migrate.NewSchema(c.driver), - Boring: NewBoringClient(c), - } -} - -// Tx returns a new transactional client. -func (c *Client) Tx(ctx context.Context) (*Tx, error) { - if _, ok := c.driver.(*txDriver); ok { - return nil, fmt.Errorf("ent: cannot start a transaction within a transaction") - } - tx, err := newTx(ctx, c.driver) - if err != nil { - return nil, fmt.Errorf("ent: starting a transaction: %v", err) - } - cfg := config{driver: tx, log: c.log, verbose: c.verbose} - return &Tx{ - config: cfg, - Boring: NewBoringClient(cfg), - }, nil -} - -// BoringClient is a client for the Boring schema. -type BoringClient struct { - config -} - -// NewBoringClient returns a client for the Boring from the given config. -func NewBoringClient(c config) *BoringClient { - return &BoringClient{config: c} -} - -// Create returns a create builder for Boring. -func (c *BoringClient) Create() *BoringCreate { - return &BoringCreate{config: c.config} -} - -// Update returns an update builder for Boring. -func (c *BoringClient) Update() *BoringUpdate { - return &BoringUpdate{config: c.config} -} - -// UpdateOne returns an update builder for the given entity. -func (c *BoringClient) UpdateOne(b *Boring) *BoringUpdateOne { - return c.UpdateOneID(b.ID) -} - -// UpdateOneID returns an update builder for the given id. -func (c *BoringClient) UpdateOneID(id string) *BoringUpdateOne { - return &BoringUpdateOne{config: c.config, id: id} -} - -// Delete returns a delete builder for Boring. -func (c *BoringClient) Delete() *BoringDelete { - return &BoringDelete{config: c.config} -} - -// DeleteOne returns a delete builder for the given entity. -func (c *BoringClient) DeleteOne(b *Boring) *BoringDeleteOne { - return c.DeleteOneID(b.ID) -} - -// DeleteOneID returns a delete builder for the given id. -func (c *BoringClient) DeleteOneID(id string) *BoringDeleteOne { - return &BoringDeleteOne{c.Delete().Where(boring.ID(id))} -} - -// Create returns a query builder for Boring. -func (c *BoringClient) Query() *BoringQuery { - return &BoringQuery{config: c.config} -} diff --git a/entc/integration/plugin/ent/config.go b/entc/integration/plugin/ent/config.go deleted file mode 100644 index 53f33835c..000000000 --- a/entc/integration/plugin/ent/config.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "fbc/ent/dialect" -) - -// Option function to configure the client. -type Option func(*config) - -// Config is the configuration for the client and its builder. -type config struct { - // driver is the driver used for execute database requests. - driver dialect.Driver - // verbose enable a verbosity logging. - verbose bool - // log used for logging on verbose mode. - log func(...interface{}) -} - -// Options applies the options on the config object. -func (c *config) options(opts ...Option) { - for _, opt := range opts { - opt(c) - } - if c.verbose { - c.driver = dialect.Debug(c.driver, c.log) - } -} - -// Verbose sets the client logging to verbose. -func Verbose() Option { - return func(c *config) { - c.verbose = true - } -} - -// Log sets the client logging to verbose. -func Log(fn func(...interface{})) Option { - return func(c *config) { - c.log = fn - } -} - -// Driver configures the client driver. -func Driver(driver dialect.Driver) Option { - return func(c *config) { - c.driver = driver - } -} diff --git a/entc/integration/plugin/ent/context.go b/entc/integration/plugin/ent/context.go deleted file mode 100644 index 7f97cb6a6..000000000 --- a/entc/integration/plugin/ent/context.go +++ /dev/null @@ -1,20 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" -) - -type contextKey struct{} - -// FromContext returns the Client stored in a context, or nil if there isn't one. -func FromContext(ctx context.Context) *Client { - c, _ := ctx.Value(contextKey{}).(*Client) - return c -} - -// NewContext returns a new context with the given Client attached. -func NewContext(parent context.Context, c *Client) context.Context { - return context.WithValue(parent, contextKey{}, c) -} diff --git a/entc/integration/plugin/ent/ent.go b/entc/integration/plugin/ent/ent.go deleted file mode 100644 index 73351a955..000000000 --- a/entc/integration/plugin/ent/ent.go +++ /dev/null @@ -1,329 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "fmt" - "strconv" - "strings" - - "fbc/ent/dialect" - "fbc/ent/dialect/gremlin" - "fbc/ent/dialect/gremlin/encoding/graphson" - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/gremlin/graph/dsl/__" - "fbc/ent/dialect/sql" -) - -// Order applies an ordering on either graph traversal or sql selector. -type Order func(interface{}) - -// OrderPerDialect construct the "order by" clause for graph traversals based on dialect type. -func OrderPerDialect(f0 func(*sql.Selector), f1 func(*dsl.Traversal)) Order { - return Order(func(v interface{}) { - switch v := v.(type) { - case *sql.Selector: - f0(v) - case *dsl.Traversal: - f1(v) - default: - panic(fmt.Sprintf("unknown type for order: %T", v)) - } - }) -} - -// Asc applies the given fields in ASC order. -func Asc(fields ...string) Order { - return OrderPerDialect( - func(s *sql.Selector) { - for _, f := range fields { - s.OrderBy(sql.Asc(f)) - } - }, - func(tr *dsl.Traversal) { - for _, f := range fields { - tr.By(f, dsl.Incr) - } - }, - ) -} - -// Desc applies the given fields in DESC order. -func Desc(fields ...string) Order { - return OrderPerDialect( - func(s *sql.Selector) { - for _, f := range fields { - s.OrderBy(sql.Desc(f)) - } - }, - func(tr *dsl.Traversal) { - for _, f := range fields { - tr.By(f, dsl.Decr) - } - }, - ) -} - -// Aggregate applies an aggregation step on the group-by traversal/selector. -type Aggregate struct { - // SQL the column wrapped with the aggregation function. - SQL func(*sql.Selector) string - // Gremlin gets two labels as parameters. The first used in the `As` step for the predicate, - // and the second is an optional name for the next predicates (or for later usage). - Gremlin func(string, string) (string, *dsl.Traversal) -} - -// As is a pseudo aggregation function for renaming another other functions with custom names. For example: -// -// GroupBy(field1, field2). -// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). -// Scan(ctx, &v) -// -func As(fn Aggregate, end string) Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.As(fn.SQL(s), end) - }, - Gremlin: func(start, _ string) (string, *dsl.Traversal) { - return fn.Gremlin(start, end) - }, - } -} - -// DefaultCountLabel is the default label name for the Count aggregation function. -// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response. -// In order to "count" 2 or more fields and avoid conflicting, use the `ent.As(ent.Count(field), "custom_name")` -// function with custom name in order to override it. -const DefaultCountLabel = "count" - -// Count applies the "count" aggregation function on each group. -func Count() Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.Count("*") - }, - Gremlin: func(start, end string) (string, *dsl.Traversal) { - if end == "" { - end = DefaultCountLabel - } - return end, __.As(start).Count(dsl.Local).As(end) - }, - } -} - -// DefaultMaxLabel is the default label name for the Max aggregation function. -// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response. -// In order to "max" 2 or more fields and avoid conflicting, use the `ent.As(ent.Max(field), "custom_name")` -// function with custom name in order to override it. -const DefaultMaxLabel = "max" - -// Max applies the "max" aggregation function on the given field of each group. -func Max(field string) Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.Max(s.C(field)) - }, - Gremlin: func(start, end string) (string, *dsl.Traversal) { - if end == "" { - end = DefaultMaxLabel - } - return end, __.As(start).Unfold().Values(field).Max().As(end) - }, - } -} - -// DefaultMeanLabel is the default label name for the Mean aggregation function. -// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response. -// In order to "mean" 2 or more fields and avoid conflicting, use the `ent.As(ent.Mean(field), "custom_name")` -// function with custom name in order to override it. -const DefaultMeanLabel = "mean" - -// Mean applies the "mean" aggregation function on the given field of each group. -func Mean(field string) Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.Avg(s.C(field)) - }, - Gremlin: func(start, end string) (string, *dsl.Traversal) { - if end == "" { - end = DefaultMeanLabel - } - return end, __.As(start).Unfold().Values(field).Mean().As(end) - }, - } -} - -// DefaultMinLabel is the default label name for the Min aggregation function. -// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response. -// In order to "min" 2 or more fields and avoid conflicting, use the `ent.As(ent.Min(field), "custom_name")` -// function with custom name in order to override it. -const DefaultMinLabel = "min" - -// Min applies the "min" aggregation function on the given field of each group. -func Min(field string) Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.Min(s.C(field)) - }, - Gremlin: func(start, end string) (string, *dsl.Traversal) { - if end == "" { - end = DefaultMinLabel - } - return end, __.As(start).Unfold().Values(field).Min().As(end) - }, - } -} - -// DefaultSumLabel is the default label name for the Sum aggregation function. -// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response. -// In order to "sum" 2 or more fields and avoid conflicting, use the `ent.As(ent.Sum(field), "custom_name")` -// function with custom name in order to override it. -const DefaultSumLabel = "sum" - -// Sum applies the "sum" aggregation function on the given field of each group. -func Sum(field string) Aggregate { - return Aggregate{ - SQL: func(s *sql.Selector) string { - return sql.Sum(s.C(field)) - }, - Gremlin: func(start, end string) (string, *dsl.Traversal) { - if end == "" { - end = DefaultSumLabel - } - return end, __.As(start).Unfold().Values(field).Sum().As(end) - }, - } -} - -// ErrNotFound returns when trying to fetch a specific entity and it was not found in the database. -type ErrNotFound struct { - label string -} - -// Error implements the error interface. -func (e *ErrNotFound) Error() string { - return fmt.Sprintf("ent: %s not found", e.label) -} - -// IsNotFound returns a boolean indicating whether the error is a not found error. -func IsNotFound(err error) bool { - _, ok := err.(*ErrNotFound) - return ok -} - -// MaskNotFound masks nor found error. -func MaskNotFound(err error) error { - if IsNotFound(err) { - return nil - } - return err -} - -// ErrNotSingular returns when trying to fetch a singular entity and more then one was found in the database. -type ErrNotSingular struct { - label string -} - -// Error implements the error interface. -func (e *ErrNotSingular) Error() string { - return fmt.Sprintf("ent: %s not singular", e.label) -} - -// IsNotSingular returns a boolean indicating whether the error is a not singular error. -func IsNotSingular(err error) bool { - _, ok := err.(*ErrNotSingular) - return ok -} - -// ErrConstraintFailed returns when trying to create/update one or more entities and -// one or more of their constraints failed. For example, violation of edge or field uniqueness. -type ErrConstraintFailed struct { - msg string - wrap error -} - -// Error implements the error interface. -func (e ErrConstraintFailed) Error() string { - return fmt.Sprintf("ent: unique constraint failed: %s", e.msg) -} - -// Unwrap implements the errors.Wrapper interface. -func (e *ErrConstraintFailed) Unwrap() error { - return e.wrap -} - -// IsConstraintFailure returns a boolean indicating whether the error is a constraint failure. -func IsConstraintFailure(err error) bool { - _, ok := err.(*ErrConstraintFailed) - return ok -} - -func isSQLConstraintError(err error) (*ErrConstraintFailed, bool) { - // Error number 1062 is ER_DUP_ENTRY in mysql, and "UNIQUE constraint failed" is SQLite prefix. - if msg := err.Error(); strings.HasPrefix(msg, "Error 1062") || strings.HasPrefix(msg, "UNIQUE constraint failed") { - return &ErrConstraintFailed{msg, err}, true - } - return nil, false -} - -// rollback calls to tx.Rollback and wraps the given error with the rollback error if occurred. -func rollback(tx dialect.Tx, err error) error { - if rerr := tx.Rollback(); rerr != nil { - err = fmt.Errorf("%s: %v", err.Error(), rerr) - } - if err, ok := isSQLConstraintError(err); ok { - return err - } - return err -} - -// Code implements the dsl.Node interface. -func (e ErrConstraintFailed) Code() (string, []interface{}) { - return strconv.Quote(e.prefix() + e.msg), nil -} - -func (e *ErrConstraintFailed) UnmarshalGraphson(b []byte) error { - var v [1]*string - if err := graphson.Unmarshal(b, &v); err != nil { - return err - } - if v[0] == nil { - return fmt.Errorf("ent: missing string value") - } - if !strings.HasPrefix(*v[0], e.prefix()) { - return fmt.Errorf("ent: invalid string for error: %s", *v[0]) - } - e.msg = strings.TrimPrefix(*v[0], e.prefix()) - return nil -} - -// prefix returns the prefix used for gremlin constants. -func (ErrConstraintFailed) prefix() string { return "Error: " } - -// NewErrUniqueField creates a constraint error for unique fields. -func NewErrUniqueField(label, field string, v interface{}) *ErrConstraintFailed { - return &ErrConstraintFailed{msg: fmt.Sprintf("field %s.%s with value: %#v", label, field, v)} -} - -// NewErrUniqueEdge creates a constraint error for unique edges. -func NewErrUniqueEdge(label, edge, id string) *ErrConstraintFailed { - return &ErrConstraintFailed{msg: fmt.Sprintf("edge %s.%s with id: %#v", label, edge, id)} -} - -// isConstantError indicates if the given response holds a gremlin constant containing an error. -func isConstantError(r *gremlin.Response) (*ErrConstraintFailed, bool) { - e := &ErrConstraintFailed{} - if err := graphson.Unmarshal(r.Result.Data, e); err != nil { - return nil, false - } - return e, true -} - -// keys returns the keys/ids from the edge map. -func keys(m map[string]struct{}) []string { - s := make([]string, 0, len(m)) - for id, _ := range m { - s = append(s, id) - } - return s -} diff --git a/entc/integration/plugin/ent/example_test.go b/entc/integration/plugin/ent/example_test.go deleted file mode 100644 index a26b040e5..000000000 --- a/entc/integration/plugin/ent/example_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - "log" - - "fbc/ent/dialect/sql" -) - -// dsn for the database. In order to run the tests locally, run the following command: -// -// ENT_INTEGRATION_ENDPOINT="root:pass@tcp(localhost:3306)/test?parseTime=True" go test -v -// -var dsn string - -func ExampleBoring() { - if dsn == "" { - return - } - ctx := context.Background() - drv, err := sql.Open("mysql", dsn) - if err != nil { - log.Fatalf("failed creating database client: %v", err) - } - defer drv.Close() - client := NewClient(Driver(drv)) - // creating vertices for the boring's edges. - - // create boring vertex with its edges. - b := client.Boring. - Create(). - SaveX(ctx) - log.Println("boring created:", b) - - // query edges. - - // Output: -} diff --git a/entc/integration/plugin/ent/migrate/migrate.go b/entc/integration/plugin/ent/migrate/migrate.go deleted file mode 100644 index 7d512335a..000000000 --- a/entc/integration/plugin/ent/migrate/migrate.go +++ /dev/null @@ -1,29 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package migrate - -import ( - "context" - "fmt" - - "fbc/ent/dialect" - "fbc/ent/dialect/sql/schema" -) - -// Schema is the API for creating, migrating and dropping a schema. -type Schema struct { - drv dialect.Driver - universalID bool -} - -// NewSchema creates a new schema client. -func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } - -// Create creates all schema resources. -func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { - migrate, err := schema.NewMigrate(s.drv, opts...) - if err != nil { - return fmt.Errorf("ent/migrate: %v", err) - } - return migrate.Create(ctx, Tables...) -} diff --git a/entc/integration/plugin/ent/migrate/schema.go b/entc/integration/plugin/ent/migrate/schema.go deleted file mode 100644 index 04f067e78..000000000 --- a/entc/integration/plugin/ent/migrate/schema.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package migrate - -import ( - "fbc/ent/dialect/sql/schema" - "fbc/ent/field" -) - -var ( - nullable = true - // BoringsColumns holds the columns for the "borings" table. - BoringsColumns = []*schema.Column{ - {Name: "id", Type: field.TypeInt, Increment: true}, - } - // BoringsTable holds the schema information for the "borings" table. - BoringsTable = &schema.Table{ - Name: "borings", - Columns: BoringsColumns, - PrimaryKey: []*schema.Column{BoringsColumns[0]}, - ForeignKeys: []*schema.ForeignKey{}, - } - // Tables holds all the tables in the schema. - Tables = []*schema.Table{ - BoringsTable, - } -) - -func init() { -} diff --git a/entc/integration/plugin/ent/predicate/predicate.go b/entc/integration/plugin/ent/predicate/predicate.go deleted file mode 100644 index 3157d13a9..000000000 --- a/entc/integration/plugin/ent/predicate/predicate.go +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package predicate - -import ( - "fmt" - - "fbc/ent/dialect/gremlin/graph/dsl" - "fbc/ent/dialect/sql" -) - -// Boring is the predicate function for boring builders. -type Boring func(interface{}) - -// BoringPerDialect construct a predicate for graph traversals based on dialect type. -func BoringPerDialect(f0 func(*sql.Selector), f1 func(*dsl.Traversal)) Boring { - return Boring(func(v interface{}) { - switch v := v.(type) { - case *sql.Selector: - f0(v) - case *dsl.Traversal: - f1(v) - default: - panic(fmt.Sprintf("unknown type for predicate: %T", v)) - } - }) -} diff --git a/entc/integration/plugin/ent/schema/boring.go b/entc/integration/plugin/ent/schema/boring.go deleted file mode 100644 index 63ffbecc0..000000000 --- a/entc/integration/plugin/ent/schema/boring.go +++ /dev/null @@ -1,18 +0,0 @@ -package schema - -import "fbc/ent" - -// Boring holds the schema definition for the Boring entity. -type Boring struct { - ent.Schema -} - -// Fields of the Boring. -func (Boring) Fields() []ent.Field { - return nil -} - -// Edges of the Boring. -func (Boring) Edges() []ent.Edge { - return nil -} diff --git a/entc/integration/plugin/ent/tx.go b/entc/integration/plugin/ent/tx.go deleted file mode 100644 index b9972b8a4..000000000 --- a/entc/integration/plugin/ent/tx.go +++ /dev/null @@ -1,93 +0,0 @@ -// Code generated (@generated) by entc, DO NOT EDIT. - -package ent - -import ( - "context" - - "fbc/ent/dialect" - "fbc/ent/entc/integration/plugin/ent/migrate" -) - -// Tx is a transactional client that is created by calling Client.Tx(). -type Tx struct { - config - // Boring is the client for interacting with the Boring builders. - Boring *BoringClient -} - -// Commit commits the transaction. -func (tx *Tx) Commit() error { - return tx.config.driver.(*txDriver).tx.Commit() -} - -// Rollback rollbacks the transaction. -func (tx *Tx) Rollback() error { - return tx.config.driver.(*txDriver).tx.Rollback() -} - -// Client returns a Client that binds to current transaction. -func (tx *Tx) Client() *Client { - return &Client{ - config: tx.config, - Schema: migrate.NewSchema(tx.driver), - Boring: NewBoringClient(tx.config), - } -} - -// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. -// The idea is to support transactions without adding any extra code to the builders. -// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance. -// Commit and Rollback are nop for the internal builders and the user must call one -// of them in order to commit or rollback the transaction. -// -// If a closed transaction is embedded in one of the generated entities, and the entity -// applies a query, for example: Boring.QueryXXX(), the query will be executed -// through the driver which created this transaction. -// -// Note that txDriver is not goroutine safe. -type txDriver struct { - // the driver we started the transaction from. - drv dialect.Driver - // tx is the underlying transaction. - tx dialect.Tx -} - -// newTx creates a new transactional driver. -func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) { - tx, err := drv.Tx(ctx) - if err != nil { - return nil, err - } - return &txDriver{tx: tx, drv: drv}, nil -} - -// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls -// from the internal builders. Should be called only by the internal builders. -func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil } - -// Dialect returns the dialect of the driver we started the transaction from. -func (tx *txDriver) Dialect() string { return tx.drv.Dialect() } - -// Close is a nop close. -func (*txDriver) Close() error { return nil } - -// Commit is a nop commit for the internal builders. -// User must call `Tx.Commit` in order to commit the transaction. -func (*txDriver) Commit() error { return nil } - -// Rollback is a nop rollback for the internal builders. -// User must call `Tx.Rollback` in order to rollback the transaction. -func (*txDriver) Rollback() error { return nil } - -// Exec calls tx.Exec. -func (tx *txDriver) Exec(ctx context.Context, query string, args interface{}, v interface{}) error { - return tx.tx.Exec(ctx, query, args, v) -} - -// Query calls tx.Query. -func (tx *txDriver) Query(ctx context.Context, query string, args interface{}, v interface{}) error { - return tx.tx.Query(ctx, query, args, v) -} - -var _ dialect.Driver = (*txDriver)(nil) diff --git a/entc/integration/plugin/plugin_test.go b/entc/integration/plugin/plugin_test.go deleted file mode 100644 index f9e40ecd0..000000000 --- a/entc/integration/plugin/plugin_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package plugin - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestPlugin(t *testing.T) { - plg := "printer.so" - - // build entc plugin. - cmd := exec.Command("go", "build", "-o", plg, "-buildmode", "plugin", "./testdata") - _, err := run(cmd) - require.NoError(t, err) - defer os.Remove(plg) - - // execute entc generate and expect the plugin to be executed. - cmd = exec.Command("go", "run", "../../cmd/entc/entc.go", "generate", "--storage", "sql,gremlin", "--plugin", plg, "./ent/schema") - out, err := run(cmd) - require.NoError(t, err) - require.Equal(t, "Boring\n", out, "printer plugin should print node names") - -} - -func run(cmd *exec.Cmd) (string, error) { - out := bytes.NewBuffer(nil) - cmd.Stderr = out - cmd.Stdout = out - if err := cmd.Run(); err != nil { - return "", fmt.Errorf("integration/plugin: %s", out) - } - return out.String(), nil -} diff --git a/entc/integration/plugin/testdata/printer.go b/entc/integration/plugin/testdata/printer.go deleted file mode 100644 index 07971df91..000000000 --- a/entc/integration/plugin/testdata/printer.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "fmt" - - "fbc/ent/entc/gen" - "fbc/ent/entc/plugin" -) - -// Gen is the required plugin symbol. -var Gen = plugin.GeneratorFunc(func(graph *gen.Graph) error { - for _, n := range graph.Nodes { - fmt.Println(n.Name) - } - return nil -}) diff --git a/entc/internal/build/bindata.go b/entc/internal/build/bindata.go deleted file mode 100644 index 4f9fc56e8..000000000 --- a/entc/internal/build/bindata.go +++ /dev/null @@ -1,235 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. -// sources: -// template/build.tmpl -package build - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _templateBuildTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8f\x41\x4b\x03\x31\x10\x85\xcf\x3b\xbf\xe2\x11\x2a\x28\xb4\xe9\xbd\xd0\x9b\x67\x11\x7a\x14\x91\x74\x3b\xbb\x0d\x36\xd9\x75\x93\x8a\x32\xcc\x7f\x97\x64\xd7\x1e\x7a\x0a\x33\x79\xf9\xbe\x3c\x11\x9c\xb8\xf3\x91\x61\x82\xf3\xd1\x40\x95\x46\xd7\x7e\xba\x9e\x51\x16\x44\x3e\x8c\xc3\x94\xf1\x48\x8d\x08\xc6\xc9\xc7\xdc\xc1\x3c\x7c\x19\xd8\xd7\x25\xa7\x4a\xd4\x98\xee\xd8\x6e\x39\x66\x43\x4f\x44\x22\x58\x1d\x5d\x62\xec\xf6\xa8\xe7\x7f\xb6\x44\xb7\x5b\x88\xc0\x1e\x7e\xc3\x71\xb8\x40\x15\x3e\x21\x9f\x19\xe3\xe5\xda\xfb\x88\x6e\x98\xc0\x3f\xc5\xe9\x63\x5f\x2f\xee\xbc\x33\x59\x15\xa9\x3d\x73\x70\x69\x7e\x11\x73\x6b\xe9\xdb\x4d\x77\xec\x3d\xde\xde\x39\x66\x7b\xa8\x59\xa9\x25\x26\x17\x7b\xc6\xea\x63\x8d\x55\x74\xa1\x7e\xd2\xbe\xb8\xc0\xa9\x34\x69\x1a\x91\xcd\xcd\x61\xcb\x70\x93\x27\x99\x31\xbb\xa2\xb3\xcf\xdc\xb9\xeb\x25\xcf\x2b\x35\x0b\x4c\x75\x5d\x25\x1c\x4f\xd8\xa8\x92\xd2\x32\xa8\xfe\x05\x00\x00\xff\xff\x80\xe4\x62\x1c\x6c\x01\x00\x00") - -func templateBuildTmplBytes() ([]byte, error) { - return bindataRead( - _templateBuildTmpl, - "template/build.tmpl", - ) -} - -func templateBuildTmpl() (*asset, error) { - bytes, err := templateBuildTmplBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "template/build.tmpl", size: 364, mode: os.FileMode(420), modTime: time.Unix(1562857148, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "template/build.tmpl": templateBuildTmpl, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "template": &bintree{nil, map[string]*bintree{ - "build.tmpl": &bintree{templateBuildTmpl, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/entc/internal/build/build_test.go b/entc/internal/build/build_test.go deleted file mode 100644 index 7e3eff5f6..000000000 --- a/entc/internal/build/build_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package build - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBuild(t *testing.T) { - cfg := &Config{Path: "./testdata/valid"} - plg, err := cfg.Build() - require.NoError(t, err) - schemas, err := plg.Load() - require.NoError(t, err) - require.Len(t, schemas, 3) - require.Equal(t, "fbc/ent/entc/internal/build/testdata/valid", plg.PkgPath) -} - -func TestBuildWrongPath(t *testing.T) { - cfg := &Config{Path: "./boring"} - plg, err := cfg.Build() - require.Error(t, err) - require.Nil(t, plg) -} - -func TestBuildSpecific(t *testing.T) { - cfg := &Config{Path: "./testdata/valid", Names: []string{"User"}} - plg, err := cfg.Build() - require.NoError(t, err) - schemas, err := plg.Load() - require.NoError(t, err) - require.Len(t, schemas, 1) -} - -func TestBuildNoSchema(t *testing.T) { - cfg := &Config{Path: "./testdata/invalid"} - plg, err := cfg.Build() - require.Error(t, err) - require.Nil(t, plg) -} diff --git a/entc/internal/build/template/build.tmpl b/entc/internal/build/template/build.tmpl deleted file mode 100644 index 369482f01..000000000 --- a/entc/internal/build/template/build.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -{{ define "main" }} -package main - -import ( - {{ printf "%q" .Package }} - - "fbc/ent" -) - -{{ $base := base .Package}} - -// {{ .Symbol }} is the plugin for exporting the {{ printf "%q" $base }} schemas for entc. -var {{ .Symbol }} = []ent.Schema{ - {{ range $_, $name := .Names }} - {{- $base }}.{{- printf "%s{Schema: ent.DefaultSchema}" $name }}, - {{ end -}} -} -{{ end }} \ No newline at end of file diff --git a/entc/internal/build/testdata/valid/schema.go b/entc/internal/build/testdata/valid/schema.go deleted file mode 100644 index 57c778047..000000000 --- a/entc/internal/build/testdata/valid/schema.go +++ /dev/null @@ -1,18 +0,0 @@ -package valid - -import "fbc/ent" - -// User holds the user schema. -type User struct { - ent.Schema -} - -// Group holds the group schema. -type Group struct { - ent.Schema -} - -// Tag holds the tag schema. -type Tag struct { - ent.Schema -} diff --git a/entc/load/bindata.go b/entc/load/bindata.go new file mode 100644 index 000000000..44f05651f --- /dev/null +++ b/entc/load/bindata.go @@ -0,0 +1,258 @@ +// Code generated by go-bindata. (@generated) DO NOT EDIT. +// sources: +// template/main.tmpl +// schema.go +package load + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _templateMainTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x92\x41\x8b\xdb\x30\x10\x85\xcf\x9a\x5f\x31\x15\x29\x2b\x81\x57\x4b\xaf\x0b\x39\x35\x39\xb4\xd0\xdd\x42\x16\x7a\x48\xc3\x22\xc7\xe3\xac\xa8\x2d\xbb\x92\x52\xba\x08\xfd\xf7\x22\xc9\x0e\xf4\x94\x8c\xe6\xe9\x7b\x6f\xc6\x8a\x11\x3b\xea\x8d\x25\xe4\xa3\x36\x96\x63\x4a\xf0\xf0\x80\x9f\xa7\x8e\xf0\x42\x96\x9c\x0e\xd4\x61\xfb\x8e\x77\x64\xc3\xf9\x76\x74\xa7\x70\xf7\x8c\x4f\xcf\x2f\xb8\xdf\x7d\x79\x51\x30\xeb\xf3\x2f\x7d\x21\xcc\x0c\x00\x33\xce\x93\x0b\x28\x80\xf1\xc9\x73\x60\xbc\x7d\x0f\x94\xff\xc4\x88\x81\xc6\x79\xd0\x81\x90\x57\x95\x2f\x96\xa5\x35\x3b\x63\x43\x8f\xfc\xe3\x6f\x8e\xea\xfb\x42\x4c\x09\x24\x40\x8c\xb8\x69\xb5\x27\x7c\xdc\x62\xf9\x5d\xfb\xf9\xee\x1f\xed\xd0\x9f\xdf\x68\xd4\x1e\xb7\x78\x3c\x91\x0d\xea\x50\xea\x58\xb8\x4e\xdb\x0b\xe1\xe6\xb5\xc1\x8d\xd5\x63\x61\xa8\x27\x3d\x92\xcf\x70\xc6\x62\xbc\x5f\xe0\x29\xa9\x5c\xdc\x72\xf8\x58\x31\x8f\x98\x91\x3b\xea\xf5\x75\x08\xf5\x28\xf1\x05\x96\x52\x53\x4c\xc8\x76\x78\x9f\x12\x24\x80\xfe\x6a\xcf\x65\x13\x42\x62\x04\x96\xe3\x0d\xc6\x92\xc7\xe3\xe9\x78\xca\xab\x00\xd6\x4f\x0e\x5f\x9b\x25\x75\x0e\x54\x33\xae\x53\x44\x60\xac\x6d\x90\x9c\xcb\xbd\x6f\xda\xf9\x37\x3d\x54\x63\x51\x35\x12\x18\x33\x7d\x51\x7c\xd8\xa2\x35\x43\xb9\xc3\x7a\x6d\x06\x41\xce\xe5\x76\x9e\xad\xfa\x6e\x51\xcf\x33\xd9\x4e\x94\xb2\xc1\x56\x42\xee\x4e\x5e\x1d\x42\x37\x5d\x83\xfa\xe1\x4c\x20\x51\xbe\x92\xfa\x3a\x19\xbb\x0a\x6b\x5c\xc1\x7f\x5a\x2e\xa5\xbc\xcd\xb6\xba\x64\xfb\xc9\x95\x21\x2b\x8b\x9c\xab\xac\x43\x70\xc6\x5e\xb2\x46\xed\xb3\x46\x48\x59\x34\xfb\xbf\x26\x88\x4f\x85\xf4\xdf\x5b\xa8\x43\xd5\xa7\xb0\x2c\x33\x25\xf8\x17\x00\x00\xff\xff\x0b\x99\x2b\x2f\x9d\x02\x00\x00") + +func templateMainTmplBytes() ([]byte, error) { + return bindataRead( + _templateMainTmpl, + "template/main.tmpl", + ) +} + +func templateMainTmpl() (*asset, error) { + bytes, err := templateMainTmplBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "template/main.tmpl", size: 669, mode: os.FileMode(420), modTime: time.Unix(1564567133, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _schemaGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x56\x51\x6f\xdb\x36\x10\x7e\x16\x7f\xc5\xcd\x0f\x81\x14\x18\xca\xbb\x0a\x3f\x0c\x5b\x87\x75\xc3\x92\xa1\xd9\xf6\x52\x14\x2d\x2d\x1d\x1d\xb6\x32\xe5\x92\x54\x8b\x34\xf0\x7f\x1f\xee\x78\xb4\x25\xdb\x4d\x81\xe6\x25\xe4\x7d\xdf\xdd\xf1\xee\x3e\x91\xde\xe9\xf6\xa3\xde\x20\xf4\x83\xee\x94\xb2\xdb\xdd\xe0\x23\x94\xaa\x58\xa0\x6b\x87\xce\xba\xcd\xcd\x87\x30\xb8\x85\x2a\x16\x1e\x4d\x8f\x6d\x5c\x28\x55\x2c\xcc\xba\xbd\x41\x17\x17\xc7\xe5\x8d\xb1\xd8\x77\x0b\x55\x29\x75\x73\x03\xf7\xed\x03\x6e\x35\x78\xdc\x79\x0c\xe8\x62\x00\xed\x00\x5d\xac\x05\x88\x0f\x3a\xc2\x17\x1d\x38\x2d\x76\x60\xfc\xb0\x05\x0d\xed\xb0\xdd\xf5\x16\x3b\x18\x03\x7a\x90\xa3\xd5\x2a\x3e\xee\x30\x87\x0c\xd1\x8f\x6d\x84\x27\x55\xdc\xea\x2d\x02\x90\xc1\xba\x0d\x00\xbc\xa7\x83\x36\x0b\xa7\xb7\xb8\x1c\xb6\x36\xe2\x76\x17\x1f\x17\xef\x55\xf1\xb2\xdb\x60\x00\x78\xf3\xf6\x9a\x56\x07\x22\x92\x79\xce\xfc\x8d\x6a\x08\xc4\xe4\x55\x66\x72\x69\x73\xea\x9e\xcb\x4c\xac\xf3\x2a\x93\xfd\x07\x8a\x4c\x8e\x67\x35\xc2\xb4\xce\x67\x2a\xfd\x87\x62\xa4\x3f\x3e\x73\xcd\x06\xa1\x53\x82\x13\xba\xde\xc0\x37\xa3\x47\xbd\x99\xb3\xef\xed\xd7\x1c\xfc\xda\xba\x28\x4b\x61\x07\xfb\xf5\x24\xf8\x2f\x0f\xda\x07\x64\xda\xf5\x31\xba\xd0\xdb\x04\xce\x3d\xfe\x75\xf6\xd3\x98\x52\xac\x87\xa1\x9f\x27\x18\x19\x9c\x3b\xdc\x8e\x7d\xaf\xd7\x3d\x5e\x74\x70\x02\xce\x5d\xee\x76\xd1\x0e\x4e\xf7\x17\x5d\x06\x01\xe7\x2e\xbf\xa2\xd1\x63\x1f\x2f\x1f\xab\x4b\xe0\xdc\xe3\x3f\xdd\xdb\x4e\xc7\xc1\x07\x38\x36\x2a\x7b\x7c\x3e\x80\x17\x04\xc5\x02\x3d\xd7\x13\x9b\x7f\x40\x4e\xec\x77\x41\x4d\x32\x8f\xef\xeb\x68\x4e\x7c\x46\x41\x27\xc4\x53\xed\xbc\x46\x93\x92\xcf\x79\x1e\xcd\xbb\xf3\xec\xaf\xd1\x88\xcc\x66\xdf\xab\x47\xf3\x0d\xbd\xc8\x58\x9e\x51\xca\x2b\xf7\x19\x7d\xc0\x53\xaa\x4d\xe6\xd3\xf4\x9f\x46\xeb\xb1\x3b\xe1\x7a\x31\x5f\x98\xda\x2d\x7e\xe1\x93\xb6\x1e\x75\x44\x9e\x9a\xcc\x88\xee\x98\x34\xa8\x90\xae\x2f\xeb\x22\x7a\xa3\x5b\xac\x95\x19\x5d\x9b\x5d\x4b\x3c\xcc\xb9\x92\xb2\x9f\x54\xe1\x10\x9a\x15\x5c\xd1\xf6\x49\x15\x3c\xbe\x86\x1b\x83\x35\xad\xcb\x6a\xa9\x0a\x9e\x55\xb6\xd2\x5a\xac\x7a\x93\x8c\x64\xd5\x9b\x64\x94\x29\x34\x64\x94\x75\x02\x52\x27\x1b\x66\xbf\x0a\x69\x97\x10\x69\x5c\x93\x10\xd9\xe5\x68\xa9\x21\x0d\x43\x79\xc7\xd8\x5e\x15\xd6\x00\x9f\x1e\xeb\x9f\x43\x18\xda\xb2\x7a\x01\x08\x3f\xad\xc0\xd9\x9e\x4a\x2b\x1c\x1f\x01\x56\xc7\x0e\x54\xec\xe7\x31\x8e\xde\x81\x43\xe9\xed\x5f\xda\x87\x07\xdd\xcb\xed\xcf\x8f\x12\x7d\x0a\x38\x7d\x4d\x0e\x4d\xa5\xd5\x00\x1a\xfe\xb8\xbf\xbb\x25\x67\xfe\x64\x5a\xed\x60\x8d\xd0\x21\xb9\x76\x89\x42\x01\xc4\x79\x58\x7f\xc0\x36\xca\x3f\x99\xca\x2c\x69\x19\x72\xee\x9c\xb0\x82\xf2\xcd\xdb\xf5\x63\xc4\x25\xa0\xf7\x83\xaf\xa8\xa2\xc0\xb3\x4a\x84\xa7\xd4\x66\xeb\x3a\xeb\xb1\x8d\xa5\x3c\x9d\x3c\x9f\x3b\x23\x01\xab\x4a\xa6\xb8\x57\x85\x19\x3c\xbc\x5b\x82\xa1\x18\x5e\x3b\xfa\x6a\x99\x93\x5e\x92\x50\x72\x86\x22\x30\x7e\xc5\x36\xda\x4f\x14\x01\x60\x26\x9a\x98\x88\x82\x80\xa3\x2c\x26\xba\x60\x20\x2b\x63\xa2\x00\xb2\xcf\x35\x70\xb8\x67\x1b\x01\xf3\x5e\xe0\x7c\xa7\x66\x38\xef\x05\x96\xfb\xb3\x49\xa1\x7f\xd7\x41\x0c\x02\x1f\x2f\xcb\x06\x7a\x74\xa5\xa9\x8f\x96\xb2\x62\xce\x5e\x15\xa4\xa7\xb0\x84\xe1\x23\x75\xc0\xd4\x65\x7a\xde\xe8\x49\xf2\xd5\x0b\x32\x73\x3f\xe8\x15\x22\x42\x60\xa4\xac\xd8\x66\x78\x03\x2b\xb8\x22\xf8\x18\xae\x3d\x0b\x27\x4f\xd6\x2c\xa4\xbc\x54\xc4\x6b\x33\xe1\x10\x38\xbf\x71\x2b\xb8\x12\x9e\x84\x0f\x32\x38\x58\x81\xde\xed\xd0\x75\x65\xb6\x2c\x21\x98\xa4\x74\x99\x39\x9e\xcd\x9c\x7f\xaf\xe4\x91\xa7\xdd\x34\x0e\x1b\x96\x93\xef\x66\xf6\xe1\xd0\x65\x55\x8b\x7e\xcb\x50\xd1\x57\xc4\x92\x3e\x68\x31\xc2\x54\x8d\xd5\x6c\x47\x29\xe9\x58\xb1\xfe\xd3\xba\xae\xac\x60\xb5\x3a\xe0\x7f\x47\xcf\x27\xa2\x6a\x63\xfd\xb2\xc7\x6d\x39\x4b\x1c\xd5\x5e\xfd\x1f\x00\x00\xff\xff\x3e\x7d\x4d\xef\x4d\x0a\x00\x00") + +func schemaGoBytes() ([]byte, error) { + return bindataRead( + _schemaGo, + "schema.go", + ) +} + +func schemaGo() (*asset, error) { + bytes, err := schemaGoBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "schema.go", size: 2637, mode: os.FileMode(420), modTime: time.Unix(1564657397, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "template/main.tmpl": templateMainTmpl, + "schema.go": schemaGo, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "schema.go": &bintree{schemaGo, map[string]*bintree{}}, + "template": &bintree{nil, map[string]*bintree{ + "main.tmpl": &bintree{templateMainTmpl, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/entc/internal/build/build.go b/entc/load/load.go similarity index 51% rename from entc/internal/build/build.go rename to entc/load/load.go index 566c8a7bc..d7cf24b1c 100644 --- a/entc/internal/build/build.go +++ b/entc/load/load.go @@ -1,17 +1,20 @@ -// Package build is the interface for loading schema package into a Go plugin. -package build +// Package load is the interface for loading schema package into a Go program. +package load import ( "bytes" + "encoding/json" "fbc/ent" "fmt" + "go/ast" "go/format" + "go/parser" + "go/token" "go/types" "io/ioutil" "os" "os/exec" "path/filepath" - "plugin" "reflect" "sort" "strings" @@ -22,32 +25,16 @@ import ( "golang.org/x/tools/go/packages" ) -// Symbol is the exported "Symbol" of the plugin. -const Symbol = "Names" - -// Plugin holds the plugin build info. -type Plugin struct { - // Path is the path for the Go plugin. - Path string +// A SchemaSpec holds an ent.schema package that created by Load. +type SchemaSpec struct { + // Schemas are the schema descriptors. + Schemas []*Schema // PkgPath is the path where the schema package reside. // Note that path can be either a package path (e.g. github.com/a8m/x) // or a filepath (e.g. ./ent/schema). PkgPath string } -// Load loads the schemas from the generated plugin. -func (p *Plugin) Load() ([]ent.Schema, error) { - plg, err := plugin.Open(p.Path) - if err != nil { - return nil, errors.WithMessagef(err, "open plugin %s", p.Path) - } - schemas, err := plg.Lookup(Symbol) - if err != nil { - return nil, errors.WithMessagef(err, "find schemas in plugin") - } - return *schemas.(*[]ent.Schema), nil -} - // Config holds the configuration for package building. type Config struct { // Path is the path for the schema package. @@ -58,7 +45,7 @@ type Config struct { } // Build loads the schemas package and build the Go plugin with this info. -func (c *Config) Build() (*Plugin, error) { +func (c *Config) Load() (*SchemaSpec, error) { pkgPath, err := c.load() if err != nil { return nil, errors.WithMessage(err, "load schemas dir") @@ -67,10 +54,10 @@ func (c *Config) Build() (*Plugin, error) { return nil, errors.Errorf("no schema found in: %s", c.Path) } b := bytes.NewBuffer(nil) - err = templates.ExecuteTemplate(b, "main", struct { + err = buildTmpl.ExecuteTemplate(b, "main", struct { *Config - Symbol, Package string - }{c, Symbol, pkgPath}) + Package string + }{c, pkgPath}) if err != nil { return nil, errors.WithMessage(err, "execute template") } @@ -83,12 +70,19 @@ func (c *Config) Build() (*Plugin, error) { return nil, errors.WithMessagef(err, "write file %s", target) } defer os.Remove(target) - plg := filepath.Join(os.TempDir(), fmt.Sprintf("%s.so", filename(pkgPath))) - cmd := exec.Command("go", "build", "-o", plg, "-buildmode", "plugin", target) - if err := run(cmd); err != nil { + out, err := run(target) + if err != nil { return nil, err } - return &Plugin{PkgPath: pkgPath, Path: plg}, nil + spec := &SchemaSpec{PkgPath: pkgPath} + for _, line := range strings.Split(out, "\n") { + schema := &Schema{} + if err := json.Unmarshal([]byte(line), schema); err != nil { + return nil, errors.WithMessagef(err, "unmarshal schema %s", line) + } + spec.Schemas = append(spec.Schemas, schema) + } + return spec, nil } // load loads the schemas info. @@ -120,16 +114,52 @@ func (c *Config) load() (string, error) { return pkg.PkgPath, err } -//go:generate go-bindata -pkg=build ./template/... +//go:generate go-bindata -pkg=load ./template/... schema.go -var templates = tmpl() +var buildTmpl = templates() -func tmpl() *template.Template { - t := template.New("templates").Funcs(template.FuncMap{"base": filepath.Base}) - for _, asset := range AssetNames() { - t = template.Must(t.Parse(string(MustAsset(asset)))) +func templates() *template.Template { + tmpl := template.New("templates").Funcs(template.FuncMap{"base": filepath.Base}) + tmpl = template.Must(tmpl.Parse(string(MustAsset("template/main.tmpl")))) + // turns the schema file and its imports into templates. + tmpls, err := schemaTemplates() + if err != nil { + panic(err) } - return t + for _, t := range tmpls { + tmpl = template.Must(tmpl.Parse(t)) + } + return tmpl +} + +// schemaTemplates returns the templates needed for loading the schema.go file. +func schemaTemplates() ([]string, error) { + const name = "schema.go" + var ( + imports []string + code bytes.Buffer + fset = token.NewFileSet() + ) + f, err := parser.ParseFile(fset, name, string(MustAsset(name)), parser.AllErrors) + if err != nil { + return nil, errors.WithMessagef(err, "parse file: %s", name) + } + for _, decl := range f.Decls { + if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT { + for _, spec := range decl.Specs { + imports = append(imports, spec.(*ast.ImportSpec).Path.Value) + } + continue + } + if err := format.Node(&code, fset, decl); err != nil { + return nil, errors.WithMessage(err, "format node") + } + code.WriteByte('\n') + } + return []string{ + fmt.Sprintf(`{{ define "schema" }} %s {{ end }}`, code.String()), + fmt.Sprintf(`{{ define "imports" }} %s {{ end }}`, strings.Join(imports, "\n")), + }, nil } func filename(pkg string) string { @@ -137,12 +167,15 @@ func filename(pkg string) string { return fmt.Sprintf("entc_%s_%d", name, time.Now().Unix()) } -// Run runs an exec command and returns the stderr if it failed. -func run(cmd *exec.Cmd) error { - out := bytes.NewBuffer(nil) - cmd.Stderr = out +// run 'go run' command and return its output. +func run(target string) (string, error) { + cmd := exec.Command("go", "run", target) + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + cmd.Stdout = stdout + cmd.Stderr = stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("entc/internal/build: %s", out) + return "", fmt.Errorf("entc/load: %s", stderr) } - return nil + return stdout.String(), nil } diff --git a/entc/load/load_test.go b/entc/load/load_test.go new file mode 100644 index 000000000..c843320ec --- /dev/null +++ b/entc/load/load_test.go @@ -0,0 +1,38 @@ +package load + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLoad(t *testing.T) { + cfg := &Config{Path: "./testdata/valid"} + spec, err := cfg.Load() + require.NoError(t, err) + require.Len(t, spec.Schemas, 3) + require.Equal(t, "fbc/ent/entc/load/testdata/valid", spec.PkgPath) +} + +func TestLoadWrongPath(t *testing.T) { + cfg := &Config{Path: "./boring"} + plg, err := cfg.Load() + require.Error(t, err) + require.Nil(t, plg) +} + +func TestLoadSpecific(t *testing.T) { + cfg := &Config{Path: "./testdata/valid", Names: []string{"User"}} + spec, err := cfg.Load() + require.NoError(t, err) + require.Len(t, spec.Schemas, 1) + require.Equal(t, "User", spec.Schemas[0].Name) + require.Equal(t, "fbc/ent/entc/load/testdata/valid", spec.PkgPath) +} + +func TestLoadNoSchema(t *testing.T) { + cfg := &Config{Path: "./testdata/invalid"} + schemas, err := cfg.Load() + require.Error(t, err) + require.Empty(t, schemas) +} diff --git a/entc/load/schema.go b/entc/load/schema.go new file mode 100644 index 000000000..f069df7bd --- /dev/null +++ b/entc/load/schema.go @@ -0,0 +1,97 @@ +package load + +import ( + "encoding/json" + "reflect" + + "fbc/ent" + "fbc/ent/field" +) + +// Schema represents an ent.Schema that was loaded from a complied user package. +type Schema struct { + Name string `json:"name,omitempty"` + Edges []*Edge `json:"edges,omitempty"` + Fields []*Field `json:"fields,omitempty"` +} + +// Field represents an ent.Field that was loaded from a complied user package. +type Field struct { + Name string `json:"name,omitempty"` + Type field.Type `json:"type,omitempty"` + Tag string `json:"tag,omitempty"` + Size *int `json:"size,omitempty"` + Charset *string `json:"charset,omitempty"` + Unique bool `json:"unique,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Optional bool `json:"optional,omitempty"` + Default bool `json:"default,omitempty"` + Validators int `json:"validators,omitempty"` +} + +// Edge represents an ent.Edge that was loaded from a complied user package. +type Edge struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Tag string `json:"tag,omitempty"` + RefName string `json:"ref_name,omitempty"` + Ref *Edge `json:"ref,omitempty"` + Unique bool `json:"unique,omitempty"` + Inverse bool `json:"inverse,omitempty"` + Required bool `json:"required,omitempty"` +} + +// NewEdge creates an loaded edge from schema interface. +func NewEdge(e ent.Edge) *Edge { + ne := &Edge{ + Name: e.Name(), + Type: e.Type(), + Tag: e.Tag(), + RefName: e.RefName(), + Unique: e.IsUnique(), + Inverse: e.IsInverse(), + Required: e.IsRequired(), + } + if e := e.Assoc(); e != nil { + ne.Ref = NewEdge(e) + } + return ne +} + +// MarshalSchema encode the ent.Schema interface into a JSON +// that can be decoded into the Schema object object. +func MarshalSchema(schema ent.Schema) ([]byte, error) { + s := &Schema{Name: indirect(reflect.TypeOf(schema)).Name()} + for _, f := range schema.Fields() { + sf := &Field{ + Name: f.Name(), + Type: f.Type(), + Tag: f.Tag(), + Unique: f.IsUnique(), + Nullable: f.IsNullable(), + Optional: f.IsOptional(), + Default: f.HasDefault(), + Validators: len(f.Validators()), + } + if s, ok := f.(field.Sizer); ok { + size := s.Size() + sf.Size = &size + } + if c, ok := f.(field.Charseter); ok { + charset := c.Charset() + sf.Charset = &charset + } + s.Fields = append(s.Fields, sf) + } + for _, e := range schema.Edges() { + s.Edges = append(s.Edges, NewEdge(e)) + } + return json.Marshal(s) +} + +func indirect(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} diff --git a/entc/load/schema_test.go b/entc/load/schema_test.go new file mode 100644 index 000000000..341b4d3c8 --- /dev/null +++ b/entc/load/schema_test.go @@ -0,0 +1,85 @@ +package load + +import ( + "encoding/json" + "testing" + + "fbc/ent" + "fbc/ent/edge" + "fbc/ent/field" + + "github.com/stretchr/testify/require" +) + +type User struct { + ent.Schema +} + +func (User) Fields() []ent.Field { + return []ent.Field{ + field.Int("age"), + field.String("name"), + field.String("nullable"). + Nullable(), + field.String("optional"). + Optional(), + } +} + +func (User) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("groups", Group.Type), + edge.To("parent", User.Type). + Unique(). + From("children"), + } +} + +type Group struct { + ent.Schema +} + +func (Group) Fields() []ent.Field { return nil } + +func (Group) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("users", User.Type), + } +} + +func TestMarshalSchema(t *testing.T) { + for _, u := range []ent.Schema{User{}, &User{}} { + buf, err := MarshalSchema(u) + require.NoError(t, err) + + schema := &Schema{} + require.NoError(t, json.Unmarshal(buf, schema)) + require.Equal(t, "User", schema.Name) + require.Len(t, schema.Fields, 4) + require.Equal(t, "age", schema.Fields[0].Name) + require.Equal(t, field.TypeInt, schema.Fields[0].Type) + + require.Equal(t, "name", schema.Fields[1].Name) + require.Equal(t, field.TypeString, schema.Fields[1].Type) + + require.Equal(t, "nullable", schema.Fields[2].Name) + require.Equal(t, field.TypeString, schema.Fields[2].Type) + require.True(t, schema.Fields[2].Nullable) + require.False(t, schema.Fields[2].Optional) + + require.Equal(t, "optional", schema.Fields[3].Name) + require.Equal(t, field.TypeString, schema.Fields[3].Type) + require.False(t, schema.Fields[3].Nullable) + require.True(t, schema.Fields[3].Optional) + + require.Len(t, schema.Edges, 2) + require.Equal(t, "groups", schema.Edges[0].Name) + require.Equal(t, "Group", schema.Edges[0].Type) + require.False(t, schema.Edges[0].Inverse) + require.Equal(t, "children", schema.Edges[1].Name) + require.Equal(t, "User", schema.Edges[1].Type) + require.True(t, schema.Edges[1].Inverse) + require.Equal(t, "parent", schema.Edges[1].Ref.Name) + require.True(t, schema.Edges[1].Ref.Unique) + } +} diff --git a/entc/load/template/main.tmpl b/entc/load/template/main.tmpl new file mode 100644 index 000000000..39ce8b53a --- /dev/null +++ b/entc/load/template/main.tmpl @@ -0,0 +1,40 @@ +{{ define "main" }} +// Code generated by 'entc generate'. DO NOT EDIT. +package main + +import ( + "os" + "bytes" + {{ template "imports" }} + + {{ printf "%q" .Package }} +) + +{{ $base := base .Package}} + +var schemas = []ent.Schema{ + {{ range $_, $name := .Names }} + {{- $base }}.{{- printf "%s{Schema: ent.DefaultSchema}" $name }}, + {{ end -}} +} + +func main() { + var lines [][]byte + for _, schema := range schemas { + b, err := MarshalSchema(schema) + if err != nil { + fail(err) + } + lines = append(lines, b) + } + os.Stdout.Write(bytes.Join(lines, []byte("\n"))) +} + +func fail(err error) { + os.Stderr.WriteString(err.Error()) + os.Exit(1) +} + +{{ template "schema" }} + +{{ end }} diff --git a/entc/internal/build/testdata/invalid/schema.go b/entc/load/testdata/invalid/schema.go similarity index 100% rename from entc/internal/build/testdata/invalid/schema.go rename to entc/load/testdata/invalid/schema.go diff --git a/entc/load/testdata/valid/schema.go b/entc/load/testdata/valid/schema.go new file mode 100644 index 000000000..bc33690e2 --- /dev/null +++ b/entc/load/testdata/valid/schema.go @@ -0,0 +1,41 @@ +package valid + +import ( + "fbc/ent" + "fbc/ent/field" +) + +// User holds the user schema. +type User struct { + ent.Schema +} + +func (User) Fields() []ent.Field { + return []ent.Field{ + field.Int("age"), + field.String("name"), + } +} + +// Group holds the group schema. +type Group struct { + ent.Schema +} + +func (Group) Fields() []ent.Field { + return []ent.Field{ + field.Time("expired_at"), + field.String("organization"), + } +} + +// Tag holds the tag schema. +type Tag struct { + ent.Schema +} + +func (Tag) Fields() []ent.Field { + return []ent.Field{ + field.String("text"), + } +} diff --git a/entc/plugin/plugin.go b/entc/plugin/plugin.go deleted file mode 100644 index 8c34eb488..000000000 --- a/entc/plugin/plugin.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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. -// -// The second argument is an optional config for the graph creation. -// -// 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", gen.Config{}) -// if err != nil { -// log.Fatal(err) -// } -// for _, node := range graph.Nodes { -// log.Println(node.Name) -// } -// } -// -func LoadGraph(path string, cfg gen.Config) (*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 - } - cfg.Schema = plg.PkgPath - cfg.Package = filepath.Dir(plg.PkgPath) - return gen.NewGraph(cfg, 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, cfg gen.Config) *gen.Graph { - graph, err := LoadGraph(path, cfg) - 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) -} diff --git a/entc/plugin/plugin_test.go b/entc/plugin/plugin_test.go deleted file mode 100644 index 43444fdc1..000000000 --- a/entc/plugin/plugin_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package plugin - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestExecInvalid(t *testing.T) { - err := Exec("./testdata/notfound", nil) - require.Error(t, err, "plugin not found") - - dest := "invalid.so" - require.NoError(t, buildPlg("./testdata/invalid", dest)) - defer os.Remove(dest) - - err = Exec(dest, nil) - require.Error(t, err, "does not implement the entc/plugin interface") -} - -func TestExecValid(t *testing.T) { - err := Exec("./testdata/notfound", nil) - require.Error(t, err, "plugin not found") - - dest := "valid.so" - require.NoError(t, buildPlg("./testdata/valid", dest)) - defer os.Remove(dest) - - err = Exec(dest, nil) - require.NoError(t, err) -} - -func buildPlg(src, dest string) error { - out := bytes.NewBuffer(nil) - cmd := exec.Command("go", "build", "-o", dest, "-buildmode", "plugin", src) - cmd.Stderr = out - if err := cmd.Run(); err != nil { - return fmt.Errorf("entc/plugin: %s", out) - } - return nil -} diff --git a/entc/plugin/testdata/invalid/invalid.go b/entc/plugin/testdata/invalid/invalid.go deleted file mode 100644 index ec9f9e5cb..000000000 --- a/entc/plugin/testdata/invalid/invalid.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -// Gen does not implement the plugin.Generator interface. -var Gen = func() {} diff --git a/entc/plugin/testdata/valid/valid.go b/entc/plugin/testdata/valid/valid.go deleted file mode 100644 index abd26f4de..000000000 --- a/entc/plugin/testdata/valid/valid.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "fbc/ent/entc/gen" -) - -// Generator implements the plugin.Generator interface. -type Generator struct{} - -// Gen implementation. -func (Generator) Gen(*gen.Graph) error { - // logic goes here. - return nil -} - -// Gen is the required plugin symbol. -var Gen = Generator{}