Skip to content

Instantly share code, notes, and snippets.

@egonelbre
Last active December 27, 2019 10:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egonelbre/5589690e7c25cc485f47ac74bfb53544 to your computer and use it in GitHub Desktop.
Save egonelbre/5589690e7c25cc485f47ac74bfb53544 to your computer and use it in GitHub Desktop.
generate-aliases generates aliases for specified package
module generate-aliases
go 1.13
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")
}
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