-
Notifications
You must be signed in to change notification settings - Fork 8
/
proto_types_renderer.go
201 lines (171 loc) · 5.7 KB
/
proto_types_renderer.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package render
import (
"path/filepath"
"sort"
"strings"
"github.com/solo-io/skv2/codegen/collector"
)
// renders kubernetes from templates
type ProtoCodeRenderer struct {
templateRenderer
// the go module of the project
GoModule string
// the relative path to the api dir
// types will render in the package <module>/<apiRoot>/<group>/<version>
ApiRoot string
}
func RenderProtoTypes(grp Group) ([]OutFile, error) {
defaultKubeCodeRenderer := ProtoCodeRenderer{
templateRenderer: DefaultTemplateRenderer,
GoModule: grp.Module,
ApiRoot: grp.ApiRoot,
}
return defaultKubeCodeRenderer.RenderProtoHelpers(grp)
}
func (r ProtoCodeRenderer) RenderProtoHelpers(grp Group) ([]OutFile, error) {
// only render proto helpers for proto groups in the current module
if !grp.HasProtos() {
return nil, nil
}
var files []OutFile
deepCopyFiles, err := r.deepCopyGenTemplate(grp)
if err != nil {
return nil, err
}
files = append(files, deepCopyFiles...)
jsonFiles, err := r.jsonGenTemplate(grp)
if err != nil {
return nil, err
}
files = append(files, jsonFiles...)
return files, nil
}
const (
protoDeepCopyTemplate = "code/types/proto_deepcopy.gotmpl"
protoDeepCopyGo = "proto_deepcopy.go"
protoJsonTemplate = "code/types/json.gen.gotmpl"
protoJsonGo = "json.gen.go"
)
// helper type for rendering proto_deepcopy.go files
type descriptorsWithGopath struct {
// list of descriptors pulled from the group
Descriptors []*collector.DescriptorWithPath
// list of resources pulled from the group
Resources []Resource
// package name used to render the package name in the go template
PackageName string
// group name
GroupName string
// go package of the group api root
rootGoPackage string
// full go package which the template render funcs will use to match against the
// set of descriptors to find the relevant messages
goPackageToMatch string
}
/*
Get the relevant descriptors for a group of descriptors with a go package to match against.
A unique object is initialized for each external go package to the group package
*/
func (grp descriptorsWithGopath) getUniqueDescriptorsWithPath() []*collector.DescriptorWithPath {
result := make(map[string]*collector.DescriptorWithPath)
for _, desc := range grp.Descriptors {
if desc.GetOptions().GetGoPackage() == grp.goPackageToMatch {
result[desc.ProtoFilePath] = desc
}
}
var array []*collector.DescriptorWithPath
for _, v := range result {
array = append(array, v)
}
sort.Slice(array, func(i, j int) bool {
return array[i].ProtoFilePath < array[j].ProtoFilePath
})
return array
}
/*
Create and render the templates for the proto_deepcopy files in order to support
proto_deepcopy funcs for packages which are different than the main group package
The empty string package name is treated as local, and so it it computed the same way as before
Any other package name is than rendered to the relative path supplied.
*/
func (r ProtoCodeRenderer) deepCopyGenTemplate(grp Group) ([]OutFile, error) {
var result []OutFile
for _, pkgForGroup := range uniqueGoImportPathsForGroup(grp) {
// render the proto helper code in the directory containing the type's package
outPath := "." + strings.TrimPrefix(pkgForGroup, r.GoModule)
inputTmpls := inputTemplates{
protoDeepCopyTemplate: OutFile{
Path: filepath.Join(outPath, protoDeepCopyGo),
},
}
packageName := filepath.Base(pkgForGroup)
files, err := r.renderCoreTemplates(inputTmpls, descriptorsWithGopath{
Descriptors: grp.Descriptors,
Resources: grp.Resources,
PackageName: packageName,
rootGoPackage: filepath.Join(grp.Module, grp.ApiRoot, grp.GroupVersion.String()),
goPackageToMatch: pkgForGroup,
})
if err != nil {
return nil, err
}
result = append(result, files...)
}
return result, nil
}
/*
Create and render the templates for protobuf to json marshalling/unmarshalling.
The empty string package name is treated as local, and so it it computed the same way as before
Any other package name is than rendered to the relative path supplied.
*/
func (r ProtoCodeRenderer) jsonGenTemplate(grp Group) ([]OutFile, error) {
var result []OutFile
for _, pkgForGroup := range uniqueGoImportPathsForGroup(grp) {
// render the proto helper code in the directory containing the type's package
outPath := "." + strings.TrimPrefix(pkgForGroup, r.GoModule)
inputTmpls := inputTemplates{
protoJsonTemplate: OutFile{
Path: filepath.Join(outPath, protoJsonGo),
},
}
packageName := filepath.Base(pkgForGroup)
files, err := r.renderCoreTemplates(inputTmpls, descriptorsWithGopath{
Descriptors: grp.Descriptors,
Resources: grp.Resources,
PackageName: packageName,
GroupName: grp.Group,
rootGoPackage: filepath.Join(grp.Module, grp.ApiRoot, grp.GroupVersion.String()),
goPackageToMatch: pkgForGroup,
})
if err != nil {
return nil, err
}
result = append(result, files...)
}
return result, nil
}
/*
Get all of the unique paths for a group by checking the packages of the resources
This list can include an empty string ("") which corresponds to the local group
*/
func uniqueGoImportPathsForGroup(grp Group) []string {
resultMap := make(map[string]struct{})
for _, res := range grp.Resources {
// if the group has had its GoPackage set, either the types are being imported from another package, or they've been automatically set by the proto renderer
if res.Spec.Type.GoPackage == "" {
continue
}
resultMap[res.Spec.Type.GoPackage] = struct{}{}
if res.Status != nil {
resultMap[res.Status.Type.GoPackage] = struct{}{}
}
}
var result []string
for k, _ := range resultMap {
if k != "" {
result = append(result, k)
}
}
sort.Strings(result)
return result
}