github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/gohanscript/tools/gen.go (about)

     1  // Copyright (C) 2016  Juniper Networks, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package main
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"go/ast"
    22  	"go/parser"
    23  	"go/token"
    24  	"io/ioutil"
    25  	"os"
    26  	"os/exec"
    27  	"path/filepath"
    28  	"regexp"
    29  	"strings"
    30  	"unicode"
    31  
    32  	"reflect"
    33  
    34  	"github.com/codegangsta/cli"
    35  	"github.com/flosch/pongo2"
    36  )
    37  
    38  func toUnderScoreCase(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
    39  	s := in.String()
    40  	if len(s) == 0 {
    41  		return nil, nil
    42  	}
    43  	parts := []string{}
    44  	chars := []rune(s)
    45  	var buffer bytes.Buffer
    46  	for i := 0; i < len(chars)-1; i++ {
    47  		if unicode.IsUpper(chars[i]) && unicode.IsLower(chars[i+1]) && buffer.String() != "" {
    48  			parts = append(parts, buffer.String())
    49  			buffer.Reset()
    50  		}
    51  		buffer.WriteRune(unicode.ToLower(chars[i]))
    52  		if unicode.IsLower(chars[i]) && unicode.IsUpper(chars[i+1]) {
    53  			parts = append(parts, buffer.String())
    54  			buffer.Reset()
    55  		}
    56  	}
    57  	buffer.WriteRune(unicode.ToLower(chars[len(chars)-1]))
    58  	parts = append(parts, buffer.String())
    59  	return pongo2.AsValue(strings.Join(parts, "_")), nil
    60  }
    61  
    62  func reflectType(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
    63  	i := in.Interface()
    64  	v := reflect.ValueOf(i)
    65  	t := v.Type()
    66  	return pongo2.AsValue(t), nil
    67  }
    68  
    69  func exprToString(expr interface{}) string {
    70  	switch a := expr.(type) {
    71  	case *ast.Ident:
    72  		return a.Name
    73  	case *ast.SelectorExpr:
    74  		return exprToString(a.X) + "." + exprToString(a.Sel)
    75  	case *ast.ArrayType:
    76  		return "[]" + exprToString(a.Elt)
    77  	case *ast.InterfaceType:
    78  		return "interface{}"
    79  	case *ast.MapType:
    80  		return "map[" + exprToString(a.Key) + "]" + exprToString(a.Value)
    81  	case *ast.StarExpr:
    82  		return "*" + exprToString(a.X)
    83  	}
    84  	return ""
    85  }
    86  
    87  func astType(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
    88  	i := in.Interface()
    89  	return pongo2.AsValue(exprToString(i)), nil
    90  }
    91  
    92  func init() {
    93  	pongo2.RegisterFilter("to_under_score_case", toUnderScoreCase)
    94  	pongo2.RegisterFilter("type", reflectType)
    95  	pongo2.RegisterFilter("astType", astType)
    96  }
    97  
    98  //using hacks in https://github.com/mattn/anko/blob/master/tool/makebuiltin.go
    99  
   100  func pkgName(f string) string {
   101  	file, err := parser.ParseFile(token.NewFileSet(), f, nil, parser.PackageClauseOnly)
   102  	if err != nil || file == nil {
   103  		return ""
   104  	}
   105  	return file.Name.Name
   106  }
   107  
   108  func isGoFile(dir os.FileInfo) bool {
   109  	return !dir.IsDir() &&
   110  		!strings.HasPrefix(dir.Name(), ".") && // ignore .files
   111  		filepath.Ext(dir.Name()) == ".go"
   112  }
   113  
   114  func isPkgFile(dir os.FileInfo) bool {
   115  	return isGoFile(dir) && !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
   116  }
   117  
   118  func parseDir(p string) (map[string]*ast.Package, error) {
   119  	_, pn := filepath.Split(p)
   120  
   121  	isGoDir := func(d os.FileInfo) bool {
   122  		if isPkgFile(d) {
   123  			name := pkgName(p + "/" + d.Name())
   124  			return name == pn
   125  		}
   126  		return false
   127  	}
   128  
   129  	pkgs, err := parser.ParseDir(token.NewFileSet(), p, isGoDir, parser.ParseComments)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return pkgs, nil
   134  }
   135  
   136  //Gen generate adapter code for specified package
   137  func Gen(pkg, template, export, exportPath string) {
   138  	paths := []string{filepath.Join(os.Getenv("GOROOT"), "src")}
   139  	if os.Getenv("GOPATH") == "" {
   140  		fmt.Println("GOPATH isn't specified")
   141  	}
   142  	for _, p := range strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator)) {
   143  		paths = append(paths, filepath.Join(p, "src"))
   144  	}
   145  	for _, p := range paths {
   146  		pkgPath := filepath.Join(p, pkg)
   147  		pkgs, err := parseDir(pkgPath)
   148  		if err != nil {
   149  			continue
   150  		}
   151  
   152  		for _, pkgObj := range pkgs {
   153  
   154  			for filePath, f := range pkgObj.Files {
   155  				imports := []string{}
   156  				funcs := []*ast.FuncDecl{}
   157  				for _, d := range f.Decls {
   158  					switch decl := d.(type) {
   159  					case *ast.GenDecl:
   160  						for _, s := range decl.Specs {
   161  							switch spec := s.(type) {
   162  							case *ast.ImportSpec:
   163  								path := spec.Path.Value
   164  								path = strings.Replace(path, "\"", "", -1)
   165  								imports = append(imports, path)
   166  							}
   167  						}
   168  					case *ast.FuncDecl:
   169  						if decl.Recv != nil {
   170  							continue
   171  						}
   172  						c := decl.Name.Name[0]
   173  						if c < 'A' || c > 'Z' {
   174  							continue
   175  						}
   176  						funcs = append(funcs, decl)
   177  					}
   178  				}
   179  				if len(funcs) == 0 {
   180  					continue
   181  				}
   182  				tpl, err := pongo2.FromFile(template)
   183  				if err != nil {
   184  					panic(err)
   185  				}
   186  				_, pkgName := filepath.Split(pkg)
   187  				_, fileName := filepath.Split(filePath)
   188  				outputFile := filepath.Join(exportPath, pkgName+"_"+fileName)
   189  				output, err := tpl.Execute(
   190  					pongo2.Context{"full_package": pkg, "package": pkgName,
   191  						"funcs":          funcs,
   192  						"imports":        imports,
   193  						"export_package": export})
   194  
   195  				if err != nil {
   196  					panic(err)
   197  				}
   198  				re := regexp.MustCompile("\n+")
   199  				output = re.ReplaceAllString(output, "\n")
   200  				os.MkdirAll(exportPath, os.ModePerm)
   201  				ioutil.WriteFile(outputFile, []byte(output), os.ModePerm)
   202  				out, err := exec.Command("goimports", "./"+outputFile).CombinedOutput()
   203  				if err != nil {
   204  					fmt.Println(string(out))
   205  					panic(err)
   206  				}
   207  				ioutil.WriteFile(outputFile, out, os.ModePerm)
   208  			}
   209  		}
   210  		return
   211  	}
   212  }
   213  
   214  //Run execute main command
   215  func Run(name, usage, version string) {
   216  	app := cli.NewApp()
   217  	app.Name = name
   218  	app.Usage = usage
   219  	app.Version = version
   220  	app.Commands = []cli.Command{
   221  		getLibGenCommand(),
   222  	}
   223  	app.Run(os.Args)
   224  }
   225  
   226  func getLibGenCommand() cli.Command {
   227  	return cli.Command{
   228  		Name:      "generate_lib",
   229  		ShortName: "genlib",
   230  		Usage:     "Generate donburi lib code from plain go code",
   231  		Description: `
   232  helper command for Generate donburi lib code glue.(you need to apply go fmt for generated code)`,
   233  		Flags: []cli.Flag{
   234  			cli.StringFlag{Name: "package, p", Value: "", Usage: "Package"},
   235  			cli.StringFlag{Name: "template, t", Value: "../templates/lib.tmpl", Usage: "Template File"},
   236  			cli.StringFlag{Name: "export, e", Value: "autogen", Usage: "Package to export"},
   237  			cli.StringFlag{Name: "export_path, ep", Value: "../autogen", Usage: "export path"},
   238  		},
   239  		Action: func(c *cli.Context) {
   240  			pkg := c.String("package")
   241  			template := c.String("template")
   242  			export := c.String("export")
   243  			exportPath := c.String("export_path")
   244  			Gen(pkg, template, export, exportPath)
   245  		},
   246  	}
   247  }
   248  
   249  func main() {
   250  	Run("donburi", "Donburi", "0.1.0")
   251  }