From 07fba400fe3cfb771934c9a52f2baee8ba3b8a4b Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 00:36:49 +0200 Subject: [PATCH 1/9] move cli handling to a fork of urfave/cli.v2 --- .vscode/cSpell.json | 17 +++ .vscode/settings.json | 3 + README.md | 77 +++++------ cmd/genx/main.go | 290 ++++++++++++++++++++++++------------------ 4 files changed, 212 insertions(+), 175 deletions(-) create mode 100644 .vscode/cSpell.json create mode 100644 .vscode/settings.json diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json new file mode 100644 index 0000000..6677879 --- /dev/null +++ b/.vscode/cSpell.json @@ -0,0 +1,17 @@ +// cSpell Settings +{ + // Version of the setting file. Always 0.1 + "version": "0.1", + // language - current active spelling language + "language": "en", + // words - list of words to be always considered correct + "words": [ + "genx" + ], + // flagWords - list of words to be always considered incorrect + // This is useful for offensive words and common spelling errors. + // For example "hte" should be "the" + "flagWords": [ + "hte" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..251d67b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.enabled": true +} diff --git a/README.md b/README.md index 33aca11..a5b6f33 100644 --- a/README.md +++ b/README.md @@ -160,54 +160,35 @@ For Example I needed to remove a field from the struct and change all usage of i ## Usage ([`cmd/genx`](https://github.com/OneOfOne/genx/tree/master/cmd/genx/main.go)): ``` -usage: genx [-t T=type] [-s xx.xx=[yy.]yy] [-fld struct-field-to-remove] [-fn func-to-remove] [-tags "go build tags"] - [-m] [-n package-name] [-pkg input package] [-f input file] [-o output file or dir] - -Types: - The -t/-s flags supports full package paths or short ones and letting goimports handle it. - -t "KV=string - -t "M=*cmap.CMap" - -t "M=github.com/OneOfOne/cmap.*CMap" - -s "cm.HashFn=github.com/OneOfOne/cmap/hashers#H.Fnv32" - -s "cm.HashFn=github.com/OneOfOne/cmap/hashers.Fnv32" - -s "cm.HashFn=hashers.Fnv32" - -t "RemoveThisType" - -fld "RemoveThisStructField,OtherField=NewFieldName" - -fn "RemoveThisFunc,OtherFunc=NewFuncName" - -Examples: - genx -pkg github.com/OneOfOne/cmap -t "KT=interface{},VT=interface{}" -m -n cmap -o ./cmap.go - genx -f github.com/OneOfOne/cmap/lmap.go -t "KT=string,VT=int" -fn "NewLMap,NewLMapSize=NewStringInt" -n main -o ./lmap_string_int.go - - genx -pkg github.com/OneOfOne/cmap -n stringcmap -t KT=string -t VT=interface{} -fld HashFn \ - -fn DefaultKeyHasher -s "cm.HashFn=hashers.Fnv32" -m -o ./stringcmap/cmap.go - -Flags: - -f file - file to parse - -fld field - struct fields to remove or rename (ex: -fld HashFn -fld priv=Pub) - -fn value - func`s to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse) - -get - go get the package if it doesn't exist - -goFlags flags - extra go get flags (ex: -goFlags '-t -race') - -n package name - package name sets the output package name, uses input package name if empty. - -o string - output dir if parsing a package or output filename if parsing a file (default "/dev/stdin") - -pkg package - package to parse - -s selector spec - selector specs to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something') - -seed - alias for -m -pkg github.com/OneOfOne/seeds/ - -t type spec - generic type specs to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType) - -tags tags - go build tags, used for parsing and automatically passed to go get. - -v verbose +➤ genx -h +NAME: + genx - A new cli application + +USAGE: + genx [global options] command [command options] [arguments...] + +VERSION: + v0.5 + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --seed seed-name alias for -pkg github.com/OneOfOne/genx/seeds/seed-name + --input file, -i file, -f file file to process, use `-` to process stdin. + --package package, -pkg package package to process + --name name, -n name package name to use for output, uses the input package's name by default. + --type type, -t type generic type names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType) + --selector selector, -s selector selectors to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something') + --field field, -fld field struct fields to remove or rename (ex: -fld HashFn -fld priv=Pub) + --func func, -fn func functions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse) + --tags value go extra build tags, used for parsing and automatically passed to any go subcommands. + --go-flags flags extra flags to pass to go subcommands flags (ex: --goFlags '-race') + --output value, -o value output dir if parsing a package or output filename if you want the output to be merged. (default: "/dev/stdout") + --get go get the package if it doesn't exist (default: false) + --verbose, -v verbose output (default: false) + --help, -h show help (default: false) + --version, -V print the version (default: false) ``` ## Contributions diff --git a/cmd/genx/main.go b/cmd/genx/main.go index 32bb68c..a14dc13 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -1,7 +1,6 @@ package main import ( - "flag" "fmt" "log" "os" @@ -9,97 +8,127 @@ import ( "path/filepath" "strings" + "github.com/OneOfOne/cli" "github.com/OneOfOne/genx" ) -type sflags []string - -func (sf *sflags) String() string { - return strings.Join(*sf, " ") -} - -func (sf *sflags) Set(value string) error { - parts := strings.Split(value, ",") - for i, p := range parts { - parts[i] = strings.TrimSpace(p) +type sflags []*[2]string + +func flattenFlags(in []string) (out sflags) { + for _, f := range in { + for _, p := range strings.Split(f, ",") { + kv := strings.Split(p, "=") + switch len(kv) { + case 2: + out = append(out, &[2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])}) + case 1: + out = append(out, &[2]string{strings.TrimSpace(kv[0]), ""}) + } + } } - *sf = append(*sf, parts...) - return nil + return } -func (sf *sflags) Split(i int) (_, _ string) { - parts := strings.Split((*sf)[i], "=") - switch len(parts) { - case 2: - return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) - case 1: - return strings.TrimSpace(parts[0]), "" - default: - return +func main() { + log.SetFlags(log.Lshortfile) + cli.VersionFlag = &cli.BoolFlag{ + Name: "version", + Aliases: []string{"V"}, + Usage: "print the version", } -} - -var ( - types, fields, selectors, funcs, tags sflags - - seed, inFile, inPkg, outPath string - goFlags string - pkgName string - mergeFiles bool - goGet bool - verbose bool -) + app := &cli.App{ + Name: "genx", + Version: "v0.5", + + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "seed", + Usage: "alias for -pkg github.com/OneOfOne/genx/seeds/`seed-name`", + }, + + &cli.StringFlag{ + Name: "input", + Aliases: []string{"i", "f" /* compatibility */}, + Usage: "`file` to process, use `-` to process stdin.", + }, + + &cli.StringFlag{ + Name: "package", + Aliases: []string{"-pkg"}, + Usage: "`package` to process", + }, + + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Usage: "package `name` to use for output, uses the input package's name by default.", + }, + + &cli.StringSliceFlag{ + Name: "type", + Aliases: []string{"t"}, + Usage: "generic `type` names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType)", + }, + + &cli.StringSliceFlag{ + Name: "selector", + Aliases: []string{"s"}, + Usage: "`selector`s to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something')", + }, + + &cli.StringSliceFlag{ + Name: "field", + Aliases: []string{"-fld"}, + Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld priv=Pub)", + }, + + &cli.StringSliceFlag{ + Name: "func", + Aliases: []string{"-fn"}, + Usage: "`func`tions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse)", + }, + + &cli.StringSliceFlag{ + Name: "tags", + Usage: "go extra build tags, used for parsing and automatically passed to any go subcommands.", + }, + + &cli.StringSliceFlag{ + Name: "go-flags", + Usage: "extra flags to pass to go subcommands `flags` (ex: --goFlags '-race')", + }, + + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Value: "/dev/stdout", + Usage: "output dir if parsing a package or output filename if you want the output to be merged.", + }, + + &cli.BoolFlag{ + Name: "get", + Usage: "go get the package if it doesn't exist", + }, + + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "verbose output", + }, + }, + Action: runGen, + } -func init() { - log.SetFlags(log.Lshortfile) - flag.Usage = func() { - fmt.Fprintln(os.Stderr, `usage: genx [-t T=type] [-s xx.xx=[yy.]yy] [-fld struct-field-to-remove] [-fn func-to-remove] [-tags "go build tags"] - [-m] [-n package-name] [-pkg input package] [-f input file] [-o output file or dir] - -Types: - The -t/-s flags supports full package paths or short ones and letting goimports handle it. - -t "KV=string - -t "M=*cmap.CMap" - -t "M=github.com/OneOfOne/cmap.*CMap" - -s "cm.HashFn=github.com/OneOfOne/cmap/hashers#H.Fnv32" - -s "cm.HashFn=github.com/OneOfOne/cmap/hashers.Fnv32" - -s "cm.HashFn=hashers.Fnv32" - -t "RemoveThisType" - -fld "RemoveThisStructField,OtherField=NewFieldName" - -fn "RemoveThisFunc,OtherFunc=NewFuncName" - -Examples: - genx -pkg github.com/OneOfOne/cmap -t "KT=interface{},VT=interface{}" -m -n cmap -o ./cmap.go - genx -f github.com/OneOfOne/cmap/lmap.go -t "KT=string,VT=int" -fn "NewLMap,NewLMapSize=NewStringInt" -n main -o ./lmap_string_int.go - - genx -pkg github.com/OneOfOne/cmap -n stringcmap -t KT=string -t VT=interface{} -fld HashFn \ - -fn DefaultKeyHasher -s "cm.HashFn=hashers.Fnv32" -m -o ./stringcmap/cmap.go - -Flags:`) - flag.PrintDefaults() - } - flag.Var(&types, "t", "generic `type spec`s to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType)") - flag.Var(&selectors, "s", "`selector spec`s to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something')") - flag.Var(&fields, "fld", "struct `field`s to remove or rename (ex: -fld HashFn -fld priv=Pub)") - flag.Var(&funcs, "fn", "func`s to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse)") - flag.Var(&tags, "tags", "go build `tags`, used for parsing and automatically passed to go get.") - flag.StringVar(&seed, "seed", "", "alias for -m -pkg github.com/OneOfOne/seeds/``") - flag.StringVar(&inFile, "f", "", "`file` to parse") - flag.StringVar(&inPkg, "pkg", "", "`package` to parse") - flag.StringVar(&outPath, "o", "/dev/stdout", "output dir if parsing a package or output filename if parsing a file") - flag.StringVar(&pkgName, "n", "", "`package name` sets the output package name, uses input package name if empty.") - flag.StringVar(&goFlags, "goFlags", "", "extra go get `flags` (ex: -goFlags '-t -race')") - flag.BoolVar(&verbose, "v", false, "verbose") - flag.BoolVar(&goGet, "get", false, "go get the package if it doesn't exist") - - flag.Parse() + // TODO: support other actions + app.Run(os.Args) } -func main() { +func runGen(c *cli.Context) error { rewriters := map[string]string{} - for i := range types { - key, val := types.Split(i) + + for _, kv := range flattenFlags(c.StringSlice("type")) { + key, val := kv[0], kv[1] if key == "" { continue } @@ -108,8 +137,9 @@ func main() { } rewriters["type:"+key] = val } - for i := range selectors { - key, val := selectors.Split(i) + + for _, kv := range flattenFlags(c.StringSlice("selector")) { + key, val := kv[0], kv[1] if key == "" { continue } @@ -118,8 +148,10 @@ func main() { } rewriters["selector:"+key] = val } - for i := range fields { - key, val := fields.Split(i) + + for _, kv := range flattenFlags(c.StringSlice("field")) { + key, val := kv[0], kv[1] + if key == "" { continue } @@ -128,8 +160,10 @@ func main() { } rewriters["field:"+key] = val } - for i := range funcs { - key, val := funcs.Split(i) + + for _, kv := range flattenFlags(c.StringSlice("func")) { + key, val := kv[0], kv[1] + if key == "" { continue } @@ -139,14 +173,20 @@ func main() { rewriters["func:"+key] = val } - g := genx.New(pkgName, rewriters) - g.BuildTags = append(g.BuildTags, tags...) + g := genx.New(c.String("name"), rewriters) + g.BuildTags = append(g.BuildTags, c.StringSlice("tags")...) - if verbose { + if c.Bool("verbose") { log.Printf("rewriters: %+q", g.OrderedRewriters()) log.Printf("build tags: %+q", g.BuildTags) } + var ( + outPath = c.String("output") + + mergeFiles bool + inPkg string + ) switch outPath { case "", "-", "/dev/stdout": outPath = "/dev/stdout" @@ -156,63 +196,66 @@ func main() { // auto merge files if the output is a file not a dir. mergeFiles = !mergeFiles && filepath.Ext(outPath) == ".go" - if seed != "" { + if seed := c.String("seed"); seed != "" { inPkg = "github.com/OneOfOne/genx/seeds/" + seed mergeFiles = true } if inPkg != "" { - out, err := goListThenGet(g.BuildTags, inPkg) + out, err := goListThenGet(c, g.BuildTags, inPkg) if err != nil { - log.Fatalf("error: %s\n", out) + return cli.Exit(err, 2) } + inPkg = out - // if !strings.HasPrefix(inpk, prefix string) pkg, err := g.ParsePkg(inPkg, false) + if err != nil { - log.Fatalf("error parsing package (%s): %v\n", inPkg, err) + return cli.Exit(fmt.Sprintf("error parsing package (%s): %v\n", inPkg, err), 1) } if mergeFiles { - if err := pkg.WriteAllMerged(outPath, false); err != nil { - log.Fatalf("error writing merged package: %v", err) - } + err = pkg.WriteAllMerged(outPath, false) } else { - if err := pkg.WritePkg(outPath); err != nil { - log.Fatalf("error writing merged package: %v", err) - } + err = pkg.WritePkg(outPath) } - return - } - switch inFile { - case "", "-": - default: - out, err := goListThenGet(g.BuildTags, inFile) if err != nil { - log.Fatalf("error:\n%s\n", out) + return cli.Exit(err, 1) + } + } else if inFile := c.String("input"); inFile != "" { + if inFile != "-" && inFile != "/dev/stdin" { + out, err := goListThenGet(c, g.BuildTags, inFile) + if err != nil { + return cli.Exit(err, 2) + } + inFile = out } - pf, err := g.Parse(out, nil) + pf, err := g.Parse(inFile, nil) if err != nil { - log.Fatalf("error parsing file (%s): %v\n%s", inFile, err, pf.Src) + return cli.Exit(fmt.Sprintf("error parsing file (%s): %v\n%s", inFile, err, pf.Src), 1) } + if err := pf.WriteFile(outPath); err != nil { - log.Fatalf("error writing file: %v", err) + return cli.Exit(err, 1) } + } else { + cli.ShowAppHelpAndExit(c, 1) } -} -func execCmd(c string, args ...string) (string, error) { + return nil +} +func execCmd(ctx *cli.Context, c string, args ...string) (string, error) { cmd := exec.Command(c, args...) - if verbose { + if ctx.Bool("verbose") { log.Printf("executing: %s %s", c, strings.Join(args, " ")) } out, err := cmd.CombinedOutput() return strings.TrimSpace(string(out)), err } -func goListThenGet(tags []string, path string) (out string, err error) { +func goListThenGet(ctx *cli.Context, tags []string, path string) (out string, err error) { if _, err = os.Stat(path); err == nil { return path, nil } @@ -224,21 +267,19 @@ func goListThenGet(tags []string, path string) (out string, err error) { } args := []string{"-tags", strings.Join(tags, " ")} - if goFlags != "" { - args = append(args, strings.Split(goFlags, " ")...) - } + args = append(args, ctx.StringSlice("go-flags")...) args = append(args, dir) listArgs := append([]string{"list", "-f", "{{.Dir}}"}, args...) - if out, err = execCmd("go", listArgs...); err != nil && strings.Contains(out, "cannot find package") { - if !goGet { - out = fmt.Sprintf("`%s` not found and `-get` isn't specified.", path) + if out, err = execCmd(ctx, "go", listArgs...); err != nil && strings.Contains(out, "cannot find package") { + if !ctx.Bool("get") { + out = fmt.Sprintf("`%s` not found and `--get` isn't specified.", path) return } - if out, err = execCmd("go", append([]string{"get", "-u", "-v"}, args...)...); err == nil && isFile { - out, err = execCmd("go", listArgs...) + if out, err = execCmd(ctx, "go", append([]string{"get", "-u", "-v"}, args...)...); err == nil && isFile { + out, err = execCmd(ctx, "go", listArgs...) } } @@ -247,8 +288,3 @@ func goListThenGet(tags []string, path string) (out string, err error) { } return } - -/* -go run cmd/genx/main.go -t KT=string -t "VT=interface{}" -rm-field HashFn -s "cm.HashFn=hashers.Fnv32" -s "cmap=-" -pkg ../cmap/internal/cmap/ - - n stringcmap -m -*/ From c90c004529548c9179d4047b9023c314d778aeb5 Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 00:38:31 +0200 Subject: [PATCH 2/9] WIP --- all_types.go | 6 +- genx.go | 114 +++++++++++++------------------------- rewriters.go | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 80 deletions(-) create mode 100644 rewriters.go diff --git a/all_types.go b/all_types.go index f6d4107..28ab728 100644 --- a/all_types.go +++ b/all_types.go @@ -36,13 +36,13 @@ var ( m map[KT]VT ktCh chan KT vtCh chan VT - kvCh chan Both + kvCh chan BothKT ktA [100]KT vtA [100]VT - a [100]Both + a [100]BothKT ktS []KT vtS []VT - s []Both + s []BothKT ) func XXX(vs ...interface{}) interface{} { diff --git a/genx.go b/genx.go index 9c41b23..70e0899 100644 --- a/genx.go +++ b/genx.go @@ -10,6 +10,7 @@ import ( "go/token" "log" "path/filepath" + "reflect" "regexp" "strings" @@ -19,28 +20,42 @@ import ( "golang.org/x/tools/imports" ) +type procFunc func(*xast.Node) *xast.Node type GenX struct { pkgName string rewriters map[string]string irepl *strings.Replacer imports map[string]string - zero_types map[string]bool + zeroTypes map[string]bool curReturnTypes []string visited map[ast.Node]bool BuildTags []string CommentFilters []func(string) string + + rewriteFuncs map[reflect.Type][]procFunc } func New(pkgName string, rewriters map[string]string) *GenX { g := &GenX{ - pkgName: pkgName, - rewriters: map[string]string{}, - imports: map[string]string{}, - visited: map[ast.Node]bool{}, - irepl: geireplacer(rewriters, true), - zero_types: map[string]bool{}, - BuildTags: []string{"genx"}, + pkgName: pkgName, + rewriters: map[string]string{}, + imports: map[string]string{}, + visited: map[ast.Node]bool{}, + irepl: geireplacer(rewriters, true), + zeroTypes: map[string]bool{}, + BuildTags: []string{"genx"}, + CommentFilters: []func(string) string{ + regexpReplacer(`// \+build [!]?genx.*|//go:generate genx`, ""), + }, + } + + g.rewriteFuncs = map[reflect.Type][]procFunc{ + reflect.TypeOf((*ast.TypeSpec)(nil)): {g.rewriteTypeSpec}, + reflect.TypeOf((*ast.Ident)(nil)): {g.rewriteIdent}, + reflect.TypeOf((*ast.Field)(nil)): {g.rewriteField}, + reflect.TypeOf((*ast.FuncDecl)(nil)): {g.rewriteFuncDecl}, + reflect.TypeOf((*ast.File)(nil)): {g.rewriteFile}, } for k, v := range rewriters { @@ -69,7 +84,7 @@ func New(pkgName string, rewriters map[string]string) *GenX { g.BuildTags = append(g.BuildTags, "genx_"+strings.ToLower(kw)+"_builtin") } g.BuildTags = append(g.BuildTags, "genx_"+strings.ToLower(kw)+"_"+csel) - g.zero_types[sel] = false + g.zeroTypes[sel] = false g.CommentFilters = append(g.CommentFilters, regexpReplacer(`\b(`+kw+`)\b`, sel)) g.CommentFilters = append(g.CommentFilters, regexpReplacer(`(`+kw+`)`, strings.Title(csel))) } @@ -78,7 +93,6 @@ func New(pkgName string, rewriters map[string]string) *GenX { g.rewriters[k] = sel } - g.CommentFilters = append(g.CommentFilters, regexpReplacer(`\+build \!?genx.*|go:generate genx`, "")) return g } @@ -152,9 +166,9 @@ func (g *GenX) process(idx int, fset *token.FileSet, name string, file *ast.File return } - if idx == 0 && len(g.zero_types) > 0 { + if idx == 0 && len(g.zeroTypes) > 0 { buf.WriteByte('\n') - for t, used := range g.zero_types { + for t, used := range g.zeroTypes { if used { fmt.Fprintf(&buf, "var zero_%s %s\n", cleanUpName.ReplaceAllString(t, ""), t) } @@ -180,73 +194,17 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { } g.visited[n] = true - rewr := g.rewriters - switch n := n.(type) { - case *ast.TypeSpec: - if t := getIdent(n.Name); t != nil { - nn, ok := rewr["type:"+t.Name] - if !ok { + if fns, ok := g.rewriteFuncs[reflect.TypeOf(n)]; ok { + for _, fn := range fns { + if node = fn(node); node.Canceled() { break } - if nn == "-" || nn == "" { - return node.Delete() - } - switch n.Type.(type) { - case *ast.SelectorExpr, *ast.InterfaceType, *ast.Ident: - return node.Delete() - default: - t.Name = nn - } - - } - - case *ast.FuncDecl: - if t := getIdent(n.Name); t != nil { - nn := rewr["func:"+t.Name] - if nn == "-" { - return node.Delete() - } else if nn != "" { - t.Name = nn - } - } - - if recv := n.Recv; recv != nil && len(recv.List) == 1 { - t := getIdent(recv.List[0].Type) - if t == nil { - log.Panicf("hmm... %#+v", recv.List[0].Type) - } - nn, ok := rewr["type:"+t.Name] - - if nn == "-" { - return node.Delete() - } - if ok { - t.Name = nn - } else { - t.Name = g.irepl.Replace(t.Name) - } - } - - if t, ok := g.rewriteExprTypes("type:", n.Type).(*ast.FuncType); ok { - n.Type = t - } else { - return node.Delete() - } - - if g.shouldNukeFuncBody(n.Body) { - return node.Delete() - } - - case *ast.Ident: - if t, ok := rewr["type:"+n.Name]; ok { - if t == "-" { - break - } - n.Name = t - } else { - n.Name = g.irepl.Replace(n.Name) } + return node + } + rewr := g.rewriters + switch n := n.(type) { case *ast.Field: n.Type = g.rewriteExprTypes("type:", n.Type) @@ -324,12 +282,14 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { for i, r := range n.Results { if rt := getIdent(r); rt != nil && rt.Name == "nil" { crt := cleanUpName.ReplaceAllString(g.curReturnTypes[i], "") - if _, ok := g.zero_types[crt]; ok { - g.zero_types[crt] = true + if _, ok := g.zeroTypes[crt]; ok { + g.zeroTypes[crt] = true rt.Name = "zero_" + cleanUpName.ReplaceAllString(crt, "") } } } + case *ast.File: + } return node diff --git a/rewriters.go b/rewriters.go new file mode 100644 index 0000000..614f913 --- /dev/null +++ b/rewriters.go @@ -0,0 +1,152 @@ +package genx + +import ( + "go/ast" + "log" + "strings" + + "github.com/OneOfOne/xast" +) + +func (g *GenX) rewriteField(node *xast.Node) *xast.Node { + n := node.Node().(*ast.Field) + n.Type = g.rewriteExprTypes("type:", n.Type) + + if len(n.Names) == 0 { + return node + } + + names := n.Names[:0] + for _, n := range n.Names { + nn, ok := g.rewriters["field:"+n.Name] + if nn == "-" { + continue + } + if ok { + n.Name = nn + } else { + n.Name = g.irepl.Replace(n.Name) + } + names = append(names, n) + + } + + if n.Names = names; len(n.Names) == 0 { + return node.Delete() + } + + return node +} +func (g *GenX) rewriteTypeSpec(node *xast.Node) *xast.Node { + n := node.Node().(*ast.TypeSpec) + if t := getIdent(n.Name); t != nil { + nn, ok := g.rewriters["type:"+t.Name] + if !ok { + return node + } + if nn == "-" || nn == "" { + return node.Delete() + } + switch n.Type.(type) { + case *ast.SelectorExpr, *ast.InterfaceType, *ast.Ident: + return node.Delete() + default: + t.Name = nn + } + + } + return node +} + +func (g *GenX) rewriteIdent(node *xast.Node) *xast.Node { + n := node.Node().(*ast.Ident) + if t, ok := g.rewriters["type:"+n.Name]; ok { + if t == "-" { + return node + } + n.Name = t + } else { + n.Name = g.irepl.Replace(n.Name) + } + return node +} + +func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { + n := node.Node().(*ast.FuncDecl) + if t := getIdent(n.Name); t != nil { + nn := g.rewriters["func:"+t.Name] + if nn == "-" { + return node.Delete() + } else if nn != "" { + t.Name = nn + } + } + + if recv := n.Recv; recv != nil && len(recv.List) == 1 { + t := getIdent(recv.List[0].Type) + if t == nil { + log.Panicf("hmm... %#+v", recv.List[0].Type) + } + nn, ok := g.rewriters["type:"+t.Name] + + if nn == "-" { + return node.Delete() + } + if ok { + t.Name = nn + } else { + t.Name = g.irepl.Replace(t.Name) + } + } + + if t, ok := g.rewriteExprTypes("type:", n.Type).(*ast.FuncType); ok { + n.Type = t + } else { + return node.Delete() + } + + if g.shouldNukeFuncBody(n.Body) { + return node.Delete() + } + + return node +} + +func (g *GenX) rewriteFile(node *xast.Node) *xast.Node { + n := node.Node().(*ast.File) + for _, cg := range n.Comments { + list := cg.List[:0] + for _, c := range cg.List { + for _, f := range g.CommentFilters { + if c.Text = f(c.Text); c.Text == "" { + break + } + } + if c.Text != "" && strings.TrimSpace(c.Text) != "//" { + list = append(list, c) + } + + } + cg.List = list + } + + if n.Doc == nil { + return node + } + + list := n.Doc.List[:0] + for _, c := range n.Doc.List { + for _, f := range g.CommentFilters { + if c.Text = f(c.Text); c.Text == "" { + break + } + } + + if c.Text != "" && strings.TrimSpace(c.Text) != "//" { + list = append(list, c) + } + + } + n.Doc.List = list + return node +} From 985b748c2832cef6494476df6e4bbc84d6973538 Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 00:42:14 +0200 Subject: [PATCH 3/9] typos --- .vscode/cSpell.json | 13 ++++++++++++- README.md | 8 ++++---- cmd/genx/main.go | 2 +- genx.go | 5 ++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index 6677879..70a6ce1 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -6,7 +6,18 @@ "language": "en", // words - list of words to be always considered correct "words": [ - "genx" + "Iface", + "astrewrite", + "cmap", + "geireplacer", + "genny", + "genx", + "irepl", + "lmap", + "sflags", + "tions", + "vals", + "xsel" ], // flagWords - list of words to be always considered incorrect // This is useful for offensive words and common spelling errors. diff --git a/README.md b/README.md index a5b6f33..c9dd5c6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ * If you intend on generating files in the same package, you may add `// +build genx` to your template(s). * Transparently handles [genny](https://github.com/cheekybits/genny)'s `generic.Type`. * Supports a few [seeds](https://github.com/OneOfOne/genx/tree/master/seeds/). -* Adds build tags based on the types you pass, so you can target specifc types (ex: `// +build genx_t_string` or `// +build genx_vt_builtin` ) +* Adds build tags based on the types you pass, so you can target specific types (ex: `// +build genx_t_string` or `// +build genx_vt_builtin` ) * Automatically handles nil returns, will return the zero value of the type. * Doesn't need modifying the source package if there's only one type involved. @@ -52,7 +52,7 @@ ➤ genx -f github.com/OneOfOne/cmap/lmap.go -t "KT=string,VT=int" -fn "NewLMap,NewLMapSize=NewStringInt" -n main -v -o ./lmap_string_int.go ``` -### Modifing an external library that doesn't specifically support generics: +### Modifying an external library that doesn't specifically support generics: Using [fatih](https://github.com/fatih)'s excellent [set](https://github.com/fatih/set) library: ``` @@ -150,7 +150,7 @@ For Example I needed to remove a field from the struct and change all usage of i * Handle removing comments properly rather than using regexp. * More seeds. * ~~Add proper examples.~~ -* ~~Support specalized functions by type.~~ +* ~~Support specialized functions by type.~~ * ~~Support removing structs and their methods.~~ ## Credits @@ -180,7 +180,7 @@ GLOBAL OPTIONS: --name name, -n name package name to use for output, uses the input package's name by default. --type type, -t type generic type names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType) --selector selector, -s selector selectors to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something') - --field field, -fld field struct fields to remove or rename (ex: -fld HashFn -fld priv=Pub) + --field field, -fld field struct fields to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc) --func func, -fn func functions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse) --tags value go extra build tags, used for parsing and automatically passed to any go subcommands. --go-flags flags extra flags to pass to go subcommands flags (ex: --goFlags '-race') diff --git a/cmd/genx/main.go b/cmd/genx/main.go index a14dc13..66be144 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -80,7 +80,7 @@ func main() { &cli.StringSliceFlag{ Name: "field", Aliases: []string{"-fld"}, - Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld priv=Pub)", + Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc)", }, &cli.StringSliceFlag{ diff --git a/genx.go b/genx.go index 70e0899..4da4b97 100644 --- a/genx.go +++ b/genx.go @@ -203,7 +203,6 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { return node } - rewr := g.rewriters switch n := n.(type) { case *ast.Field: n.Type = g.rewriteExprTypes("type:", n.Type) @@ -214,7 +213,7 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { names := n.Names[:0] for _, n := range n.Names { - nn, ok := rewr["field:"+n.Name] + nn, ok := g.rewriters["field:"+n.Name] if nn == "-" { continue } @@ -239,7 +238,7 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { } case *ast.KeyValueExpr: - if key := getIdent(n.Key); key != nil && rewr["field:"+key.Name] == "-" { + if key := getIdent(n.Key); key != nil && g.rewriters["field:"+key.Name] == "-" { return node.Delete() } From 969ce2f669ba95cdccd804505779b6dba2de5579 Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 01:15:01 +0200 Subject: [PATCH 4/9] some cleanup --- .vscode/cSpell.json | 1 + cmd/genx/main.go | 36 ++++++++++++++++++++++-------------- genx.go | 4 +--- rewriters.go | 20 ++++---------------- 4 files changed, 28 insertions(+), 33 deletions(-) diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index 70a6ce1..5671b64 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -16,6 +16,7 @@ "lmap", "sflags", "tions", + "tmpl", "vals", "xsel" ], diff --git a/cmd/genx/main.go b/cmd/genx/main.go index 66be144..c0687b9 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -41,12 +41,19 @@ func main() { Name: "genx", Version: "v0.5", + Commands: []*cli.Command{ + // { + // Name: "implement", + // Aliases: []string{"impl", "i"}, + // Flags: []cli.Flag{}, + // Action: runImpl, + // }, + }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "seed", Usage: "alias for -pkg github.com/OneOfOne/genx/seeds/`seed-name`", }, - &cli.StringFlag{ Name: "input", Aliases: []string{"i", "f" /* compatibility */}, @@ -56,7 +63,7 @@ func main() { &cli.StringFlag{ Name: "package", Aliases: []string{"-pkg"}, - Usage: "`package` to process", + Usage: "`package` to process.", }, &cli.StringFlag{ @@ -68,25 +75,32 @@ func main() { &cli.StringSliceFlag{ Name: "type", Aliases: []string{"t"}, - Usage: "generic `type` names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType)", + Usage: "generic `type` names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType).", }, &cli.StringSliceFlag{ Name: "selector", Aliases: []string{"s"}, - Usage: "`selector`s to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something')", + Usage: "`selector`s to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something').", }, &cli.StringSliceFlag{ Name: "field", Aliases: []string{"-fld"}, - Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc)", + Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc).", }, &cli.StringSliceFlag{ Name: "func", Aliases: []string{"-fn"}, - Usage: "`func`tions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse)", + Usage: "`func`tions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse).", + }, + + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Value: "/dev/stdout", + Usage: "output dir if parsing a package or output filename if you want the output to be merged.", }, &cli.StringSliceFlag{ @@ -95,17 +109,10 @@ func main() { }, &cli.StringSliceFlag{ - Name: "go-flags", + Name: "goFlags", Usage: "extra flags to pass to go subcommands `flags` (ex: --goFlags '-race')", }, - &cli.StringFlag{ - Name: "output", - Aliases: []string{"o"}, - Value: "/dev/stdout", - Usage: "output dir if parsing a package or output filename if you want the output to be merged.", - }, - &cli.BoolFlag{ Name: "get", Usage: "go get the package if it doesn't exist", @@ -187,6 +194,7 @@ func runGen(c *cli.Context) error { mergeFiles bool inPkg string ) + switch outPath { case "", "-", "/dev/stdout": outPath = "/dev/stdout" diff --git a/genx.go b/genx.go index 4da4b97..218aef8 100644 --- a/genx.go +++ b/genx.go @@ -45,9 +45,7 @@ func New(pkgName string, rewriters map[string]string) *GenX { irepl: geireplacer(rewriters, true), zeroTypes: map[string]bool{}, BuildTags: []string{"genx"}, - CommentFilters: []func(string) string{ - regexpReplacer(`// \+build [!]?genx.*|//go:generate genx`, ""), - }, + // CommentFilters: []func(string) string{}, } g.rewriteFuncs = map[reflect.Type][]procFunc{ diff --git a/rewriters.go b/rewriters.go index 614f913..176bd4d 100644 --- a/rewriters.go +++ b/rewriters.go @@ -3,7 +3,6 @@ package genx import ( "go/ast" "log" - "strings" "github.com/OneOfOne/xast" ) @@ -112,20 +111,16 @@ func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { return node } +var nukeGenxComments = regexpReplacer(`// \+build [!]?genx.*|//go:generate genx`, "") + func (g *GenX) rewriteFile(node *xast.Node) *xast.Node { n := node.Node().(*ast.File) for _, cg := range n.Comments { list := cg.List[:0] for _, c := range cg.List { - for _, f := range g.CommentFilters { - if c.Text = f(c.Text); c.Text == "" { - break - } - } - if c.Text != "" && strings.TrimSpace(c.Text) != "//" { + if c.Text = nukeGenxComments(c.Text); c.Text != "" { list = append(list, c) } - } cg.List = list } @@ -136,16 +131,9 @@ func (g *GenX) rewriteFile(node *xast.Node) *xast.Node { list := n.Doc.List[:0] for _, c := range n.Doc.List { - for _, f := range g.CommentFilters { - if c.Text = f(c.Text); c.Text == "" { - break - } - } - - if c.Text != "" && strings.TrimSpace(c.Text) != "//" { + if c.Text = nukeGenxComments(c.Text); c.Text != "" { list = append(list, c) } - } n.Doc.List = list return node From 4af2e23f92dcf1a890ecff36ddbd131115235e3e Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 02:48:12 +0200 Subject: [PATCH 5/9] big refactor. Fixes #6 --- all_types.go | 27 ++++++--- cmd/genx/main.go | 10 ++-- genx.go | 145 ++++++++++------------------------------------- rewriters.go | 136 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 128 deletions(-) diff --git a/all_types.go b/all_types.go index 28ab728..3c23b5e 100644 --- a/all_types.go +++ b/all_types.go @@ -7,42 +7,53 @@ import "github.com/cheekybits/genny/generic" type KT generic.Type type VT interface{} -type BothKT struct { +type TypeWithKT struct { K KT V VT Call func(k KT) VT - RemoveMeToo int // comment + RemoveMeToo VT // comment } // RemoveMe comment -func (b *BothKT) RemoveMe() { +func (b *TypeWithKT) RemoveMe() { b.K = new(KT) b.V = new(VT) } // some comment -func (b BothKT) RemoveMe2() { +func (b TypeWithKT) RemoveMe2() { b.K = new(KT) b.V = new(VT) } +func DoParam(b *TypeWithKT) {} +func DoRes() *TypeWithKT { return nil } +func DoBoth(b *TypeWithKT) *TypeWithKT { return nil } + func DoStuff(k ...KT) VT { - var b BothKT + b := &TypeWithKT{} return b.Call(k[0]) } +func DoStuff2(k ...KT) VT { + var b TypeWithKT + return b.RemoveMe2 +} + var ( m map[KT]VT ktCh chan KT vtCh chan VT - kvCh chan BothKT + kvCh chan TypeWithKT ktA [100]KT vtA [100]VT - a [100]BothKT + a [100]TypeWithKT ktS []KT vtS []VT - s []BothKT + s []*TypeWithKT + m map[TypeWithKT]int + mp map[*TypeWithKT]int ) func XXX(vs ...interface{}) interface{} { diff --git a/cmd/genx/main.go b/cmd/genx/main.go index c0687b9..42de530 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -22,7 +22,7 @@ func flattenFlags(in []string) (out sflags) { case 2: out = append(out, &[2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])}) case 1: - out = append(out, &[2]string{strings.TrimSpace(kv[0]), ""}) + out = append(out, &[2]string{strings.TrimSpace(kv[0])}) } } } @@ -56,13 +56,13 @@ func main() { }, &cli.StringFlag{ Name: "input", - Aliases: []string{"i", "f" /* compatibility */}, + Aliases: []string{"i"}, Usage: "`file` to process, use `-` to process stdin.", }, &cli.StringFlag{ Name: "package", - Aliases: []string{"-pkg"}, + Aliases: []string{"pkg"}, Usage: "`package` to process.", }, @@ -86,13 +86,13 @@ func main() { &cli.StringSliceFlag{ Name: "field", - Aliases: []string{"-fld"}, + Aliases: []string{"fld"}, Usage: "struct `field`s to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc).", }, &cli.StringSliceFlag{ Name: "func", - Aliases: []string{"-fn"}, + Aliases: []string{"fn"}, Usage: "`func`tions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse).", }, diff --git a/genx.go b/genx.go index 218aef8..8c55efd 100644 --- a/genx.go +++ b/genx.go @@ -49,11 +49,20 @@ func New(pkgName string, rewriters map[string]string) *GenX { } g.rewriteFuncs = map[reflect.Type][]procFunc{ - reflect.TypeOf((*ast.TypeSpec)(nil)): {g.rewriteTypeSpec}, - reflect.TypeOf((*ast.Ident)(nil)): {g.rewriteIdent}, - reflect.TypeOf((*ast.Field)(nil)): {g.rewriteField}, - reflect.TypeOf((*ast.FuncDecl)(nil)): {g.rewriteFuncDecl}, - reflect.TypeOf((*ast.File)(nil)): {g.rewriteFile}, + reflect.TypeOf((*ast.TypeSpec)(nil)): {g.rewriteTypeSpec}, + reflect.TypeOf((*ast.Ident)(nil)): {g.rewriteIdent}, + reflect.TypeOf((*ast.Field)(nil)): {g.rewriteField}, + reflect.TypeOf((*ast.FuncDecl)(nil)): {g.rewriteFuncDecl}, + reflect.TypeOf((*ast.File)(nil)): {g.rewriteFile}, + reflect.TypeOf((*ast.Comment)(nil)): {g.rewriteComment}, + reflect.TypeOf((*ast.SelectorExpr)(nil)): {g.rewriteSelectorExpr}, + reflect.TypeOf((*ast.KeyValueExpr)(nil)): {g.rewriteKeyValueExpr}, + reflect.TypeOf((*ast.InterfaceType)(nil)): {g.rewriteInterfaceType}, + reflect.TypeOf((*ast.ReturnStmt)(nil)): {g.rewriteReturnStmt}, + reflect.TypeOf((*ast.ArrayType)(nil)): {g.rewriteArrayType}, + reflect.TypeOf((*ast.ChanType)(nil)): {g.rewriteChanType}, + reflect.TypeOf((*ast.MapType)(nil)): {g.rewriteMapType}, + reflect.TypeOf((*ast.FuncType)(nil)): {g.rewriteFuncType}, } for k, v := range rewriters { @@ -198,109 +207,11 @@ func (g *GenX) rewrite(node *xast.Node) *xast.Node { break } } - return node - } - - switch n := n.(type) { - case *ast.Field: - n.Type = g.rewriteExprTypes("type:", n.Type) - - if len(n.Names) == 0 { - break - } - - names := n.Names[:0] - for _, n := range n.Names { - nn, ok := g.rewriters["field:"+n.Name] - if nn == "-" { - continue - } - if ok { - n.Name = nn - } else { - n.Name = g.irepl.Replace(n.Name) - } - names = append(names, n) - - } - - if n.Names = names; len(n.Names) == 0 { - return node.Delete() - } - - case *ast.Comment: - for _, f := range g.CommentFilters { - if n.Text = f(n.Text); n.Text == "" { - return node.Delete() - } - } - - case *ast.KeyValueExpr: - if key := getIdent(n.Key); key != nil && g.rewriters["field:"+key.Name] == "-" { - return node.Delete() - } - - case *ast.SelectorExpr: - if x := getIdent(n.X); x != nil && n.Sel != nil { - if nv := g.rewriters["selector:."+n.Sel.Name]; nv != "" { - n.Sel.Name = nv - break - } - nv := g.rewriters["selector:"+x.Name+"."+n.Sel.Name] - if nv == "" { - if x.Name == g.pkgName { - x.Name = n.Sel.Name - return node.SetNode(x) - } - x.Name, n.Sel.Name = g.irepl.Replace(x.Name), g.irepl.Replace(n.Sel.Name) - break - } - if nv == "-" { - return node.Delete() - } - if xsel := strings.Split(nv, "."); len(xsel) == 2 { - x.Name, n.Sel.Name = xsel[0], xsel[1] - break - } else { - x.Name = nv - return node.SetNode(x) - } - - } - case *ast.InterfaceType: - if n.Methods != nil && len(n.Methods.List) == 0 { - if nt := g.rewriters["type:interface{}"]; nt != "" { - return node.SetNode(&ast.Ident{ - Name: nt, - }) - } - } - case *ast.ReturnStmt: - for i, r := range n.Results { - if rt := getIdent(r); rt != nil && rt.Name == "nil" { - crt := cleanUpName.ReplaceAllString(g.curReturnTypes[i], "") - if _, ok := g.zeroTypes[crt]; ok { - g.zeroTypes[crt] = true - rt.Name = "zero_" + cleanUpName.ReplaceAllString(crt, "") - } - } - } - case *ast.File: - } return node } -func indexOf(ss []string, v string) int { - for i, s := range ss { - if s == v { - return i - } - } - return -1 -} - func (g *GenX) shouldNukeFuncBody(bs *ast.BlockStmt) (found bool) { if bs == nil { return @@ -310,6 +221,20 @@ func (g *GenX) shouldNukeFuncBody(bs *ast.BlockStmt) (found bool) { return false } switch n := n.(type) { + case *ast.SelectorExpr: + x := getIdent(n.X) + if x == nil { + break + } + if x.Obj != nil && x.Obj.Type != nil { + ot := getIdent(x.Obj.Type) + if found = ot != nil && g.rewriters["type:"+ot.Name] == "-"; found { + return false + } + } + if found = g.rewriters["field:"+n.Sel.Name] == "-"; found { + return false + } case *ast.Ident: if found = g.rewriters["type:"+n.Name] == "-"; found { return false @@ -354,17 +279,7 @@ func (g *GenX) rewriteExprTypes(prefix string, ex ast.Expr) ast.Expr { if t.Elt = g.rewriteExprTypes(prefix, t.Elt); t.Elt == nil { return nil } - case *ast.ArrayType: - if t.Elt = g.rewriteExprTypes(prefix, t.Elt); t.Elt == nil { - return nil - } - case *ast.MapType: - if t.Key = g.rewriteExprTypes(prefix, t.Key); t.Key == nil { - return nil - } - if t.Value = g.rewriteExprTypes(prefix, t.Value); t.Value == nil { - return nil - } + case *ast.FuncType: if t.Params != nil { for _, p := range t.Params.List { @@ -388,7 +303,7 @@ func (g *GenX) rewriteExprTypes(prefix string, ex ast.Expr) ast.Expr { return ex } -func getIdent(ex ast.Expr) *ast.Ident { +func getIdent(ex interface{}) *ast.Ident { switch ex := ex.(type) { case *ast.Ident: return ex diff --git a/rewriters.go b/rewriters.go index 176bd4d..92bf24f 100644 --- a/rewriters.go +++ b/rewriters.go @@ -3,6 +3,7 @@ package genx import ( "go/ast" "log" + "strings" "github.com/OneOfOne/xast" ) @@ -36,6 +37,7 @@ func (g *GenX) rewriteField(node *xast.Node) *xast.Node { return node } + func (g *GenX) rewriteTypeSpec(node *xast.Node) *xast.Node { n := node.Node().(*ast.TypeSpec) if t := getIdent(n.Name); t != nil { @@ -70,6 +72,140 @@ func (g *GenX) rewriteIdent(node *xast.Node) *xast.Node { return node } +func (g *GenX) rewriteArrayType(node *xast.Node) *xast.Node { + n := node.Node().(*ast.ArrayType) + if x := getIdent(n.Elt); x != nil && g.rewriters["type:"+x.Name] == "-" { + node.Parent().Delete() + return node.Delete() + } + return node + +} + +func (g *GenX) rewriteChanType(node *xast.Node) *xast.Node { + n := node.Node().(*ast.ChanType) + if x := getIdent(n.Value); x != nil && g.rewriters["type:"+x.Name] == "-" { + node.Parent().Delete() + return node.Delete() + } + return node +} + +func (g *GenX) rewriteFuncType(node *xast.Node) *xast.Node { + n := node.Node().(*ast.FuncType) + if n.Params != nil { + for _, p := range n.Params.List { + if x := getIdent(p.Type); x != nil && g.rewriters["type:"+x.Name] == "-" { + node.Parent().Delete() + return node.Delete() + } + } + } + if n.Results != nil { + g.curReturnTypes = g.curReturnTypes[:0] + for _, p := range n.Results.List { + if x := getIdent(p.Type); x != nil && g.rewriters["type:"+x.Name] == "-" { + return node.Delete() + } + if rt := getIdent(p.Type); rt != nil { + g.curReturnTypes = append(g.curReturnTypes, rt.Name) + } + } + } + + return node +} + +func (g *GenX) rewriteMapType(node *xast.Node) *xast.Node { + n := node.Node().(*ast.MapType) + if x := getIdent(n.Key); x != nil && g.rewriters["type:"+x.Name] == "-" { + node.Parent().Delete() + return node.Delete() + } + if x := getIdent(n.Value); x != nil && g.rewriters["type:"+x.Name] == "-" { + node.Parent().Delete() + return node.Delete() + } + return node +} + +func (g *GenX) rewriteComment(node *xast.Node) *xast.Node { + n := node.Node().(*ast.Comment) + for _, f := range g.CommentFilters { + if n.Text = f(n.Text); n.Text == "" { + return node.Delete() + } + } + return node +} + +func (g *GenX) rewriteKeyValueExpr(node *xast.Node) *xast.Node { + n := node.Node().(*ast.KeyValueExpr) + if key := getIdent(n.Key); key != nil && g.rewriters["field:"+key.Name] == "-" { + return node.Delete() + } + return node +} + +func (g *GenX) rewriteReturnStmt(node *xast.Node) *xast.Node { + n := node.Node().(*ast.ReturnStmt) + for i, r := range n.Results { + if rt := getIdent(r); rt != nil && rt.Name == "nil" { + crt := cleanUpName.ReplaceAllString(g.curReturnTypes[i], "") + if _, ok := g.zeroTypes[crt]; ok { + g.zeroTypes[crt] = true + rt.Name = "zero_" + cleanUpName.ReplaceAllString(crt, "") + } + } + } + return node +} + +func (g *GenX) rewriteInterfaceType(node *xast.Node) *xast.Node { + n := node.Node().(*ast.InterfaceType) + if n.Methods != nil && len(n.Methods.List) == 0 { + if nt := g.rewriters["type:interface{}"]; nt != "" { + return node.SetNode(&ast.Ident{ + Name: nt, + }) + } + } + return node +} + +func (g *GenX) rewriteSelectorExpr(node *xast.Node) *xast.Node { + n := node.Node().(*ast.SelectorExpr) + x := getIdent(n.X) + + if x == nil || n.Sel == nil { + return node + } + if nv := g.rewriters["selector:."+n.Sel.Name]; nv != "" { + n.Sel.Name = nv + return node + } + nv := g.rewriters["selector:"+x.Name+"."+n.Sel.Name] + if nv == "" { + if x.Name == g.pkgName { + x.Name = n.Sel.Name + return node.SetNode(x) + } + x.Name, n.Sel.Name = g.irepl.Replace(x.Name), g.irepl.Replace(n.Sel.Name) + return node + } + if nv == "-" { + return node.Delete() + } + if xsel := strings.Split(nv, "."); len(xsel) == 2 { + x.Name, n.Sel.Name = xsel[0], xsel[1] + } else { + x.Name = nv + return node.SetNode(x) + } + + return node +} + func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { n := node.Node().(*ast.FuncDecl) if t := getIdent(n.Name); t != nil { From 568c571410fe08922327542d3e4dd0d7286e644b Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 05:31:49 +0200 Subject: [PATCH 6/9] few tests and bug fixes and coverage update. Updates #4 --- all_types.go | 27 ++++++------ cmd/genx/main.go | 14 +++--- genx.go | 70 ++++++------------------------ rewriters.go | 108 +++++++++++++++++++++++++++++++++------------- rewriters_test.go | 89 ++++++++++++++++++++++++++++++++++++++ utils.go | 7 +++ 6 files changed, 206 insertions(+), 109 deletions(-) create mode 100644 rewriters_test.go diff --git a/all_types.go b/all_types.go index 3c23b5e..694b2bc 100644 --- a/all_types.go +++ b/all_types.go @@ -1,5 +1,3 @@ -// +build ignore - package genx import "github.com/cheekybits/genny/generic" @@ -11,18 +9,20 @@ type TypeWithKT struct { K KT V VT - Call func(k KT) VT - RemoveMeToo VT // comment + Call func(k KT) VT + RemoveMe VT // RemoveMe comment + + Iface interface{} } -// RemoveMe comment -func (b *TypeWithKT) RemoveMe() { +// MethodWithPtr comment +func (b *TypeWithKT) MethodWithPtr() { b.K = new(KT) b.V = new(VT) } -// some comment -func (b TypeWithKT) RemoveMe2() { +// MethodWithValue comment +func (b TypeWithKT) MethodWithValue() { b.K = new(KT) b.V = new(VT) } @@ -32,13 +32,15 @@ func DoRes() *TypeWithKT { return nil } func DoBoth(b *TypeWithKT) *TypeWithKT { return nil } func DoStuff(k ...KT) VT { - b := &TypeWithKT{} + b := &TypeWithKT{ + RemoveMe: nil, + } return b.Call(k[0]) } -func DoStuff2(k ...KT) VT { +func DoStuffTwo(k ...KT) VT { var b TypeWithKT - return b.RemoveMe2 + return b.RemoveMe } var ( @@ -52,8 +54,7 @@ var ( ktS []KT vtS []VT s []*TypeWithKT - m map[TypeWithKT]int - mp map[*TypeWithKT]int + mp map[*TypeWithKT]interface{} ) func XXX(vs ...interface{}) interface{} { diff --git a/cmd/genx/main.go b/cmd/genx/main.go index 42de530..457600f 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -40,14 +40,10 @@ func main() { app := &cli.App{ Name: "genx", Version: "v0.5", - - Commands: []*cli.Command{ - // { - // Name: "implement", - // Aliases: []string{"impl", "i"}, - // Flags: []cli.Flag{}, - // Action: runImpl, - // }, + Authors: []*cli.Author{{ + Name: "Ahmed W.", + Email: "oneofone+genx gmail com", + }, }, Flags: []cli.Flag{ &cli.StringFlag{ @@ -275,7 +271,7 @@ func goListThenGet(ctx *cli.Context, tags []string, path string) (out string, er } args := []string{"-tags", strings.Join(tags, " ")} - args = append(args, ctx.StringSlice("go-flags")...) + args = append(args, ctx.StringSlice("goFlags")...) args = append(args, dir) diff --git a/genx.go b/genx.go index 8c55efd..32b186d 100644 --- a/genx.go +++ b/genx.go @@ -63,6 +63,8 @@ func New(pkgName string, rewriters map[string]string) *GenX { reflect.TypeOf((*ast.ChanType)(nil)): {g.rewriteChanType}, reflect.TypeOf((*ast.MapType)(nil)): {g.rewriteMapType}, reflect.TypeOf((*ast.FuncType)(nil)): {g.rewriteFuncType}, + reflect.TypeOf((*ast.StarExpr)(nil)): {g.rewriteStarExpr}, + reflect.TypeOf((*ast.Ellipsis)(nil)): {g.rewriteEllipsis}, } for k, v := range rewriters { @@ -197,6 +199,8 @@ func (g *GenX) process(idx int, fset *token.FileSet, name string, file *ast.File func (g *GenX) rewrite(node *xast.Node) *xast.Node { n := node.Node() if g.visited[n] { + //dbg.DumpWithDepth(4, n) + // log.Printf("%T %#+v", n, node.Parent().Node()) return node } g.visited[n] = true @@ -216,11 +220,20 @@ func (g *GenX) shouldNukeFuncBody(bs *ast.BlockStmt) (found bool) { if bs == nil { return } + ast.Inspect(bs, func(n ast.Node) bool { if found { return false } switch n := n.(type) { + case *ast.KeyValueExpr: + x := getIdent(n.Key) + if x == nil { + break + } + if found = g.rewriters["field:"+x.Name] == "-"; found { + return false + } case *ast.SelectorExpr: x := getIdent(n.X) if x == nil { @@ -246,63 +259,6 @@ func (g *GenX) shouldNukeFuncBody(bs *ast.BlockStmt) (found bool) { return } -func (g *GenX) rewriteExprTypes(prefix string, ex ast.Expr) ast.Expr { - if g.visited[ex] { - return ex - } - g.visited[ex] = true - - switch t := ex.(type) { - case *ast.InterfaceType: - if nt := g.rewriters[prefix+"interface{}"]; nt != "" { - if nt == "-" { - return nil - } - ex = &ast.Ident{ - Name: nt, - } - } - case *ast.Ident: - if nt := g.rewriters[prefix+t.Name]; nt != "" { - if nt == "-" { - return nil - } - t.Name = nt - } else { - t.Name = g.irepl.Replace(t.Name) - } - case *ast.StarExpr: - if t.X = g.rewriteExprTypes(prefix, t.X); t.X == nil { - return nil - } - case *ast.Ellipsis: - if t.Elt = g.rewriteExprTypes(prefix, t.Elt); t.Elt == nil { - return nil - } - - case *ast.FuncType: - if t.Params != nil { - for _, p := range t.Params.List { - if p.Type = g.rewriteExprTypes(prefix, p.Type); p.Type == nil { - return nil - } - } - } - if t.Results != nil { - g.curReturnTypes = g.curReturnTypes[:0] - for _, p := range t.Results.List { - if p.Type = g.rewriteExprTypes(prefix, p.Type); p.Type == nil { - return nil - } - if rt := getIdent(p.Type); rt != nil { - g.curReturnTypes = append(g.curReturnTypes, rt.Name) - } - } - } - } - return ex -} - func getIdent(ex interface{}) *ast.Ident { switch ex := ex.(type) { case *ast.Ident: diff --git a/rewriters.go b/rewriters.go index 92bf24f..808efe6 100644 --- a/rewriters.go +++ b/rewriters.go @@ -10,7 +10,11 @@ import ( func (g *GenX) rewriteField(node *xast.Node) *xast.Node { n := node.Node().(*ast.Field) - n.Type = g.rewriteExprTypes("type:", n.Type) + nn := g.rewrite(xast.NewNode(node, n.Type)) + if nn.Canceled() { + return node.Delete() + } + n.Type = nn.Node().(ast.Expr) if len(n.Names) == 0 { return node @@ -30,7 +34,6 @@ func (g *GenX) rewriteField(node *xast.Node) *xast.Node { names = append(names, n) } - if n.Names = names; len(n.Names) == 0 { return node.Delete() } @@ -41,19 +44,21 @@ func (g *GenX) rewriteField(node *xast.Node) *xast.Node { func (g *GenX) rewriteTypeSpec(node *xast.Node) *xast.Node { n := node.Node().(*ast.TypeSpec) if t := getIdent(n.Name); t != nil { - nn, ok := g.rewriters["type:"+t.Name] + nn := g.rewrite(xast.NewNode(node, n.Type)) + if nn.Canceled() { + g.rewriters["type:"+t.Name] = "-" + return node.Delete() + } + n.Type = nn.Node().(ast.Expr) + + tn, ok := g.rewriters["type:"+t.Name] if !ok { return node } - if nn == "-" || nn == "" { + if tn == "-" { return node.Delete() } - switch n.Type.(type) { - case *ast.SelectorExpr, *ast.InterfaceType, *ast.Ident: - return node.Delete() - default: - t.Name = nn - } + t.Name = tn } return node @@ -63,7 +68,7 @@ func (g *GenX) rewriteIdent(node *xast.Node) *xast.Node { n := node.Node().(*ast.Ident) if t, ok := g.rewriters["type:"+n.Name]; ok { if t == "-" { - return node + return deleteWithParent(node) } n.Name = t } else { @@ -74,10 +79,33 @@ func (g *GenX) rewriteIdent(node *xast.Node) *xast.Node { func (g *GenX) rewriteArrayType(node *xast.Node) *xast.Node { n := node.Node().(*ast.ArrayType) - if x := getIdent(n.Elt); x != nil && g.rewriters["type:"+x.Name] == "-" { - node.Parent().Delete() - return node.Delete() + nn := g.rewrite(xast.NewNode(node, n.Elt)) + if nn.Canceled() { + return deleteWithParent(node) } + n.Elt = nn.Node().(ast.Expr) + return node + +} + +func (g *GenX) rewriteEllipsis(node *xast.Node) *xast.Node { + n := node.Node().(*ast.Ellipsis) + nn := g.rewrite(xast.NewNode(node, n.Elt)) + if nn.Canceled() { + return deleteWithParent(node) + } + n.Elt = nn.Node().(ast.Expr) + return node + +} + +func (g *GenX) rewriteStarExpr(node *xast.Node) *xast.Node { + n := node.Node().(*ast.StarExpr) + nn := g.rewrite(xast.NewNode(node, n.X)) + if nn.Canceled() { + return deleteWithParent(node) + } + n.X = nn.Node().(ast.Expr) return node } @@ -85,8 +113,7 @@ func (g *GenX) rewriteArrayType(node *xast.Node) *xast.Node { func (g *GenX) rewriteChanType(node *xast.Node) *xast.Node { n := node.Node().(*ast.ChanType) if x := getIdent(n.Value); x != nil && g.rewriters["type:"+x.Name] == "-" { - node.Parent().Delete() - return node.Delete() + return deleteWithParent(node) } return node } @@ -95,18 +122,22 @@ func (g *GenX) rewriteFuncType(node *xast.Node) *xast.Node { n := node.Node().(*ast.FuncType) if n.Params != nil { for _, p := range n.Params.List { - if x := getIdent(p.Type); x != nil && g.rewriters["type:"+x.Name] == "-" { - node.Parent().Delete() + nn := g.rewrite(xast.NewNode(node, p.Type)) + if nn.Canceled() { return node.Delete() } + p.Type = nn.Node().(ast.Expr) } } + if n.Results != nil { g.curReturnTypes = g.curReturnTypes[:0] for _, p := range n.Results.List { - if x := getIdent(p.Type); x != nil && g.rewriters["type:"+x.Name] == "-" { + nn := g.rewrite(xast.NewNode(node, p.Type)) + if nn.Canceled() { return node.Delete() } + p.Type = nn.Node().(ast.Expr) if rt := getIdent(p.Type); rt != nil { g.curReturnTypes = append(g.curReturnTypes, rt.Name) } @@ -118,14 +149,16 @@ func (g *GenX) rewriteFuncType(node *xast.Node) *xast.Node { func (g *GenX) rewriteMapType(node *xast.Node) *xast.Node { n := node.Node().(*ast.MapType) - if x := getIdent(n.Key); x != nil && g.rewriters["type:"+x.Name] == "-" { - node.Parent().Delete() - return node.Delete() + nn := g.rewrite(xast.NewNode(node, n.Key)) + if nn.Canceled() { + return deleteWithParent(node) } - if x := getIdent(n.Value); x != nil && g.rewriters["type:"+x.Name] == "-" { - node.Parent().Delete() - return node.Delete() + n.Key = nn.Node().(ast.Expr) + nn = g.rewrite(xast.NewNode(node, n.Value)) + if nn.Canceled() { + return deleteWithParent(node) } + n.Value = nn.Node().(ast.Expr) return node } @@ -141,9 +174,18 @@ func (g *GenX) rewriteComment(node *xast.Node) *xast.Node { func (g *GenX) rewriteKeyValueExpr(node *xast.Node) *xast.Node { n := node.Node().(*ast.KeyValueExpr) - if key := getIdent(n.Key); key != nil && g.rewriters["field:"+key.Name] == "-" { + if t := getIdent(n.Key); t != nil { + if g.rewriters["type:"+t.Name] == "-" || g.rewriters["field:"+t.Name] == "-" { + return node.Delete() + } + } + + nn := g.rewrite(xast.NewNode(node, n.Value)) + if nn.Canceled() { return node.Delete() } + n.Value = nn.Node().(ast.Expr) + return node } @@ -164,7 +206,10 @@ func (g *GenX) rewriteReturnStmt(node *xast.Node) *xast.Node { func (g *GenX) rewriteInterfaceType(node *xast.Node) *xast.Node { n := node.Node().(*ast.InterfaceType) if n.Methods != nil && len(n.Methods.List) == 0 { - if nt := g.rewriters["type:interface{}"]; nt != "" { + if nt, ok := g.rewriters["type:interface{}"]; ok { + if nt == "-" { + return deleteWithParent(node) + } return node.SetNode(&ast.Ident{ Name: nt, }) @@ -184,6 +229,7 @@ func (g *GenX) rewriteSelectorExpr(node *xast.Node) *xast.Node { n.Sel.Name = nv return node } + nv := g.rewriters["selector:"+x.Name+"."+n.Sel.Name] if nv == "" { if x.Name == g.pkgName { @@ -193,9 +239,11 @@ func (g *GenX) rewriteSelectorExpr(node *xast.Node) *xast.Node { x.Name, n.Sel.Name = g.irepl.Replace(x.Name), g.irepl.Replace(n.Sel.Name) return node } + if nv == "-" { return node.Delete() } + if xsel := strings.Split(nv, "."); len(xsel) == 2 { x.Name, n.Sel.Name = xsel[0], xsel[1] } else { @@ -234,11 +282,11 @@ func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { } } - if t, ok := g.rewriteExprTypes("type:", n.Type).(*ast.FuncType); ok { - n.Type = t - } else { + nn := g.rewrite(xast.NewNode(node, n.Type)) + if nn.Canceled() { return node.Delete() } + n.Type = nn.Node().(*ast.FuncType) if g.shouldNukeFuncBody(n.Body) { return node.Delete() diff --git a/rewriters_test.go b/rewriters_test.go new file mode 100644 index 0000000..ce32ecb --- /dev/null +++ b/rewriters_test.go @@ -0,0 +1,89 @@ +package genx_test + +import ( + "io/ioutil" + "log" + "regexp" + "testing" + + "github.com/OneOfOne/genx" +) + +func init() { + log.SetFlags(log.Lshortfile) +} +func TestAllTypes(t *testing.T) { + testCases := []struct { + Name string + Input map[string]string + FailIfMatch *regexp.Regexp + }{ + { + "Delete:Type:interface{}", + map[string]string{ + "type:interface{}": "-", + }, + regexp.MustCompile(`interface\{\}`), + }, + { + "Delete:Type:TypeWithKT", + map[string]string{"type:TypeWithKT": "-"}, + regexp.MustCompile(`TypeWithKT`), + }, + { + "Delete:Field:RemoveMe", + map[string]string{"field:RemoveMe": "-"}, + regexp.MustCompile(`RemoveMe`), + }, + { + "Rename:Field:Call", + map[string]string{"field:Call": "NewFuncName"}, + regexp.MustCompile(`\.Call`), + }, + { + "Type:interface{}=int", + map[string]string{ + "type:VT": "int", + "type:interface{}": "int", + }, + regexp.MustCompile(`interface\{\}`), + }, + { + "Type:KT=int,VT=int", + map[string]string{ + "type:KT": "uint64", + "type:VT": "int", + }, + regexp.MustCompile(`KT|VT`), + }, + { + "Func:DoStuff", + map[string]string{ + "func:DoStuff": "-", + }, + // regexp.MustCompile(`DoStuff\(|\sTwo\(`), // TODO: fix #5 + regexp.MustCompile(`DoStuff\(`), + }, + } + src, err := ioutil.ReadFile("./all_types.go") + fatalIf(t, err) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + g := genx.New("", tc.Input) + pf, err := g.Parse("src.go", src) + if err != nil { + t.Errorf("%+v\n%s", err, pf.Src) + return + } + if tc.FailIfMatch.Match(pf.Src) { + t.Errorf("%s matched :(\n%s", tc.FailIfMatch, pf.Src) + } + }) + } +} + +func fatalIf(t *testing.T, err error) { + if err != nil { + t.Fatal(err) + } +} diff --git a/utils.go b/utils.go index 5bcdabd..403255f 100644 --- a/utils.go +++ b/utils.go @@ -6,6 +6,8 @@ package genx import ( "regexp" "sort" + + "github.com/OneOfOne/xast" ) func (g *GenX) OrderedRewriters() (out []string) { @@ -49,3 +51,8 @@ func regexpReplacer(src string, repl string) func(string) string { return re.ReplaceAllString(in, repl) } } + +func deleteWithParent(n *xast.Node) *xast.Node { + n.Parent().Delete() + return n.Delete() +} From a40c58192fc2a7bd7880375e778770d31c16f5da Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 05:39:15 +0200 Subject: [PATCH 7/9] clean up --- all_types.go | 6 ++++++ genx.go | 28 ---------------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/all_types.go b/all_types.go index 694b2bc..508d469 100644 --- a/all_types.go +++ b/all_types.go @@ -1,3 +1,5 @@ +// +build ignore + package genx import "github.com/cheekybits/genny/generic" @@ -43,6 +45,10 @@ func DoStuffTwo(k ...KT) VT { return b.RemoveMe } +func ReturnVT() VT { + return nil +} + var ( m map[KT]VT ktCh chan KT diff --git a/genx.go b/genx.go index 32b186d..3377af0 100644 --- a/genx.go +++ b/genx.go @@ -45,7 +45,6 @@ func New(pkgName string, rewriters map[string]string) *GenX { irepl: geireplacer(rewriters, true), zeroTypes: map[string]bool{}, BuildTags: []string{"genx"}, - // CommentFilters: []func(string) string{}, } g.rewriteFuncs = map[reflect.Type][]procFunc{ @@ -290,33 +289,6 @@ func geireplacer(m map[string]string, ident bool) *strings.Replacer { return strings.NewReplacer(kv...) } -var replRe = regexp.MustCompile(`\b([\w\d_]+)\b`) - -func reRepl(m map[string]string, ident bool) func(src string) string { - kv := map[string]string{} - for k, v := range m { - k = k[strings.Index(k, ":")+1:] - if ident { - if a := builtins[v]; a != "" { - v = a - } else { - v = cleanUpName.ReplaceAllString(strings.Title(v), "") - } - } - - kv[k] = v - } - return func(src string) string { - re := replRe.Copy() - return re.ReplaceAllStringFunc(src, func(in string) string { - if v := kv[in]; v != "" { - return v - } - return in - }) - } -} - var builtins = map[string]string{ "string": "String", "byte": "Byte", From 797d71e8abf62e67479e40c00d6783c5f900b1c0 Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 06:14:01 +0200 Subject: [PATCH 8/9] bug fix with removing type blocks --- all_types.go | 7 +++++-- cmd/genx/main.go | 16 ++++++++++------ genx.go | 1 + rewriters.go | 19 +++++++------------ 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/all_types.go b/all_types.go index 508d469..566037e 100644 --- a/all_types.go +++ b/all_types.go @@ -4,9 +4,12 @@ package genx import "github.com/cheekybits/genny/generic" -type KT generic.Type -type VT interface{} +type T generic.Type +type ( + KT interface{} + VT interface{} +) type TypeWithKT struct { K KT V VT diff --git a/cmd/genx/main.go b/cmd/genx/main.go index 457600f..533dc6e 100644 --- a/cmd/genx/main.go +++ b/cmd/genx/main.go @@ -39,6 +39,7 @@ func main() { app := &cli.App{ Name: "genx", + Usage: "Generics For Go, Yet Again.", Version: "v0.5", Authors: []*cli.Author{{ Name: "Ahmed W.", @@ -51,8 +52,8 @@ func main() { Usage: "alias for -pkg github.com/OneOfOne/genx/seeds/`seed-name`", }, &cli.StringFlag{ - Name: "input", - Aliases: []string{"i"}, + Name: "in", + Aliases: []string{"f"}, Usage: "`file` to process, use `-` to process stdin.", }, @@ -93,7 +94,7 @@ func main() { }, &cli.StringFlag{ - Name: "output", + Name: "out", Aliases: []string{"o"}, Value: "/dev/stdout", Usage: "output dir if parsing a package or output filename if you want the output to be merged.", @@ -185,7 +186,7 @@ func runGen(c *cli.Context) error { } var ( - outPath = c.String("output") + outPath = c.String("out") mergeFiles bool inPkg string @@ -203,6 +204,8 @@ func runGen(c *cli.Context) error { if seed := c.String("seed"); seed != "" { inPkg = "github.com/OneOfOne/genx/seeds/" + seed mergeFiles = true + } else { + inPkg = c.String("package") } if inPkg != "" { @@ -227,7 +230,7 @@ func runGen(c *cli.Context) error { if err != nil { return cli.Exit(err, 1) } - } else if inFile := c.String("input"); inFile != "" { + } else if inFile := c.String("in"); inFile != "" { if inFile != "-" && inFile != "/dev/stdin" { out, err := goListThenGet(c, g.BuildTags, inFile) if err != nil { @@ -245,7 +248,8 @@ func runGen(c *cli.Context) error { return cli.Exit(err, 1) } } else { - cli.ShowAppHelpAndExit(c, 1) + log.Println(c.FlagNames()) + // cli.ShowAppHelpAndExit(c, 1) } return nil diff --git a/genx.go b/genx.go index 3377af0..dd8799c 100644 --- a/genx.go +++ b/genx.go @@ -225,6 +225,7 @@ func (g *GenX) shouldNukeFuncBody(bs *ast.BlockStmt) (found bool) { return false } switch n := n.(type) { + // BUG: maybe? should we delete the func if we remove a field? case *ast.KeyValueExpr: x := getIdent(n.Key) if x == nil { diff --git a/rewriters.go b/rewriters.go index 808efe6..c9a8f51 100644 --- a/rewriters.go +++ b/rewriters.go @@ -2,7 +2,6 @@ package genx import ( "go/ast" - "log" "strings" "github.com/OneOfOne/xast" @@ -46,11 +45,9 @@ func (g *GenX) rewriteTypeSpec(node *xast.Node) *xast.Node { if t := getIdent(n.Name); t != nil { nn := g.rewrite(xast.NewNode(node, n.Type)) if nn.Canceled() { - g.rewriters["type:"+t.Name] = "-" return node.Delete() } n.Type = nn.Node().(ast.Expr) - tn, ok := g.rewriters["type:"+t.Name] if !ok { return node @@ -59,6 +56,7 @@ func (g *GenX) rewriteTypeSpec(node *xast.Node) *xast.Node { return node.Delete() } t.Name = tn + return node.Delete() } return node @@ -68,7 +66,7 @@ func (g *GenX) rewriteIdent(node *xast.Node) *xast.Node { n := node.Node().(*ast.Ident) if t, ok := g.rewriters["type:"+n.Name]; ok { if t == "-" { - return deleteWithParent(node) + return node.Delete() } n.Name = t } else { @@ -267,9 +265,6 @@ func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { if recv := n.Recv; recv != nil && len(recv.List) == 1 { t := getIdent(recv.List[0].Type) - if t == nil { - log.Panicf("hmm... %#+v", recv.List[0].Type) - } nn, ok := g.rewriters["type:"+t.Name] if nn == "-" { @@ -282,20 +277,20 @@ func (g *GenX) rewriteFuncDecl(node *xast.Node) *xast.Node { } } - nn := g.rewrite(xast.NewNode(node, n.Type)) - if nn.Canceled() { + if g.shouldNukeFuncBody(n.Body) { return node.Delete() } - n.Type = nn.Node().(*ast.FuncType) - if g.shouldNukeFuncBody(n.Body) { + nn := g.rewrite(xast.NewNode(node, n.Type)) + if nn.Canceled() { return node.Delete() } + n.Type = nn.Node().(*ast.FuncType) return node } -var nukeGenxComments = regexpReplacer(`// \+build [!]?genx.*|//go:generate genx`, "") +var nukeGenxComments = regexpReplacer(`// \+build.*|//go:generate.*`, "") func (g *GenX) rewriteFile(node *xast.Node) *xast.Node { n := node.Node().(*ast.File) From e09bea0787d7d5140f3795a856354268f14b9414 Mon Sep 17 00:00:00 2001 From: OneOfOne Date: Thu, 31 Aug 2017 06:17:19 +0200 Subject: [PATCH 9/9] update readme --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c9dd5c6..9cfefbf 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ For Example I needed to remove a field from the struct and change all usage of i ``` ➤ genx -h NAME: - genx - A new cli application + genx - Generics For Go, Yet Again. USAGE: genx [global options] command [command options] [arguments...] @@ -170,21 +170,24 @@ USAGE: VERSION: v0.5 +AUTHOR: + Ahmed W. gmail com> + COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --seed seed-name alias for -pkg github.com/OneOfOne/genx/seeds/seed-name - --input file, -i file, -f file file to process, use `-` to process stdin. - --package package, -pkg package package to process + --in file, -f file file to process, use `-` to process stdin. + --package package, --pkg package package to process. --name name, -n name package name to use for output, uses the input package's name by default. - --type type, -t type generic type names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType) - --selector selector, -s selector selectors to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something') - --field field, -fld field struct fields to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc) - --func func, -fn func functions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse) + --type type, -t type generic type names to remove or rename (ex: -t 'KV=string,KV=interface{}' -t RemoveThisType). + --selector selector, -s selector selectors to remove or rename (ex: -s 'cm.HashFn=hashers.Fnv32' -s 'x.Call=Something'). + --field field, --fld field struct fields to remove or rename (ex: -fld HashFn -fld privateFunc=PublicFunc). + --func func, --fn func functions to remove or rename (ex: -fn NotNeededFunc -fn Something=SomethingElse). + --out value, -o value output dir if parsing a package or output filename if you want the output to be merged. (default: "/dev/stdout") --tags value go extra build tags, used for parsing and automatically passed to any go subcommands. - --go-flags flags extra flags to pass to go subcommands flags (ex: --goFlags '-race') - --output value, -o value output dir if parsing a package or output filename if you want the output to be merged. (default: "/dev/stdout") + --goFlags flags extra flags to pass to go subcommands flags (ex: --goFlags '-race') --get go get the package if it doesn't exist (default: false) --verbose, -v verbose output (default: false) --help, -h show help (default: false)