/
function.go
100 lines (83 loc) · 2.91 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
package parser
import (
"fmt"
"go/ast"
"go/types"
"github.com/switchupcb/copygen/cli/models"
"github.com/switchupcb/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 := 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,
},
}
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) ([]*options.Option, bool) {
nodeOptions := make([]*options.Option, 0, len(commentoptionmap))
var manual bool
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
}
}
}
return true
})
return nodeOptions, manual
}
// 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)
}
}
}