]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | // +build codegen |
2 | ||
3 | package endpoints | |
4 | ||
5 | import ( | |
6 | "fmt" | |
7 | "io" | |
8 | "reflect" | |
9 | "strings" | |
10 | "text/template" | |
11 | "unicode" | |
12 | ) | |
13 | ||
14 | // A CodeGenOptions are the options for code generating the endpoints into | |
15 | // Go code from the endpoints model definition. | |
16 | type CodeGenOptions struct { | |
17 | // Options for how the model will be decoded. | |
18 | DecodeModelOptions DecodeModelOptions | |
107c1cdb ND |
19 | |
20 | // Disables code generation of the service endpoint prefix IDs defined in | |
21 | // the model. | |
22 | DisableGenerateServiceIDs bool | |
bae9f6d2 JC |
23 | } |
24 | ||
25 | // Set combines all of the option functions together | |
26 | func (d *CodeGenOptions) Set(optFns ...func(*CodeGenOptions)) { | |
27 | for _, fn := range optFns { | |
28 | fn(d) | |
29 | } | |
30 | } | |
31 | ||
32 | // CodeGenModel given a endpoints model file will decode it and attempt to | |
33 | // generate Go code from the model definition. Error will be returned if | |
34 | // the code is unable to be generated, or decoded. | |
35 | func CodeGenModel(modelFile io.Reader, outFile io.Writer, optFns ...func(*CodeGenOptions)) error { | |
36 | var opts CodeGenOptions | |
37 | opts.Set(optFns...) | |
38 | ||
39 | resolver, err := DecodeModel(modelFile, func(d *DecodeModelOptions) { | |
40 | *d = opts.DecodeModelOptions | |
41 | }) | |
42 | if err != nil { | |
43 | return err | |
44 | } | |
45 | ||
107c1cdb ND |
46 | v := struct { |
47 | Resolver | |
48 | CodeGenOptions | |
49 | }{ | |
50 | Resolver: resolver, | |
51 | CodeGenOptions: opts, | |
52 | } | |
53 | ||
bae9f6d2 | 54 | tmpl := template.Must(template.New("tmpl").Funcs(funcMap).Parse(v3Tmpl)) |
107c1cdb | 55 | if err := tmpl.ExecuteTemplate(outFile, "defaults", v); err != nil { |
bae9f6d2 JC |
56 | return fmt.Errorf("failed to execute template, %v", err) |
57 | } | |
58 | ||
59 | return nil | |
60 | } | |
61 | ||
62 | func toSymbol(v string) string { | |
63 | out := []rune{} | |
64 | for _, c := range strings.Title(v) { | |
65 | if !(unicode.IsNumber(c) || unicode.IsLetter(c)) { | |
66 | continue | |
67 | } | |
68 | ||
69 | out = append(out, c) | |
70 | } | |
71 | ||
72 | return string(out) | |
73 | } | |
74 | ||
75 | func quoteString(v string) string { | |
76 | return fmt.Sprintf("%q", v) | |
77 | } | |
78 | ||
79 | func regionConstName(p, r string) string { | |
80 | return toSymbol(p) + toSymbol(r) | |
81 | } | |
82 | ||
83 | func partitionGetter(id string) string { | |
84 | return fmt.Sprintf("%sPartition", toSymbol(id)) | |
85 | } | |
86 | ||
87 | func partitionVarName(id string) string { | |
88 | return fmt.Sprintf("%sPartition", strings.ToLower(toSymbol(id))) | |
89 | } | |
90 | ||
91 | func listPartitionNames(ps partitions) string { | |
92 | names := []string{} | |
93 | switch len(ps) { | |
94 | case 1: | |
95 | return ps[0].Name | |
96 | case 2: | |
97 | return fmt.Sprintf("%s and %s", ps[0].Name, ps[1].Name) | |
98 | default: | |
99 | for i, p := range ps { | |
100 | if i == len(ps)-1 { | |
101 | names = append(names, "and "+p.Name) | |
102 | } else { | |
103 | names = append(names, p.Name) | |
104 | } | |
105 | } | |
106 | return strings.Join(names, ", ") | |
107 | } | |
108 | } | |
109 | ||
110 | func boxedBoolIfSet(msg string, v boxedBool) string { | |
111 | switch v { | |
112 | case boxedTrue: | |
113 | return fmt.Sprintf(msg, "boxedTrue") | |
114 | case boxedFalse: | |
115 | return fmt.Sprintf(msg, "boxedFalse") | |
116 | default: | |
117 | return "" | |
118 | } | |
119 | } | |
120 | ||
121 | func stringIfSet(msg, v string) string { | |
122 | if len(v) == 0 { | |
123 | return "" | |
124 | } | |
125 | ||
126 | return fmt.Sprintf(msg, v) | |
127 | } | |
128 | ||
129 | func stringSliceIfSet(msg string, vs []string) string { | |
130 | if len(vs) == 0 { | |
131 | return "" | |
132 | } | |
133 | ||
134 | names := []string{} | |
135 | for _, v := range vs { | |
136 | names = append(names, `"`+v+`"`) | |
137 | } | |
138 | ||
139 | return fmt.Sprintf(msg, strings.Join(names, ",")) | |
140 | } | |
141 | ||
142 | func endpointIsSet(v endpoint) bool { | |
143 | return !reflect.DeepEqual(v, endpoint{}) | |
144 | } | |
145 | ||
146 | func serviceSet(ps partitions) map[string]struct{} { | |
147 | set := map[string]struct{}{} | |
148 | for _, p := range ps { | |
149 | for id := range p.Services { | |
150 | set[id] = struct{}{} | |
151 | } | |
152 | } | |
153 | ||
154 | return set | |
155 | } | |
156 | ||
157 | var funcMap = template.FuncMap{ | |
158 | "ToSymbol": toSymbol, | |
159 | "QuoteString": quoteString, | |
160 | "RegionConst": regionConstName, | |
161 | "PartitionGetter": partitionGetter, | |
162 | "PartitionVarName": partitionVarName, | |
163 | "ListPartitionNames": listPartitionNames, | |
164 | "BoxedBoolIfSet": boxedBoolIfSet, | |
165 | "StringIfSet": stringIfSet, | |
166 | "StringSliceIfSet": stringSliceIfSet, | |
167 | "EndpointIsSet": endpointIsSet, | |
168 | "ServicesSet": serviceSet, | |
169 | } | |
170 | ||
171 | const v3Tmpl = ` | |
172 | {{ define "defaults" -}} | |
173 | // Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT. | |
174 | ||
175 | package endpoints | |
176 | ||
177 | import ( | |
178 | "regexp" | |
179 | ) | |
180 | ||
107c1cdb | 181 | {{ template "partition consts" $.Resolver }} |
bae9f6d2 | 182 | |
107c1cdb | 183 | {{ range $_, $partition := $.Resolver }} |
bae9f6d2 JC |
184 | {{ template "partition region consts" $partition }} |
185 | {{ end }} | |
186 | ||
107c1cdb ND |
187 | {{ if not $.DisableGenerateServiceIDs -}} |
188 | {{ template "service consts" $.Resolver }} | |
189 | {{- end }} | |
bae9f6d2 | 190 | |
107c1cdb | 191 | {{ template "endpoint resolvers" $.Resolver }} |
bae9f6d2 JC |
192 | {{- end }} |
193 | ||
194 | {{ define "partition consts" }} | |
195 | // Partition identifiers | |
196 | const ( | |
197 | {{ range $_, $p := . -}} | |
198 | {{ ToSymbol $p.ID }}PartitionID = {{ QuoteString $p.ID }} // {{ $p.Name }} partition. | |
199 | {{ end -}} | |
200 | ) | |
201 | {{- end }} | |
202 | ||
203 | {{ define "partition region consts" }} | |
204 | // {{ .Name }} partition's regions. | |
205 | const ( | |
206 | {{ range $id, $region := .Regions -}} | |
207 | {{ ToSymbol $id }}RegionID = {{ QuoteString $id }} // {{ $region.Description }}. | |
208 | {{ end -}} | |
209 | ) | |
210 | {{- end }} | |
211 | ||
212 | {{ define "service consts" }} | |
213 | // Service identifiers | |
214 | const ( | |
215 | {{ $serviceSet := ServicesSet . -}} | |
216 | {{ range $id, $_ := $serviceSet -}} | |
217 | {{ ToSymbol $id }}ServiceID = {{ QuoteString $id }} // {{ ToSymbol $id }}. | |
218 | {{ end -}} | |
219 | ) | |
220 | {{- end }} | |
221 | ||
222 | {{ define "endpoint resolvers" }} | |
223 | // DefaultResolver returns an Endpoint resolver that will be able | |
224 | // to resolve endpoints for: {{ ListPartitionNames . }}. | |
225 | // | |
226 | // Use DefaultPartitions() to get the list of the default partitions. | |
227 | func DefaultResolver() Resolver { | |
228 | return defaultPartitions | |
229 | } | |
230 | ||
231 | // DefaultPartitions returns a list of the partitions the SDK is bundled | |
232 | // with. The available partitions are: {{ ListPartitionNames . }}. | |
233 | // | |
234 | // partitions := endpoints.DefaultPartitions | |
235 | // for _, p := range partitions { | |
236 | // // ... inspect partitions | |
237 | // } | |
238 | func DefaultPartitions() []Partition { | |
239 | return defaultPartitions.Partitions() | |
240 | } | |
241 | ||
242 | var defaultPartitions = partitions{ | |
243 | {{ range $_, $partition := . -}} | |
244 | {{ PartitionVarName $partition.ID }}, | |
245 | {{ end }} | |
246 | } | |
247 | ||
248 | {{ range $_, $partition := . -}} | |
249 | {{ $name := PartitionGetter $partition.ID -}} | |
250 | // {{ $name }} returns the Resolver for {{ $partition.Name }}. | |
251 | func {{ $name }}() Partition { | |
252 | return {{ PartitionVarName $partition.ID }}.Partition() | |
253 | } | |
254 | var {{ PartitionVarName $partition.ID }} = {{ template "gocode Partition" $partition }} | |
255 | {{ end }} | |
256 | {{ end }} | |
257 | ||
258 | {{ define "default partitions" }} | |
259 | func DefaultPartitions() []Partition { | |
260 | return []partition{ | |
261 | {{ range $_, $partition := . -}} | |
262 | // {{ ToSymbol $partition.ID}}Partition(), | |
263 | {{ end }} | |
264 | } | |
265 | } | |
266 | {{ end }} | |
267 | ||
268 | {{ define "gocode Partition" -}} | |
269 | partition{ | |
270 | {{ StringIfSet "ID: %q,\n" .ID -}} | |
271 | {{ StringIfSet "Name: %q,\n" .Name -}} | |
272 | {{ StringIfSet "DNSSuffix: %q,\n" .DNSSuffix -}} | |
273 | RegionRegex: {{ template "gocode RegionRegex" .RegionRegex }}, | |
274 | {{ if EndpointIsSet .Defaults -}} | |
275 | Defaults: {{ template "gocode Endpoint" .Defaults }}, | |
276 | {{- end }} | |
277 | Regions: {{ template "gocode Regions" .Regions }}, | |
278 | Services: {{ template "gocode Services" .Services }}, | |
279 | } | |
280 | {{- end }} | |
281 | ||
282 | {{ define "gocode RegionRegex" -}} | |
283 | regionRegex{ | |
284 | Regexp: func() *regexp.Regexp{ | |
285 | reg, _ := regexp.Compile({{ QuoteString .Regexp.String }}) | |
286 | return reg | |
287 | }(), | |
288 | } | |
289 | {{- end }} | |
290 | ||
291 | {{ define "gocode Regions" -}} | |
292 | regions{ | |
293 | {{ range $id, $region := . -}} | |
294 | "{{ $id }}": {{ template "gocode Region" $region }}, | |
295 | {{ end -}} | |
296 | } | |
297 | {{- end }} | |
298 | ||
299 | {{ define "gocode Region" -}} | |
300 | region{ | |
301 | {{ StringIfSet "Description: %q,\n" .Description -}} | |
302 | } | |
303 | {{- end }} | |
304 | ||
305 | {{ define "gocode Services" -}} | |
306 | services{ | |
307 | {{ range $id, $service := . -}} | |
308 | "{{ $id }}": {{ template "gocode Service" $service }}, | |
309 | {{ end }} | |
310 | } | |
311 | {{- end }} | |
312 | ||
313 | {{ define "gocode Service" -}} | |
314 | service{ | |
315 | {{ StringIfSet "PartitionEndpoint: %q,\n" .PartitionEndpoint -}} | |
316 | {{ BoxedBoolIfSet "IsRegionalized: %s,\n" .IsRegionalized -}} | |
317 | {{ if EndpointIsSet .Defaults -}} | |
318 | Defaults: {{ template "gocode Endpoint" .Defaults -}}, | |
319 | {{- end }} | |
320 | {{ if .Endpoints -}} | |
321 | Endpoints: {{ template "gocode Endpoints" .Endpoints }}, | |
322 | {{- end }} | |
323 | } | |
324 | {{- end }} | |
325 | ||
326 | {{ define "gocode Endpoints" -}} | |
327 | endpoints{ | |
328 | {{ range $id, $endpoint := . -}} | |
329 | "{{ $id }}": {{ template "gocode Endpoint" $endpoint }}, | |
330 | {{ end }} | |
331 | } | |
332 | {{- end }} | |
333 | ||
334 | {{ define "gocode Endpoint" -}} | |
335 | endpoint{ | |
336 | {{ StringIfSet "Hostname: %q,\n" .Hostname -}} | |
337 | {{ StringIfSet "SSLCommonName: %q,\n" .SSLCommonName -}} | |
338 | {{ StringSliceIfSet "Protocols: []string{%s},\n" .Protocols -}} | |
339 | {{ StringSliceIfSet "SignatureVersions: []string{%s},\n" .SignatureVersions -}} | |
340 | {{ if or .CredentialScope.Region .CredentialScope.Service -}} | |
341 | CredentialScope: credentialScope{ | |
342 | {{ StringIfSet "Region: %q,\n" .CredentialScope.Region -}} | |
343 | {{ StringIfSet "Service: %q,\n" .CredentialScope.Service -}} | |
344 | }, | |
345 | {{- end }} | |
346 | {{ BoxedBoolIfSet "HasDualStack: %s,\n" .HasDualStack -}} | |
347 | {{ StringIfSet "DualStackHostname: %q,\n" .DualStackHostname -}} | |
348 | ||
349 | } | |
350 | {{- end }} | |
351 | ` |