blob: ea24bde24428e4a3625607a571f7af859afd93f0 [file] [log] [blame]
Serge Bazanski31dd6162018-10-25 14:20:50 +02001package builder
2
3import (
4 "go/ast"
5 "go/parser"
6 "go/token"
7 "io/ioutil"
8 "sort"
9 "strings"
10
11 "github.com/pkg/errors"
12)
13
14type visitor struct {
15 Path string
16 Package string
17 Boxes []string
18 Errors []error
19}
20
21func newVisitor(path string) *visitor {
22 return &visitor{
23 Path: path,
24 Boxes: []string{},
25 Errors: []error{},
26 }
27}
28
29func (v *visitor) Run() error {
30 b, err := ioutil.ReadFile(v.Path)
31 if err != nil {
32 return errors.WithStack(err)
33 }
34
35 fset := token.NewFileSet()
36 file, err := parser.ParseFile(fset, v.Path, string(b), parser.ParseComments)
37 if err != nil {
38 return errors.WithStack(err)
39 }
40
41 v.Package = file.Name.Name
42 ast.Walk(v, file)
43
44 m := map[string]string{}
45 for _, s := range v.Boxes {
46 m[s] = s
47 }
48 v.Boxes = []string{}
49 for k := range m {
50 v.Boxes = append(v.Boxes, k)
51 }
52
53 sort.Strings(v.Boxes)
54
55 if len(v.Errors) > 0 {
56 s := make([]string, len(v.Errors))
57 for i, e := range v.Errors {
58 s[i] = e.Error()
59 }
60 return errors.New(strings.Join(s, "\n"))
61 }
62 return nil
63}
64
65func (v *visitor) Visit(node ast.Node) ast.Visitor {
66 if node == nil {
67 return v
68 }
69 if err := v.eval(node); err != nil {
70 v.Errors = append(v.Errors, err)
71 }
72 return v
73}
74
75func (v *visitor) eval(node ast.Node) error {
76 switch t := node.(type) {
77 case *ast.CallExpr:
78 return v.evalExpr(t)
79 case *ast.Ident:
80 return v.evalIdent(t)
81 case *ast.GenDecl:
82 for _, n := range t.Specs {
83 if err := v.eval(n); err != nil {
84 return errors.WithStack(err)
85 }
86 }
87 case *ast.FuncDecl:
88 if t.Body == nil {
89 return nil
90 }
91 for _, b := range t.Body.List {
92 if err := v.evalStmt(b); err != nil {
93 return errors.WithStack(err)
94 }
95 }
96 return nil
97 case *ast.ValueSpec:
98 for _, e := range t.Values {
99 if err := v.evalExpr(e); err != nil {
100 return errors.WithStack(err)
101 }
102 }
103 }
104 return nil
105}
106
107func (v *visitor) evalStmt(stmt ast.Stmt) error {
108 switch t := stmt.(type) {
109 case *ast.ExprStmt:
110 return v.evalExpr(t.X)
111 case *ast.AssignStmt:
112 for _, e := range t.Rhs {
113 if err := v.evalArgs(e); err != nil {
114 return errors.WithStack(err)
115 }
116 }
117 }
118 return nil
119}
120
121func (v *visitor) evalExpr(expr ast.Expr) error {
122 switch t := expr.(type) {
123 case *ast.CallExpr:
124 if t.Fun == nil {
125 return nil
126 }
127 for _, a := range t.Args {
128 switch at := a.(type) {
129 case *ast.CallExpr:
130 if sel, ok := t.Fun.(*ast.SelectorExpr); ok {
131 return v.evalSelector(at, sel)
132 }
133
134 if err := v.evalArgs(at); err != nil {
135 return errors.WithStack(err)
136 }
137 case *ast.CompositeLit:
138 for _, e := range at.Elts {
139 if err := v.evalExpr(e); err != nil {
140 return errors.WithStack(err)
141 }
142 }
143 }
144 }
145 if ft, ok := t.Fun.(*ast.SelectorExpr); ok {
146 return v.evalSelector(t, ft)
147 }
148 case *ast.KeyValueExpr:
149 return v.evalExpr(t.Value)
150 }
151 return nil
152}
153
154func (v *visitor) evalArgs(expr ast.Expr) error {
155 switch at := expr.(type) {
156 case *ast.CompositeLit:
157 for _, e := range at.Elts {
158 if err := v.evalExpr(e); err != nil {
159 return errors.WithStack(err)
160 }
161 }
162 // case *ast.BasicLit:
163 // fmt.Println("evalArgs", at.Value)
164 // v.addBox(at.Value)
165 case *ast.CallExpr:
166 if at.Fun == nil {
167 return nil
168 }
169 switch st := at.Fun.(type) {
170 case *ast.SelectorExpr:
171 if err := v.evalSelector(at, st); err != nil {
172 return errors.WithStack(err)
173 }
174 case *ast.Ident:
175 return v.evalIdent(st)
176 }
177 for _, a := range at.Args {
178 if err := v.evalArgs(a); err != nil {
179 return errors.WithStack(err)
180 }
181 }
182 }
183 return nil
184}
185
186func (v *visitor) evalSelector(expr *ast.CallExpr, sel *ast.SelectorExpr) error {
187 x, ok := sel.X.(*ast.Ident)
188 if !ok {
189 return nil
190 }
191 if x.Name == "packr" && sel.Sel.Name == "NewBox" {
192 for _, e := range expr.Args {
193 switch at := e.(type) {
194 case *ast.Ident:
195 switch at.Obj.Kind {
196 case ast.Var:
197 if as, ok := at.Obj.Decl.(*ast.AssignStmt); ok {
198 v.addVariable(as)
199 }
200 case ast.Con:
201 if vs, ok := at.Obj.Decl.(*ast.ValueSpec); ok {
202 v.addConstant(vs)
203 }
204 }
205 return v.evalIdent(at)
206 case *ast.BasicLit:
207 v.addBox(at.Value)
208 case *ast.CallExpr:
209 return v.evalExpr(at)
210 }
211 }
212 }
213
214 return nil
215}
216
217func (v *visitor) evalIdent(i *ast.Ident) error {
218 if i.Obj == nil {
219 return nil
220 }
221 if s, ok := i.Obj.Decl.(*ast.AssignStmt); ok {
222 return v.evalStmt(s)
223 }
224 return nil
225}
226
227func (v *visitor) addBox(b string) {
228 b = strings.Replace(b, "\"", "", -1)
229 v.Boxes = append(v.Boxes, b)
230}
231
232func (v *visitor) addVariable(as *ast.AssignStmt) error {
233 if len(as.Rhs) == 1 {
234 if bs, ok := as.Rhs[0].(*ast.BasicLit); ok {
235 v.addBox(bs.Value)
236 }
237 }
238 return nil
239}
240
241func (v *visitor) addConstant(vs *ast.ValueSpec) error {
242 if len(vs.Values) == 1 {
243 if bs, ok := vs.Values[0].(*ast.BasicLit); ok {
244 v.addBox(bs.Value)
245 }
246 }
247 return nil
248}