Last active
December 27, 2019 10:41
-
-
Save egonelbre/5589690e7c25cc485f47ac74bfb53544 to your computer and use it in GitHub Desktop.
generate-aliases generates aliases for specified package
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module generate-aliases | |
go 1.13 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"flag" | |
"fmt" | |
"go/ast" | |
"go/parser" | |
"go/token" | |
"go/types" | |
"io" | |
"io/ioutil" | |
"log" | |
"os" | |
"path" | |
"path/filepath" | |
"strings" | |
) | |
func main() { | |
pkgpath := flag.String("package", "", "full package path") | |
pkgdir := flag.String("dir", "", "package directory") | |
verbose := flag.Bool("verbose", false, "verbose output") | |
flag.Parse() | |
if *pkgpath == "" || *pkgdir == "" { | |
flag.Usage() | |
os.Exit(1) | |
} | |
cfg := &Config{ | |
PackagePath: *pkgpath, | |
PackageID: path.Base(*pkgpath), | |
} | |
var buf bytes.Buffer | |
out := &buf | |
var errout io.Writer | |
errout = ioutil.Discard | |
if *verbose { | |
errout = os.Stderr | |
} | |
fmt.Fprint(out, "// Copyright (C) 2019 Storj Labs, Inc.\n") | |
fmt.Fprint(out, "// See LICENSE for copying information.\n\n") | |
fmt.Fprintf(out, "package %s\n\n", cfg.PackageID) | |
fmt.Fprintf(out, "import %q\n\n", cfg.PackagePath) | |
err := filepath.Walk(*pkgdir, func(path string, info os.FileInfo, err error) error { | |
if err != nil || info.IsDir() { | |
if path == *pkgdir { | |
return nil | |
} | |
return filepath.SkipDir | |
} | |
if filepath.Ext(path) != ".go" { | |
return nil | |
} | |
if strings.HasSuffix(path, "_test.go") { | |
return nil | |
} | |
fset := token.NewFileSet() | |
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments) | |
if err != nil { | |
return err | |
} | |
for _, decl := range f.Decls { | |
switch decl := decl.(type) { | |
case *ast.FuncDecl: | |
cfg.WriteFuncAlias(out, decl) | |
case *ast.GenDecl: | |
switch decl.Tok { | |
case token.TYPE: | |
cfg.WriteTypeAlias(out, decl) | |
case token.CONST: | |
cfg.WriteConstAlias(out, decl) | |
default: | |
fmt.Fprintf(errout, "ignored %T %v %#v\n", decl, decl.Tok, decl) | |
} | |
default: | |
fmt.Fprintf(errout, "ignored %T %#v\n", decl, decl) | |
} | |
} | |
return nil | |
}) | |
fmt.Println(out.String()) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
type Config struct { | |
PackagePath string | |
PackageID string | |
} | |
func (cfg *Config) WriteTypeAlias(w io.Writer, decl *ast.GenDecl) { | |
exportedCount := cfg.exportedCount(decl.Specs) | |
if exportedCount == 0 { | |
return | |
} | |
if decl.Doc != nil { | |
for _, line := range decl.Doc.List { | |
fmt.Fprintf(w, "%s\n", line.Text) | |
} | |
} | |
multispec := exportedCount > 1 | |
if multispec { | |
fmt.Fprint(w, "type (\n") | |
} | |
for _, spec := range decl.Specs { | |
spec := spec.(*ast.TypeSpec) | |
if !spec.Name.IsExported() { | |
continue | |
} | |
if spec.Doc != nil { | |
for _, line := range spec.Doc.List { | |
if multispec { | |
fmt.Fprint(w, "\t") | |
} | |
fmt.Fprintf(w, "%s\n", line.Text) | |
} | |
} | |
if !multispec { | |
fmt.Fprintf(w, "type %s = %s.%s\n", spec.Name, cfg.PackageID, spec.Name) | |
} else { | |
fmt.Fprintf(w, "\t%s = %s.%s\n", spec.Name, cfg.PackageID, spec.Name) | |
} | |
} | |
if multispec { | |
fmt.Fprint(w, ")\n") | |
} | |
fmt.Fprint(w, "\n") | |
} | |
func (cfg *Config) WriteConstAlias(w io.Writer, decl *ast.GenDecl) { | |
exportedCount := cfg.exportedCount(decl.Specs) | |
if exportedCount == 0 { | |
return | |
} | |
if decl.Doc != nil { | |
for _, line := range decl.Doc.List { | |
fmt.Fprintf(w, "%s\n", line.Text) | |
} | |
} | |
multispec := exportedCount > 1 | |
if multispec { | |
fmt.Fprint(w, "const (\n") | |
} | |
for _, spec := range decl.Specs { | |
spec := spec.(*ast.ValueSpec) | |
if !spec.Names[0].IsExported() { | |
continue | |
} | |
if spec.Doc != nil { | |
for _, line := range spec.Doc.List { | |
if multispec { | |
fmt.Fprint(w, "\t") | |
} | |
fmt.Fprintf(w, "%s\n", line.Text) | |
} | |
} | |
if !multispec { | |
fmt.Fprintf(w, "const %s = %s.%s\n", spec.Names[0], cfg.PackageID, spec.Names[0]) | |
} else { | |
fmt.Fprintf(w, "\t%s = %s.%s\n", spec.Names[0], cfg.PackageID, spec.Names[0]) | |
} | |
} | |
if multispec { | |
fmt.Fprint(w, ")\n") | |
} | |
fmt.Fprint(w, "\n") | |
} | |
func (cfg *Config) exportedCount(specs []ast.Spec) int { | |
count := 0 | |
for _, spec := range specs { | |
if spec, ok := spec.(*ast.TypeSpec); ok && spec.Name.IsExported() { | |
count++ | |
} | |
if spec, ok := spec.(*ast.ValueSpec); ok && spec.Names[0].IsExported() { | |
count++ | |
} | |
} | |
return count | |
} | |
func (cfg *Config) WriteFuncAlias(w io.Writer, decl *ast.FuncDecl) { | |
if decl.Recv != nil { | |
// skip methods | |
return | |
} | |
if !decl.Name.IsExported() { | |
// skip private | |
return | |
} | |
if strings.HasPrefix(decl.Name.String(), "Test") { | |
return | |
} | |
if decl.Doc != nil { | |
for _, line := range decl.Doc.List { | |
fmt.Fprintf(w, "%s\n", line.Text) | |
} | |
} | |
fmt.Fprintf(w, "func %s(", decl.Name) | |
for iparam, param := range decl.Type.Params.List { | |
if iparam > 0 { | |
fmt.Fprint(w, ", ") | |
} | |
for iname, name := range param.Names { | |
if iname > 0 { | |
fmt.Fprintf(w, ", ") | |
} | |
fmt.Fprint(w, name.String()) | |
} | |
fmt.Fprint(w, " ", types.ExprString(param.Type)) | |
} | |
fmt.Fprint(w, ")") | |
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { | |
fmt.Fprint(w, " (") | |
for iparam, param := range decl.Type.Results.List { | |
if iparam > 0 { | |
fmt.Fprint(w, ", ") | |
} | |
for iname, name := range param.Names { | |
if iname > 0 { | |
fmt.Fprintf(w, ", ") | |
} | |
fmt.Fprint(w, name.String()) | |
} | |
if len(param.Names) > 0 { | |
fmt.Fprint(w, " ") | |
} | |
fmt.Fprint(w, types.ExprString(param.Type)) | |
} | |
fmt.Fprint(w, ")") | |
} | |
fmt.Fprint(w, " {\n") | |
fmt.Fprint(w, "\t") | |
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { | |
fmt.Fprint(w, "return ") | |
} | |
fmt.Fprintf(w, "%s.%s(", cfg.PackageID, decl.Name) | |
first := true | |
for _, param := range decl.Type.Params.List { | |
for _, name := range param.Names { | |
if !first { | |
fmt.Fprint(w, ", ") | |
} | |
first = false | |
fmt.Fprint(w, name.String()) | |
} | |
if _, ok := param.Type.(*ast.Ellipsis); ok { | |
fmt.Fprint(w, "...") | |
} | |
} | |
fmt.Fprint(w, ")\n") | |
fmt.Fprint(w, "}\n") | |
fmt.Fprint(w, "\n") | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
func Example(a, b int64, xs ...int64) bool { | |
return false | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment