-
-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy pathimport.go
128 lines (117 loc) Β· 3.37 KB
/
import.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
127
128
package codegen
import (
"fmt"
"goa.design/goa/expr"
)
type (
// ImportSpec defines a generated import statement.
ImportSpec struct {
// Name of imported package if needed.
Name string
// Go import path of package.
Path string
}
)
// NewImport creates an import spec.
func NewImport(name, path string) *ImportSpec {
return &ImportSpec{Name: name, Path: path}
}
// SimpleImport creates an import with no explicit path component.
func SimpleImport(path string) *ImportSpec {
return &ImportSpec{Path: path}
}
// Code returns the Go import statement for the ImportSpec.
func (s *ImportSpec) Code() string {
if len(s.Name) > 0 {
return fmt.Sprintf(`%s "%s"`, s.Name, s.Path)
}
return fmt.Sprintf(`"%s"`, s.Path)
}
// getMetaTypeInfo gets type and import info from an attribute's metadata. struct:field:type can have 3 arguments,
// first being the go type name, second being import path,
// and third being the name of a qualified import, in case of name collisions.
func getMetaTypeInfo(att *expr.AttributeExpr) (typeName string, importS *ImportSpec) {
if att == nil {
return typeName, importS
}
if args, ok := att.Meta["struct:field:type"]; ok {
if len(args) > 0 {
typeName = args[0]
}
if len(args) > 1 {
importS = &ImportSpec{Path: args[1]}
}
if len(args) > 2 {
importS.Name = args[2]
}
}
return typeName, importS
}
// GetMetaTypeImports parses the attribute for all user defined imports
func GetMetaTypeImports(att *expr.AttributeExpr) []*ImportSpec {
return safelyGetMetaTypeImports(att, nil)
}
// safelyGetMetaTypeImports parses attributes while keeping track of previous usertypes to avoid infinite recursion
func safelyGetMetaTypeImports(att *expr.AttributeExpr, seen map[string]struct{}) []*ImportSpec {
if att == nil {
return nil
}
if seen == nil {
seen = make(map[string]struct{})
}
uniqueImports := make(map[ImportSpec]struct{})
imports := make([]*ImportSpec, 0)
switch t := att.Type.(type) {
case expr.UserType:
if _, wasSeen := seen[t.ID()]; wasSeen {
return imports
}
seen[t.ID()] = struct{}{}
for _, im := range safelyGetMetaTypeImports(t.Attribute(), seen) {
if im != nil {
uniqueImports[*im] = struct{}{}
}
}
case *expr.Array:
_, im := getMetaTypeInfo(t.ElemType)
if im != nil {
uniqueImports[*im] = struct{}{}
}
case *expr.Map:
_, im := getMetaTypeInfo(t.ElemType)
if im != nil {
uniqueImports[*im] = struct{}{}
}
_, im = getMetaTypeInfo(t.KeyType)
if im != nil {
uniqueImports[*im] = struct{}{}
}
case *expr.Object:
for _, key := range *t {
if key != nil {
_, im := getMetaTypeInfo(key.Attribute)
if im != nil {
uniqueImports[*im] = struct{}{}
}
}
}
}
_, im := getMetaTypeInfo(att)
if im != nil {
uniqueImports[*im] = struct{}{}
}
for imp := range uniqueImports {
// Copy loop variable into body so next iteration doesnt overwrite its address https://stackoverflow.com/questions/27610039/golang-appending-leaves-only-last-element
copy := imp
imports = append(imports, ©)
}
return imports
}
// AddServiceMetaTypeImports adds meta type imports for each method of the service expr
func AddServiceMetaTypeImports(header *SectionTemplate, svc *expr.ServiceExpr) {
for _, m := range svc.Methods {
AddImport(header, GetMetaTypeImports(m.Payload)...)
AddImport(header, GetMetaTypeImports(m.StreamingPayload)...)
AddImport(header, GetMetaTypeImports(m.Result)...)
}
}