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 }