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