1// +build codegen
2
3package api
4
5import (
6	"bytes"
7	"fmt"
8	"path"
9	"regexp"
10	"sort"
11	"strings"
12	"text/template"
13
14	"github.com/aws/aws-sdk-go/private/protocol"
15)
16
17// ErrorInfo represents the error block of a shape's structure
18type ErrorInfo struct {
19	Type           string
20	Code           string
21	HTTPStatusCode int
22}
23
24// A XMLInfo defines URL and prefix for Shapes when rendered as XML
25type XMLInfo struct {
26	Prefix string
27	URI    string
28}
29
30// A ShapeRef defines the usage of a shape within the API.
31type ShapeRef struct {
32	API           *API   `json:"-"`
33	Shape         *Shape `json:"-"`
34	Documentation string
35	ShapeName     string `json:"shape"`
36	Location      string
37	LocationName  string
38	QueryName     string
39	Flattened     bool
40	Streaming     bool
41	XMLAttribute  bool
42	// Ignore, if set, will not be sent over the wire
43	Ignore              bool
44	XMLNamespace        XMLInfo
45	Payload             string
46	IdempotencyToken    bool   `json:"idempotencyToken"`
47	TimestampFormat     string `json:"timestampFormat"`
48	JSONValue           bool   `json:"jsonvalue"`
49	Deprecated          bool   `json:"deprecated"`
50	DeprecatedMsg       string `json:"deprecatedMessage"`
51	EndpointDiscoveryID bool   `json:"endpointdiscoveryid"`
52	HostLabel           bool   `json:"hostLabel"`
53
54	OrigShapeName string `json:"-"`
55
56	GenerateGetter bool
57
58	IsEventPayload bool `json:"eventpayload"`
59	IsEventHeader  bool `json:"eventheader"`
60
61	// Collection of custom tags the shape reference includes.
62	CustomTags ShapeTags
63}
64
65// A Shape defines the definition of a shape type
66type Shape struct {
67	API              *API `json:"-"`
68	ShapeName        string
69	Documentation    string
70	MemberRefs       map[string]*ShapeRef `json:"members"`
71	MemberRef        ShapeRef             `json:"member"` // List ref
72	KeyRef           ShapeRef             `json:"key"`    // map key ref
73	ValueRef         ShapeRef             `json:"value"`  // map value ref
74	Required         []string
75	Payload          string
76	Type             string
77	Exception        bool
78	Enum             []string
79	EnumConsts       []string
80	Flattened        bool
81	Streaming        bool
82	Location         string
83	LocationName     string
84	IdempotencyToken bool   `json:"idempotencyToken"`
85	TimestampFormat  string `json:"timestampFormat"`
86	XMLNamespace     XMLInfo
87	Min              float64 // optional Minimum length (string, list) or value (number)
88
89	EventStreamsMemberName string          `json:"-"`
90	EventStreamAPI         *EventStreamAPI `json:"-"`
91	EventFor               []*EventStream  `json:"-"`
92
93	IsEventStream bool `json:"eventstream"`
94	IsEvent       bool `json:"event"`
95
96	refs       []*ShapeRef // References to this shape
97	resolvePkg string      // use this package in the goType() if present
98
99	OrigShapeName string `json:"-"`
100
101	// Defines if the shape is a placeholder and should not be used directly
102	Placeholder bool
103
104	Deprecated    bool   `json:"deprecated"`
105	DeprecatedMsg string `json:"deprecatedMessage"`
106
107	Validations ShapeValidations
108
109	// Error information that is set if the shape is an error shape.
110	ErrorInfo ErrorInfo `json:"error"`
111
112	// Flags that the shape cannot be rename. Prevents the shape from being
113	// renamed further by the Input/Output.
114	AliasedShapeName bool
115
116	// Sensitive types should not be logged by SDK type loggers.
117	Sensitive bool `json:"sensitive"`
118}
119
120// CanBeEmpty returns if the shape value can sent request as an empty value.
121// String, blob, list, and map are types must not be empty when the member is
122// serialized to the uri path, or decorated with HostLabel.
123func (ref *ShapeRef) CanBeEmpty() bool {
124	switch ref.Shape.Type {
125	case "string":
126		return !(ref.Location == "uri" || ref.HostLabel)
127	case "blob", "map", "list":
128		return !(ref.Location == "uri")
129	default:
130		return true
131	}
132}
133
134// ErrorCodeName will return the error shape's name formated for
135// error code const.
136func (s *Shape) ErrorCodeName() string {
137	return "ErrCode" + s.ShapeName
138}
139
140// ErrorName will return the shape's name or error code if available based
141// on the API's protocol. This is the error code string returned by the service.
142func (s *Shape) ErrorName() string {
143	name := s.ErrorInfo.Type
144	switch s.API.Metadata.Protocol {
145	case "query", "ec2query", "rest-xml":
146		if len(s.ErrorInfo.Code) > 0 {
147			name = s.ErrorInfo.Code
148		}
149	}
150
151	if len(name) == 0 {
152		name = s.OrigShapeName
153	}
154	if len(name) == 0 {
155		name = s.ShapeName
156	}
157
158	return name
159}
160
161// PayloadRefName returns the payload member of the shape if there is one
162// modeled. If no payload is modeled, empty string will be returned.
163func (s *Shape) PayloadRefName() string {
164	if name := s.Payload; len(name) != 0 {
165		// Root shape
166		return name
167	}
168
169	for name, ref := range s.MemberRefs {
170		if ref.IsEventPayload {
171			return name
172		}
173	}
174
175	return ""
176}
177
178// GoTags returns the struct tags for a shape.
179func (s *Shape) GoTags(root, required bool) string {
180	ref := &ShapeRef{ShapeName: s.ShapeName, API: s.API, Shape: s}
181	return ref.GoTags(root, required)
182}
183
184// Rename changes the name of the Shape to newName. Also updates
185// the associated API's reference to use newName.
186func (s *Shape) Rename(newName string) {
187	if s.AliasedShapeName {
188		panic(fmt.Sprintf("attempted to rename %s, but flagged as aliased",
189			s.ShapeName))
190	}
191
192	for _, r := range s.refs {
193		r.OrigShapeName = r.ShapeName
194		r.ShapeName = newName
195	}
196
197	delete(s.API.Shapes, s.ShapeName)
198	s.OrigShapeName = s.ShapeName
199	s.API.Shapes[newName] = s
200	s.ShapeName = newName
201}
202
203// MemberNames returns a slice of struct member names.
204func (s *Shape) MemberNames() []string {
205	i, names := 0, make([]string, len(s.MemberRefs))
206	for n := range s.MemberRefs {
207		names[i] = n
208		i++
209	}
210	sort.Strings(names)
211	return names
212}
213
214// HasMember will return whether or not the shape has a given
215// member by name.
216func (s *Shape) HasMember(name string) bool {
217	_, ok := s.MemberRefs[name]
218	return ok
219}
220
221// GoTypeWithPkgName returns a shape's type as a string with the package name in
222// <packageName>.<type> format. Package naming only applies to structures.
223func (s *Shape) GoTypeWithPkgName() string {
224	return goType(s, true)
225}
226
227// GoTypeWithPkgNameElem returns the shapes type as a string with the "*"
228// removed if there was one preset.
229func (s *Shape) GoTypeWithPkgNameElem() string {
230	t := goType(s, true)
231	if strings.HasPrefix(t, "*") {
232		return t[1:]
233	}
234	return t
235}
236
237// UseIndirection returns if the shape's reference should use indirection or not.
238func (s *ShapeRef) UseIndirection() bool {
239	switch s.Shape.Type {
240	case "map", "list", "blob", "structure", "jsonvalue":
241		return false
242	}
243
244	if s.Streaming || s.Shape.Streaming {
245		return false
246	}
247
248	if s.JSONValue {
249		return false
250	}
251
252	return true
253}
254
255func (s Shape) GetTimestampFormat() string {
256	format := s.TimestampFormat
257
258	if len(format) > 0 && !protocol.IsKnownTimestampFormat(format) {
259		panic(fmt.Sprintf("Unknown timestampFormat %s, for %s",
260			format, s.ShapeName))
261	}
262
263	return format
264}
265
266func (ref ShapeRef) GetTimestampFormat() string {
267	format := ref.TimestampFormat
268
269	if len(format) == 0 {
270		format = ref.Shape.TimestampFormat
271	}
272
273	if len(format) > 0 && !protocol.IsKnownTimestampFormat(format) {
274		panic(fmt.Sprintf("Unknown timestampFormat %s, for %s",
275			format, ref.ShapeName))
276	}
277
278	return format
279}
280
281// GoStructValueType returns the Shape's Go type value instead of a pointer
282// for the type.
283func (s *Shape) GoStructValueType(name string, ref *ShapeRef) string {
284	v := s.GoStructType(name, ref)
285
286	if ref.UseIndirection() && v[0] == '*' {
287		return v[1:]
288	}
289
290	return v
291}
292
293// GoStructType returns the type of a struct field based on the API
294// model definition.
295func (s *Shape) GoStructType(name string, ref *ShapeRef) string {
296	if (ref.Streaming || ref.Shape.Streaming) && s.Payload == name {
297		rtype := "io.ReadSeeker"
298		if strings.HasSuffix(s.ShapeName, "Output") {
299			rtype = "io.ReadCloser"
300		}
301
302		s.API.imports["io"] = true
303		return rtype
304	}
305
306	if ref.JSONValue {
307		s.API.AddSDKImport("aws")
308		return "aws.JSONValue"
309	}
310
311	for _, v := range s.Validations {
312		// TODO move this to shape validation resolution
313		if (v.Ref.Shape.Type == "map" || v.Ref.Shape.Type == "list") && v.Type == ShapeValidationNested {
314			s.API.imports["fmt"] = true
315		}
316	}
317
318	return ref.GoType()
319}
320
321// GoType returns a shape's Go type
322func (s *Shape) GoType() string {
323	return goType(s, false)
324}
325
326// GoType returns a shape ref's Go type.
327func (ref *ShapeRef) GoType() string {
328	if ref.Shape == nil {
329		panic(fmt.Errorf("missing shape definition on reference for %#v", ref))
330	}
331
332	return ref.Shape.GoType()
333}
334
335// GoTypeWithPkgName returns a shape's type as a string with the package name in
336// <packageName>.<type> format. Package naming only applies to structures.
337func (ref *ShapeRef) GoTypeWithPkgName() string {
338	if ref.Shape == nil {
339		panic(fmt.Errorf("missing shape definition on reference for %#v", ref))
340	}
341
342	return ref.Shape.GoTypeWithPkgName()
343}
344
345// Returns a string version of the Shape's type.
346// If withPkgName is true, the package name will be added as a prefix
347func goType(s *Shape, withPkgName bool) string {
348	switch s.Type {
349	case "structure":
350		if withPkgName || s.resolvePkg != "" {
351			pkg := s.resolvePkg
352			if pkg != "" {
353				s.API.imports[pkg] = true
354				pkg = path.Base(pkg)
355			} else {
356				pkg = s.API.PackageName()
357			}
358			return fmt.Sprintf("*%s.%s", pkg, s.ShapeName)
359		}
360		return "*" + s.ShapeName
361	case "map":
362		return "map[string]" + goType(s.ValueRef.Shape, withPkgName)
363	case "jsonvalue":
364		return "aws.JSONValue"
365	case "list":
366		return "[]" + goType(s.MemberRef.Shape, withPkgName)
367	case "boolean":
368		return "*bool"
369	case "string", "character":
370		return "*string"
371	case "blob":
372		return "[]byte"
373	case "byte", "short", "integer", "long":
374		return "*int64"
375	case "float", "double":
376		return "*float64"
377	case "timestamp":
378		s.API.imports["time"] = true
379		return "*time.Time"
380	default:
381		panic("Unsupported shape type: " + s.Type)
382	}
383}
384
385// GoTypeElem returns the Go type for the Shape. If the shape type is a pointer just
386// the type will be returned minus the pointer *.
387func (s *Shape) GoTypeElem() string {
388	t := s.GoType()
389	if strings.HasPrefix(t, "*") {
390		return t[1:]
391	}
392	return t
393}
394
395// GoTypeElem returns the Go type for the Shape. If the shape type is a pointer just
396// the type will be returned minus the pointer *.
397func (ref *ShapeRef) GoTypeElem() string {
398	if ref.Shape == nil {
399		panic(fmt.Errorf("missing shape definition on reference for %#v", ref))
400	}
401
402	return ref.Shape.GoTypeElem()
403}
404
405// ShapeTag is a struct tag that will be applied to a shape's generated code
406type ShapeTag struct {
407	Key, Val string
408}
409
410// String returns the string representation of the shape tag
411func (s ShapeTag) String() string {
412	return fmt.Sprintf(`%s:"%s"`, s.Key, s.Val)
413}
414
415// ShapeTags is a collection of shape tags and provides serialization of the
416// tags in an ordered list.
417type ShapeTags []ShapeTag
418
419// Join returns an ordered serialization of the shape tags with the provided
420// separator.
421func (s ShapeTags) Join(sep string) string {
422	o := &bytes.Buffer{}
423	for i, t := range s {
424		o.WriteString(t.String())
425		if i < len(s)-1 {
426			o.WriteString(sep)
427		}
428	}
429
430	return o.String()
431}
432
433// String is an alias for Join with the empty space separator.
434func (s ShapeTags) String() string {
435	return s.Join(" ")
436}
437
438// GoTags returns the rendered tags string for the ShapeRef
439func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string {
440	tags := append(ShapeTags{}, ref.CustomTags...)
441
442	if ref.Location != "" {
443		tags = append(tags, ShapeTag{"location", ref.Location})
444	} else if ref.Shape.Location != "" {
445		tags = append(tags, ShapeTag{"location", ref.Shape.Location})
446	} else if ref.IsEventHeader {
447		tags = append(tags, ShapeTag{"location", "header"})
448	}
449
450	if ref.LocationName != "" {
451		tags = append(tags, ShapeTag{"locationName", ref.LocationName})
452	} else if ref.Shape.LocationName != "" {
453		tags = append(tags, ShapeTag{"locationName", ref.Shape.LocationName})
454	} else if len(ref.Shape.EventFor) != 0 && ref.API.Metadata.Protocol == "rest-xml" {
455		// RPC JSON events need to have location name modeled for round trip testing.
456		tags = append(tags, ShapeTag{"locationName", ref.Shape.ShapeName})
457	}
458
459	if ref.QueryName != "" {
460		tags = append(tags, ShapeTag{"queryName", ref.QueryName})
461	}
462	if ref.Shape.MemberRef.LocationName != "" {
463		tags = append(tags, ShapeTag{"locationNameList", ref.Shape.MemberRef.LocationName})
464	}
465	if ref.Shape.KeyRef.LocationName != "" {
466		tags = append(tags, ShapeTag{"locationNameKey", ref.Shape.KeyRef.LocationName})
467	}
468	if ref.Shape.ValueRef.LocationName != "" {
469		tags = append(tags, ShapeTag{"locationNameValue", ref.Shape.ValueRef.LocationName})
470	}
471	if ref.Shape.Min > 0 {
472		tags = append(tags, ShapeTag{"min", fmt.Sprintf("%v", ref.Shape.Min)})
473	}
474
475	if ref.Deprecated || ref.Shape.Deprecated {
476		tags = append(tags, ShapeTag{"deprecated", "true"})
477	}
478
479	// All shapes have a type
480	tags = append(tags, ShapeTag{"type", ref.Shape.Type})
481
482	// embed the timestamp type for easier lookups
483	if ref.Shape.Type == "timestamp" {
484		if format := ref.GetTimestampFormat(); len(format) > 0 {
485			tags = append(tags, ShapeTag{
486				Key: "timestampFormat",
487				Val: format,
488			})
489		}
490	}
491
492	if ref.Shape.Flattened || ref.Flattened {
493		tags = append(tags, ShapeTag{"flattened", "true"})
494	}
495	if ref.XMLAttribute {
496		tags = append(tags, ShapeTag{"xmlAttribute", "true"})
497	}
498	if isRequired {
499		tags = append(tags, ShapeTag{"required", "true"})
500	}
501	if ref.Shape.IsEnum() {
502		tags = append(tags, ShapeTag{"enum", ref.ShapeName})
503	}
504
505	if toplevel {
506		if name := ref.Shape.PayloadRefName(); len(name) > 0 {
507			tags = append(tags, ShapeTag{"payload", name})
508		}
509	}
510
511	if ref.XMLNamespace.Prefix != "" {
512		tags = append(tags, ShapeTag{"xmlPrefix", ref.XMLNamespace.Prefix})
513	} else if ref.Shape.XMLNamespace.Prefix != "" {
514		tags = append(tags, ShapeTag{"xmlPrefix", ref.Shape.XMLNamespace.Prefix})
515	}
516
517	if ref.XMLNamespace.URI != "" {
518		tags = append(tags, ShapeTag{"xmlURI", ref.XMLNamespace.URI})
519	} else if ref.Shape.XMLNamespace.URI != "" {
520		tags = append(tags, ShapeTag{"xmlURI", ref.Shape.XMLNamespace.URI})
521	}
522
523	if ref.IdempotencyToken || ref.Shape.IdempotencyToken {
524		tags = append(tags, ShapeTag{"idempotencyToken", "true"})
525	}
526
527	if ref.Ignore {
528		tags = append(tags, ShapeTag{"ignore", "true"})
529	}
530
531	if ref.Shape.Sensitive {
532		tags = append(tags, ShapeTag{"sensitive", "true"})
533	}
534
535	return fmt.Sprintf("`%s`", tags)
536}
537
538// Docstring returns the godocs formated documentation
539func (ref *ShapeRef) Docstring() string {
540	if ref.Documentation != "" {
541		return strings.Trim(ref.Documentation, "\n ")
542	}
543	return ref.Shape.Docstring()
544}
545
546// Docstring returns the godocs formated documentation
547func (s *Shape) Docstring() string {
548	return strings.Trim(s.Documentation, "\n ")
549}
550
551// IndentedDocstring is the indented form of the doc string.
552func (ref *ShapeRef) IndentedDocstring() string {
553	doc := ref.Docstring()
554	return strings.Replace(doc, "// ", "//   ", -1)
555}
556
557var goCodeStringerTmpl = template.Must(template.New("goCodeStringerTmpl").Parse(`
558// String returns the string representation
559func (s {{ .ShapeName }}) String() string {
560	return awsutil.Prettify(s)
561}
562// GoString returns the string representation
563func (s {{ .ShapeName }}) GoString() string {
564	return s.String()
565}
566`))
567
568// GoCodeStringers renders the Stringers for API input/output shapes
569func (s *Shape) GoCodeStringers() string {
570	w := bytes.Buffer{}
571	if err := goCodeStringerTmpl.Execute(&w, s); err != nil {
572		panic(fmt.Sprintln("Unexpected error executing GoCodeStringers template", err))
573	}
574
575	return w.String()
576}
577
578var enumStrip = regexp.MustCompile(`[^a-zA-Z0-9_:\./-]`)
579var enumDelims = regexp.MustCompile(`[-_:\./]+`)
580var enumCamelCase = regexp.MustCompile(`([a-z])([A-Z])`)
581
582// EnumName returns the Nth enum in the shapes Enum list
583func (s *Shape) EnumName(n int) string {
584	enum := s.Enum[n]
585	enum = enumStrip.ReplaceAllLiteralString(enum, "")
586	enum = enumCamelCase.ReplaceAllString(enum, "$1-$2")
587	parts := enumDelims.Split(enum, -1)
588	for i, v := range parts {
589		v = strings.ToLower(v)
590		parts[i] = ""
591		if len(v) > 0 {
592			parts[i] = strings.ToUpper(v[0:1])
593		}
594		if len(v) > 1 {
595			parts[i] += v[1:]
596		}
597	}
598	enum = strings.Join(parts, "")
599	enum = strings.ToUpper(enum[0:1]) + enum[1:]
600	return enum
601}
602
603// NestedShape returns the shape pointer value for the shape which is nested
604// under the current shape. If the shape is not nested nil will be returned.
605//
606// strucutures, the current shape is returned
607// map: the value shape of the map is returned
608// list: the element shape of the list is returned
609func (s *Shape) NestedShape() *Shape {
610	var nestedShape *Shape
611	switch s.Type {
612	case "structure":
613		nestedShape = s
614	case "map":
615		nestedShape = s.ValueRef.Shape
616	case "list":
617		nestedShape = s.MemberRef.Shape
618	}
619
620	return nestedShape
621}
622
623var structShapeTmpl = func() *template.Template {
624	shapeTmpl := template.Must(
625		template.New("structShapeTmpl").
626			Funcs(template.FuncMap{
627				"GetCrosslinkURL":  GetCrosslinkURL,
628				"GetDeprecatedMsg": getDeprecatedMessage,
629			}).
630			Parse(structShapeTmplDef),
631	)
632
633	template.Must(
634		shapeTmpl.AddParseTree(
635			"eventStreamAPILoopMethodTmpl", eventStreamAPILoopMethodTmpl.Tree),
636	)
637
638	template.Must(
639		shapeTmpl.AddParseTree(
640			"eventStreamEventShapeTmpl", eventStreamEventShapeTmpl.Tree),
641	)
642	template.Must(
643		shapeTmpl.AddParseTree(
644			"eventStreamExceptionEventShapeTmpl",
645			eventStreamExceptionEventShapeTmpl.Tree),
646	)
647	shapeTmpl.Funcs(eventStreamEventShapeTmplFuncs)
648
649	template.Must(
650		shapeTmpl.AddParseTree(
651			"hostLabelsShapeTmpl",
652			hostLabelsShapeTmpl.Tree),
653	)
654
655	return shapeTmpl
656}()
657
658const structShapeTmplDef = `
659{{ .Docstring }}
660{{ if .Deprecated -}}
661{{ if .Docstring -}}
662//
663{{ end -}}
664// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ShapeName }}
665{{ end -}}
666{{ $context := . -}}
667type {{ .ShapeName }} struct {
668	_ struct{} {{ .GoTags true false }}
669
670	{{ range $_, $name := $context.MemberNames -}}
671		{{ $elem := index $context.MemberRefs $name -}}
672		{{ $isBlob := $context.WillRefBeBase64Encoded $name -}}
673		{{ $isRequired := $context.IsRequired $name -}}
674		{{ $doc := $elem.Docstring -}}
675
676		{{ if $doc -}}
677			{{ $doc }}
678			{{ if $elem.Deprecated -}}
679			//
680			// Deprecated: {{ GetDeprecatedMsg $elem.DeprecatedMsg $name }}
681			{{ end -}}
682		{{ end -}}
683		{{ if $isBlob -}}
684			{{ if $doc -}}
685				//
686			{{ end -}}
687			// {{ $name }} is automatically base64 encoded/decoded by the SDK.
688		{{ end -}}
689		{{ if $isRequired -}}
690			{{ if or $doc $isBlob -}}
691				//
692			{{ end -}}
693			// {{ $name }} is a required field
694		{{ end -}}
695		{{ $name }} {{ $context.GoStructType $name $elem }} {{ $elem.GoTags false $isRequired }}
696
697	{{ end }}
698}
699{{ if not .API.NoStringerMethods }}
700	{{ .GoCodeStringers }}
701{{ end }}
702{{ if not (or .API.NoValidataShapeMethods .Exception) }}
703	{{ if .Validations -}}
704		{{ .Validations.GoCode . }}
705	{{ end }}
706{{ end }}
707
708{{ if not (or .API.NoGenStructFieldAccessors .Exception) }}
709	{{ $builderShapeName := print .ShapeName -}}
710	{{ range $_, $name := $context.MemberNames -}}
711		{{ $elem := index $context.MemberRefs $name -}}
712
713		// Set{{ $name }} sets the {{ $name }} field's value.
714		func (s *{{ $builderShapeName }}) Set{{ $name }}(v {{ $context.GoStructValueType $name $elem }}) *{{ $builderShapeName }} {
715			{{ if $elem.UseIndirection -}}
716				s.{{ $name }} = &v
717			{{ else -}}
718				s.{{ $name }} = v
719			{{ end -}}
720			return s
721		}
722
723		{{ if $elem.GenerateGetter -}}
724			func (s *{{ $builderShapeName }}) get{{ $name }}() (v {{ $context.GoStructValueType $name $elem }}) {
725				{{ if $elem.UseIndirection -}}
726					if s.{{ $name }} == nil {
727						return v
728					}
729					return *s.{{ $name }}
730				{{ else -}}
731					return s.{{ $name }}
732				{{ end -}}
733			}
734		{{- end }}
735	{{ end }}
736{{ end }}
737
738{{ if $.EventStreamsMemberName }}
739	{{ template "eventStreamAPILoopMethodTmpl" $ }}
740{{ end }}
741
742{{ if $.EventFor }}
743	{{ template "eventStreamEventShapeTmpl" $ }}
744
745	{{- if $.Exception }}
746		{{ template "eventStreamExceptionEventShapeTmpl" $ }}
747	{{ end -}}
748{{ end }}
749
750{{ if $.HasHostLabelMembers }}
751	{{ template "hostLabelsShapeTmpl" $ }}
752{{ end }}
753`
754
755var enumShapeTmpl = template.Must(template.New("EnumShape").Parse(`
756{{ .Docstring }}
757const (
758	{{ $context := . -}}
759	{{ range $index, $elem := .Enum -}}
760		{{ $name := index $context.EnumConsts $index -}}
761		// {{ $name }} is a {{ $context.ShapeName }} enum value
762		{{ $name }} = "{{ $elem }}"
763
764	{{ end }}
765)
766`))
767
768// GoCode returns the rendered Go code for the Shape.
769func (s *Shape) GoCode() string {
770	w := &bytes.Buffer{}
771
772	switch {
773	case s.EventStreamAPI != nil:
774		if err := renderEventStreamAPIShape(w, s); err != nil {
775			panic(
776				fmt.Sprintf(
777					"failed to generate eventstream API shape, %s, %v",
778					s.ShapeName, err),
779			)
780		}
781	case s.Type == "structure":
782		if err := structShapeTmpl.Execute(w, s); err != nil {
783			panic(
784				fmt.Sprintf(
785					"Failed to generate struct shape %s, %v",
786					s.ShapeName, err),
787			)
788		}
789	case s.IsEnum():
790		if err := enumShapeTmpl.Execute(w, s); err != nil {
791			panic(
792				fmt.Sprintf(
793					"Failed to generate enum shape %s, %v",
794					s.ShapeName, err),
795			)
796		}
797	default:
798		panic(fmt.Sprintln("Cannot generate toplevel shape for", s.Type))
799	}
800
801	return w.String()
802}
803
804// IsEnum returns whether this shape is an enum list
805func (s *Shape) IsEnum() bool {
806	return s.Type == "string" && len(s.Enum) > 0
807}
808
809// IsRequired returns if member is a required field. Required fields are fields
810// marked as required, hostLabels, or location of uri path.
811func (s *Shape) IsRequired(member string) bool {
812	ref, ok := s.MemberRefs[member]
813	if !ok {
814		panic(fmt.Sprintf(
815			"attempted to check required for unknown member, %s.%s",
816			s.ShapeName, member,
817		))
818	}
819	if ref.IdempotencyToken || ref.Shape.IdempotencyToken {
820		return false
821	}
822	if ref.Location == "uri" || ref.HostLabel {
823		return true
824	}
825	for _, n := range s.Required {
826		if n == member {
827			return true
828		}
829	}
830	return false
831}
832
833// IsInternal returns whether the shape was defined in this package
834func (s *Shape) IsInternal() bool {
835	return s.resolvePkg == ""
836}
837
838// removeRef removes a shape reference from the list of references this
839// shape is used in.
840func (s *Shape) removeRef(ref *ShapeRef) {
841	r := s.refs
842	for i := 0; i < len(r); i++ {
843		if r[i] == ref {
844			j := i + 1
845			copy(r[i:], r[j:])
846			for k, n := len(r)-j+i, len(r); k < n; k++ {
847				r[k] = nil // free up the end of the list
848			} // for k
849			s.refs = r[:len(r)-j+i]
850			break
851		}
852	}
853}
854
855func (s *Shape) WillRefBeBase64Encoded(refName string) bool {
856	payloadRefName := s.Payload
857	if payloadRefName == refName {
858		return false
859	}
860
861	ref, ok := s.MemberRefs[refName]
862	if !ok {
863		panic(fmt.Sprintf("shape %s does not contain %q refName", s.ShapeName, refName))
864	}
865
866	return ref.Shape.Type == "blob"
867}
868
869// Clone returns a cloned version of the shape with all references clones.
870//
871// Does not clone EventStream or Validate related values.
872func (s *Shape) Clone(newName string) *Shape {
873	if s.AliasedShapeName {
874		panic(fmt.Sprintf("attempted to clone and rename %s, but flagged as aliased",
875			s.ShapeName))
876	}
877
878	n := new(Shape)
879	*n = *s
880
881	debugLogger.Logln("cloning", s.ShapeName, "to", newName)
882
883	n.MemberRefs = map[string]*ShapeRef{}
884	for k, r := range s.MemberRefs {
885		nr := new(ShapeRef)
886		*nr = *r
887		nr.Shape.refs = append(nr.Shape.refs, nr)
888		n.MemberRefs[k] = nr
889	}
890
891	if n.MemberRef.Shape != nil {
892		n.MemberRef.Shape.refs = append(n.MemberRef.Shape.refs, &n.MemberRef)
893	}
894	if n.KeyRef.Shape != nil {
895		n.KeyRef.Shape.refs = append(n.KeyRef.Shape.refs, &n.KeyRef)
896	}
897	if n.ValueRef.Shape != nil {
898		n.ValueRef.Shape.refs = append(n.ValueRef.Shape.refs, &n.ValueRef)
899	}
900
901	n.refs = []*ShapeRef{}
902
903	n.Required = append([]string{}, n.Required...)
904	n.Enum = append([]string{}, n.Enum...)
905	n.EnumConsts = append([]string{}, n.EnumConsts...)
906
907	n.OrigShapeName = n.ShapeName
908	n.API.Shapes[newName] = n
909	n.ShapeName = newName
910
911	return n
912}
913