forked from switchupcb/copygen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
function.go
109 lines (92 loc) · 3.47 KB
/
function.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
package parser
import (
"fmt"
"go/ast"
"go/types"
"github.com/reedom/copygen/cli/models"
"github.com/reedom/copygen/cli/parser/options"
)
// parseFunctions parses the AST for functions in the setup file.
// astcopygen is used to assign options from *ast.Comments.
func (p *Parser) parseFunctions(copygen *ast.InterfaceType) ([]models.Function, error) {
numMethods := len(copygen.Methods.List)
if numMethods == 0 {
fmt.Println("WARNING: no functions are defined in the \"type Copygen interface\"")
}
// create models.Function objects.
functions := make([]models.Function, numMethods)
for i := 0; i < numMethods; i++ {
method := p.Config.SetupPkg.TypesInfo.Defs[copygen.Methods.List[i].Names[0]]
// create models.Type objects.
fieldoptions, manual, noCase, preProcess, postProcess := getNodeOptions(copygen.Methods.List[i], p.Options.CommentOptionMap)
fieldoptions = append(fieldoptions, p.Options.ConvertOptions...)
parsed, err := parseTypes(method.(*types.Func))
if err != nil {
return nil, fmt.Errorf("an error occurred while parsing the types of function %q.\n%w", method.Name(), err)
}
// set the options for each field.
setTypeOptions(parsed.fromTypes, fieldoptions)
setTypeOptions(parsed.toTypes, fieldoptions)
// map the function custom options.
customoptionmap := make(map[string][]string)
for _, option := range fieldoptions {
customoptionmap, err = options.MapCustomOption(customoptionmap, option)
if err != nil {
fmt.Printf("WARNING: %v\n", err)
}
}
// create the models.Function object.
function := models.Function{
Name: method.Name(),
To: parsed.toTypes,
From: parsed.fromTypes,
Options: models.FunctionOptions{
Custom: customoptionmap,
Manual: manual,
Error: parsed.retError,
NoCase: noCase,
PreProcess: preProcess,
PostProcess: postProcess,
},
}
functions[i] = function
}
return functions, nil
}
// getNodeOptions gets an ast.Node options from its comments.
// To reduce overhead, it also returns whether a manual matcher is used.
func getNodeOptions(x ast.Node, commentoptionmap map[string]*options.Option) (nodeOptions []*options.Option, manual, noCase bool, preProcess, postProcess string) {
nodeOptions = make([]*options.Option, 0, len(commentoptionmap))
ast.Inspect(x, func(node ast.Node) bool {
commentGroup, ok := node.(*ast.CommentGroup)
if !ok {
return true
}
for _, comment := range commentGroup.List {
if commentoptionmap[comment.Text] != nil {
nodeOptions = append(nodeOptions, commentoptionmap[comment.Text])
// specifying a match option disables automatching by default.
if options.IsMatchOptionCategory(commentoptionmap[comment.Text].Category) {
manual = true
} else if commentoptionmap[comment.Text].Category == options.CategoryNoCase {
noCase = true
} else if commentoptionmap[comment.Text].Category == options.CategoryPreProcess {
preProcess = commentoptionmap[comment.Text].Value.(string)
} else if commentoptionmap[comment.Text].Category == options.CategoryPostProcess {
postProcess = commentoptionmap[comment.Text].Value.(string)
}
}
}
return true
})
return
}
// setTypeOptions sets the options for all fields in the given types.
func setTypeOptions(types []models.Type, fieldoptions []*options.Option) {
for _, t := range types {
for _, field := range t.Field.AllFields(nil, nil) {
options.SetFieldOptions(field, fieldoptions)
options.FilterDepth(field, field.Options.Depth, 0)
}
}
}