55
66package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits
77
8+ import software.amazon.smithy.codegen.core.CodegenException
89import software.amazon.smithy.model.knowledge.HttpBinding
910import software.amazon.smithy.model.shapes.BooleanShape
1011import software.amazon.smithy.model.shapes.ByteShape
1112import software.amazon.smithy.model.shapes.DoubleShape
1213import software.amazon.smithy.model.shapes.FloatShape
1314import software.amazon.smithy.model.shapes.IntegerShape
1415import software.amazon.smithy.model.shapes.LongShape
16+ import software.amazon.smithy.model.shapes.ShapeType
1517import software.amazon.smithy.model.shapes.ShortShape
1618import software.amazon.smithy.model.traits.HttpQueryTrait
19+ import software.amazon.smithy.model.traits.StreamingTrait
20+ import software.amazon.smithy.swift.codegen.ClientRuntimeTypes
1721import software.amazon.smithy.swift.codegen.SwiftWriter
22+ import software.amazon.smithy.swift.codegen.declareSection
1823import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor
1924import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
2025import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable
2126import software.amazon.smithy.swift.codegen.model.isBoxed
27+ import software.amazon.smithy.swift.codegen.model.targetOrSelf
2228
2329interface HttpResponseTraitWithoutHttpPayloadFactory {
2430 fun construct (
@@ -37,39 +43,88 @@ class HttpResponseTraitWithoutHttpPayload(
3743) : HttpResponseBindingRenderable {
3844 override fun render () {
3945 val bodyMembers = responseBindings.filter { it.location == HttpBinding .Location .DOCUMENT }
40-
4146 val bodyMembersWithoutQueryTrait = bodyMembers
4247 .filter { ! it.member.hasTrait(HttpQueryTrait ::class .java) }
4348 .toMutableSet()
49+ val streamingMember = bodyMembers.firstOrNull { it.member.targetOrSelf(ctx.model).hasTrait(StreamingTrait ::class .java) }
4450
45- val bodyMembersWithoutQueryTraitMemberNames = bodyMembersWithoutQueryTrait.map { ctx.symbolProvider.toMemberName(it.member) }
51+ if (streamingMember != null ) {
52+ writeStreamingMember(streamingMember)
53+ } else if (bodyMembersWithoutQueryTrait.isNotEmpty()) {
54+ writeNonStreamingMembers(bodyMembersWithoutQueryTrait)
55+ }
56+ }
4657
47- if (bodyMembersWithoutQueryTrait.isNotEmpty()) {
48- writer.write(" if let data = try httpResponse.body.toData()," )
49- writer.indent()
50- writer.write(" let responseDecoder = decoder {" )
51- writer.write(" let output: ${outputShapeName} Body = try responseDecoder.decode(responseBody: data)" )
52- bodyMembersWithoutQueryTraitMemberNames.sorted().forEach {
53- writer.write(" self.$it = output.$it " )
54- }
55- writer.dedent()
56- writer.write(" } else {" )
57- writer.indent()
58- bodyMembersWithoutQueryTrait.sortedBy { it.memberName }.forEach {
59- val memberName = ctx.symbolProvider.toMemberName(it.member)
60- val type = ctx.model.expectShape(it.member.target)
61- val value = if (ctx.symbolProvider.toSymbol(it.member).isBoxed()) " nil" else {
62- when (type) {
63- is IntegerShape , is ByteShape , is ShortShape , is LongShape -> 0
64- is FloatShape , is DoubleShape -> 0.0
65- is BooleanShape -> false
66- else -> " nil"
58+ fun writeStreamingMember (streamingMember : HttpBindingDescriptor ) {
59+ val shape = ctx.model.expectShape(streamingMember.member.target)
60+ val symbol = ctx.symbolProvider.toSymbol(shape)
61+ val memberName = ctx.symbolProvider.toMemberName(streamingMember.member)
62+ when (shape.type) {
63+ ShapeType .UNION -> {
64+ writer.openBlock(" if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder {" , " } else {" ) {
65+ writer.declareSection(HttpResponseTraitWithHttpPayload .MessageDecoderSectionId ) {
66+ writer.write(" let messageDecoder: \$ D" , ClientRuntimeTypes .EventStream .MessageDecoder )
6767 }
68+ writer.write(
69+ " let decoderStream = \$ L<\$ N>(stream: stream, messageDecoder: messageDecoder, responseDecoder: responseDecoder)" ,
70+ ClientRuntimeTypes .EventStream .MessageDecoderStream ,
71+ symbol
72+ )
73+ writer.write(" self.\$ L = decoderStream.toAsyncStream()" , memberName)
74+ }
75+ writer.indent()
76+ writer.write(" self.\$ L = nil" , memberName).closeBlock(" }" )
77+ }
78+ ShapeType .BLOB -> {
79+ writer.write(" switch httpResponse.body {" )
80+ .write(" case .data(let data):" )
81+ .indent()
82+ writer.write(" self.\$ L = .data(data)" , memberName)
83+
84+ // For binary streams, we need to set the member to the stream directly.
85+ // this allows us to stream the data directly to the user
86+ // without having to buffer it in memory.
87+ writer.dedent()
88+ .write(" case .stream(let stream):" )
89+ .indent()
90+ writer.write(" self.\$ L = .stream(stream)" , memberName)
91+ writer.dedent()
92+ .write(" case .none:" )
93+ .indent()
94+ .write(" self.\$ L = nil" , memberName).closeBlock(" }" )
95+ }
96+ else -> {
97+ throw CodegenException (" member shape ${streamingMember.member} cannot stream" )
98+ }
99+ }
100+ }
101+
102+ fun writeNonStreamingMembers (members : Set <HttpBindingDescriptor >) {
103+ val memberNames = members.map { ctx.symbolProvider.toMemberName(it.member) }
104+ writer.write(" if let data = try httpResponse.body.toData()," )
105+ writer.indent()
106+ writer.write(" let responseDecoder = decoder {" )
107+ writer.write(" let output: ${outputShapeName} Body = try responseDecoder.decode(responseBody: data)" )
108+ memberNames.sorted().forEach {
109+ writer.write(" self.$it = output.$it " )
110+ }
111+ writer.dedent()
112+ writer.write(" } else {" )
113+ writer.indent()
114+ members.sortedBy { it.memberName }.forEach {
115+ val memberName = ctx.symbolProvider.toMemberName(it.member)
116+ val type = ctx.model.expectShape(it.member.target)
117+ val value = if (ctx.symbolProvider.toSymbol(it.member).isBoxed()) " nil" else {
118+ when (type) {
119+ is IntegerShape , is ByteShape , is ShortShape , is LongShape -> 0
120+ is FloatShape , is DoubleShape -> 0.0
121+ is BooleanShape -> false
122+ else -> " nil"
68123 }
69- writer.write(" self.$memberName = $value " )
70124 }
71- writer.dedent()
72- writer.write(" }" )
125+ writer.write(" self.$memberName = $value " )
73126 }
127+ writer.dedent()
128+ writer.write(" }" )
74129 }
75130}
0 commit comments