/
template.go
107 lines (94 loc) · 2.7 KB
/
template.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package main
import (
"go/ast"
"go/build"
"go/format"
"go/parser"
"go/token"
"log"
"os"
"path"
"strings"
)
func generate(dir, pkg string) {
p, err := build.Default.Import(templateDir, dir, build.ImportMode(0))
if err != nil {
log.Fatalf("Import %s failed: %s", templateDir, err)
}
if len(p.GoFiles) != 1 {
log.Fatalf("Expecting only 1 go file in dir %s", templateDir)
}
templateFilePath := path.Join(p.Dir, p.GoFiles[0])
fset, f := parseFile(templateFilePath)
// Change the package to the local package name
f.Name.Name = pkg
newDecls := []ast.Decl{}
for _, Decl := range f.Decls {
keep := true
switch d := Decl.(type) {
case *ast.GenDecl:
// A general definition
switch d.Tok {
case token.TYPE:
for _, spec := range d.Specs {
typeSpec := spec.(*ast.TypeSpec)
keep = !strings.HasPrefix(typeSpec.Name.Name, "Replace")
}
}
}
if keep {
newDecls = append(newDecls, Decl)
}
}
// remove the declarations that start with "Replace"
f.Decls = newDecls
replaceIdentifier(f, "ReplaceKey", keyType)
replaceIdentifier(f, "ReplaceValue", valueType)
// rename ACache and aWrapper with the type specified
typeName := strings.ToUpper(valueType[:1]) + valueType[1:]
replaceIdentifier(f, "ACache", typeName+"Cache")
replaceIdentifier(f, "NewACache", "New"+typeName+"Cache")
wrapperName := strings.ToLower(valueType[:1]) + valueType[1:]
replaceIdentifier(f, "aCache", wrapperName+"Cache")
replaceIdentifier(f, "aWrapper", wrapperName+"Wrapper")
replaceIdentifier(f, "stopACacheCleanup", "stop"+typeName+"CacheCleanup")
// output the new file
outputFileName := strings.ToLower(valueType) + "_cache.go"
outputFile(fset, f, outputFileName)
log.Printf("Wrote %q", outputFileName)
}
// replaceIdentifier replaces the old string with the new string
// in the given go file
func replaceIdentifier(f *ast.File, old, new string) {
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.Ident:
if x.Name == old {
x.Name = new
}
}
return true
})
}
// outputFile creates, writes and formats a go file
func outputFile(fset *token.FileSet, f *ast.File, path string) {
fd, err := os.Create(path)
if err != nil {
log.Fatalf("Error opening %q: %s", path, err)
}
if err := format.Node(fd, fset, f); err != nil {
log.Fatalf("Error formatting %q: %s", path, err)
}
if err := fd.Close(); err != nil {
log.Fatalf("Error closing %q: %s", path, err)
}
}
// parseFile gets a Fileset and a ast.File for a particular go file
func parseFile(path string) (*token.FileSet, *ast.File) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
log.Fatalf("Error parsing file: %s", err)
}
return fset, f
}