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