-
Notifications
You must be signed in to change notification settings - Fork 1
/
router.go
executable file
·126 lines (114 loc) · 3.25 KB
/
router.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package gomvc
import (
"bytes"
"fmt"
"go/token"
"log"
"os"
"path/filepath"
"sort"
"strings"
"github.com/dave/dst"
"github.com/dave/dst/decorator"
)
type RouteData struct {
Controllers []ControllerData `json:"controllers,omitempty"`
}
// CreateRouter creates a router.go file to be used in the controllers directory
func CreateRouter(data RouteData, relativeTemplatePath, destDir string) {
sort.Slice(data.Controllers, func(i, j int) bool {
return strings.Compare(
data.Controllers[i].Name,
data.Controllers[j].Name) < 1
})
outputPath := filepath.Join(destDir, "router.go")
if err := createFileFromTemplates(relativeTemplatePath, data, outputPath); err != nil {
log.Fatalf("error generating router file for %s %s\n", outputPath, err.Error())
}
}
func AddActionViaAST(data ControllerData, routerFilePath string, destDir string) {
code := createStringFromFile(routerFilePath)
f, err := decorator.Parse(code)
if err != nil {
panic(err)
}
fn, err := getFuncByName(f, "GetRouter")
if err != nil {
panic(err)
}
// current node is a function!
numStatements := len(fn.Body.List)
// the last statement is a return so we insert before the return which is why it's numStatements - 1
returnStatement := fn.Body.List[numStatements-1]
// delete return statement
fn.Body.List = fn.Body.List[:numStatements-1]
// create the controller instantiation line
controllerStmt := NewControllerStatement(data.Name)
fn.Body.List = append(fn.Body.List, controllerStmt)
// create the specific route registration line for each controller action
for _, action := range data.Actions {
routeStmt := NewRouteRegistrationStatement(action)
fn.Body.List = append(fn.Body.List, routeStmt)
}
// add back the removed return to be at the end
fn.Body.List = append(fn.Body.List, returnStatement)
// i don't know why i can't just directly write to the file
// instead of using the byte buffer intermediary
w := &bytes.Buffer{}
if err := decorator.Fprint(w, f); err != nil {
panic(err)
}
updatedContents := w.String()
newFile, _ := os.Create(routerFilePath)
if _, err := newFile.WriteString(updatedContents); err != nil {
panic(err)
}
}
func NewRouteRegistrationStatement(action Action) *dst.ExprStmt {
return &dst.ExprStmt{
X: &dst.CallExpr{
Fun: &dst.SelectorExpr{
Sel: &dst.Ident{Name: action.Method},
X: &dst.Ident{Name: "r"},
},
Args: []dst.Expr{
&dst.BasicLit{
Kind: token.STRING,
Value: fmt.Sprintf("\"%s\"", action.Path),
},
&dst.SelectorExpr{
Sel: &dst.Ident{Name: action.Name},
X: &dst.Ident{Name: fmt.Sprintf("%sCtrl", action.Resource)},
},
},
},
}
}
func NewControllerStatement(resource string) *dst.AssignStmt {
name := fmt.Sprintf("%sCtrl", resource)
return &dst.AssignStmt{
Tok: token.DEFINE,
Lhs: []dst.Expr{
&dst.Ident{
Name: name,
},
},
Rhs: []dst.Expr{
&dst.Ident{
Name: fmt.Sprintf("%sController{db: db, log: log}", Title(resource)),
},
},
}
}
// modified from https://github.com/laher/regopher/blob/master/parsing.go
func getFuncByName(f *dst.File, funcName string) (*dst.FuncDecl, error) {
for _, d := range f.Decls {
switch fn := d.(type) {
case *dst.FuncDecl:
if fn.Name.Name == funcName {
return fn, nil
}
}
}
return nil, fmt.Errorf("func not found")
}