in smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt [35:185]
fun render() {
bindings.forEach { hdrBinding ->
val memberTarget = ctx.model.expectShape(hdrBinding.member.target)
val memberName = ctx.symbolProvider.toMemberName(hdrBinding.member)
val headerName = hdrBinding.locationName
val headerDeclaration = "${memberName}HeaderValue"
val isBoxed = ctx.symbolProvider.toSymbol(memberTarget).isBoxed()
writer.write("if let $headerDeclaration = httpResponse.headers.value(for: \$S) {", headerName)
writer.indent()
when (memberTarget) {
is NumberShape -> {
val memberValue = stringToNumber(memberTarget, headerDeclaration)
writer.write("self.\$L = $memberValue", memberName)
}
is BlobShape -> {
val memberValue = "$headerDeclaration.data(using: .utf8)"
writer.write("self.\$L = $memberValue", memberName)
}
is BooleanShape -> {
val memberValue = "${SwiftTypes.Bool}($headerDeclaration) ?? false"
writer.write("self.\$L = $memberValue", memberName)
}
is StringShape -> {
val memberValue = when {
memberTarget.hasTrait<EnumTrait>() -> {
val enumSymbol = ctx.symbolProvider.toSymbol(memberTarget)
"$enumSymbol(rawValue: $headerDeclaration)"
}
memberTarget.hasTrait<MediaTypeTrait>() -> {
"try $headerDeclaration.base64DecodedString()"
}
else -> {
headerDeclaration
}
}
writer.write("self.\$L = $memberValue", memberName)
}
is TimestampShape -> {
val bindingIndex = HttpBindingIndex.of(ctx.model)
val tsFormat = bindingIndex.determineTimestampFormat(
hdrBinding.member,
HttpBinding.Location.HEADER,
defaultTimestampFormat
)
var memberValue = stringToDate(headerDeclaration, tsFormat)
if (tsFormat == TimestampFormatTrait.Format.EPOCH_SECONDS) {
memberValue = stringToDate("${headerDeclaration}Double", tsFormat)
writer.write("if let ${headerDeclaration}Double = \$N(\$LHeaderValue) {", SwiftTypes.Double, memberName)
writer.indent()
writer.write("self.\$L = $memberValue", memberName)
writer.dedent()
writer.write("} else {")
writer.indent()
writer.write("throw \$N.deserializationFailed(HeaderDeserializationError.invalidTimestampHeader(value: \$LHeaderValue))", ClientRuntimeTypes.Core.ClientError, memberName)
writer.dedent()
writer.write("}")
} else {
writer.write("self.\$L = $memberValue", memberName)
}
}
is CollectionShape -> {
// member > boolean, number, string, or timestamp
// headers are List<String>, get the internal mapping function contents (if any) to convert
// to the target symbol type
// we also have to handle multiple comma separated values (e.g. 'X-Foo': "1, 2, 3"`)
var splitFn = "splitHeaderListValues"
var splitFnPrefix = ""
var invalidHeaderListErrorName = "invalidNumbersHeaderList"
val conversion = when (val collectionMemberTarget = ctx.model.expectShape(memberTarget.member.target)) {
is BooleanShape -> {
invalidHeaderListErrorName = "invalidBooleanHeaderList"
"${SwiftTypes.Bool}(\$0)"
}
is NumberShape -> "(${stringToNumber(collectionMemberTarget, "\$0")} ?? 0)"
is TimestampShape -> {
val bindingIndex = HttpBindingIndex.of(ctx.model)
val tsFormat = bindingIndex.determineTimestampFormat(
hdrBinding.member,
HttpBinding.Location.HEADER,
defaultTimestampFormat
)
if (tsFormat == TimestampFormatTrait.Format.HTTP_DATE) {
splitFn = "splitHttpDateHeaderListValues"
splitFnPrefix = "try "
}
invalidHeaderListErrorName = "invalidTimestampHeaderList"
"(${stringToDate("\$0", tsFormat)} ?? ${ClientRuntimeTypes.Core.Date}())"
}
is StringShape -> {
invalidHeaderListErrorName = "invalidStringHeaderList"
when {
collectionMemberTarget.hasTrait<EnumTrait>() -> {
val enumSymbol = ctx.symbolProvider.toSymbol(collectionMemberTarget)
"($enumSymbol(rawValue: \$0) ?? $enumSymbol(rawValue: \"Bar\")!)"
}
collectionMemberTarget.hasTrait<MediaTypeTrait>() -> {
"try \$0.base64EncodedString()"
}
else -> ""
}
}
else -> throw CodegenException("invalid member type for header collection: binding: $hdrBinding; member: $memberName")
}
val mapFn = if (conversion.isNotEmpty()) ".map { $conversion }" else ""
var memberValue = "${memberName}HeaderValues$mapFn"
if (memberTarget.isSetShape) {
memberValue = "${SwiftTypes.Set}(${memberName}HeaderValues)"
}
writer.write("if let ${memberName}HeaderValues = $splitFnPrefix$splitFn(${memberName}HeaderValue) {")
writer.indent()
// render map function
val collectionMemberTargetShape = ctx.model.expectShape(memberTarget.member.target)
val collectionMemberTargetSymbol = ctx.symbolProvider.toSymbol(collectionMemberTargetShape)
if (!collectionMemberTargetSymbol.isBoxed()) {
writer.openBlock("self.\$L = try \$LHeaderValues.map {", "}", memberName, memberName) {
val transformedHeaderDeclaration = "${memberName}Transformed"
writer.openBlock("guard let \$L = \$L else {", "}", transformedHeaderDeclaration, conversion) {
writer.write("throw \$N.deserializationFailed(HeaderDeserializationError.\$L(value: \$LHeaderValue))", ClientRuntimeTypes.Core.ClientError, invalidHeaderListErrorName, memberName)
}
writer.write("return \$L", transformedHeaderDeclaration)
}
} else {
writer.write("self.\$L = \$L", memberName, memberValue)
}
writer.dedent()
writer.write("} else {")
writer.indent()
writer.write("self.\$L = nil", memberName)
writer.dedent()
writer.write("}")
}
else -> throw CodegenException("unknown deserialization: header binding: $hdrBinding; member: `$memberName`")
}
writer.dedent()
writer.write("} else {")
writer.indent()
var assignmentValue = "nil"
when (memberTarget) {
is NumberShape -> {
assignmentValue = if (isBoxed) "nil" else "0"
}
is BooleanShape -> {
assignmentValue = if (isBoxed) "nil" else "false"
}
}
writer.write("self.$memberName = $assignmentValue")
writer.dedent()
writer.write("}")
}
}