Skip to content

Commit b1d310f

Browse files
authored
[Generator] Design away EncodableBodyContent (#152)
[Generator] Design away EncodableBodyContent ### Motivation See apple/swift-openapi-runtime#30 Depends on Runtime 0.1.6 ### Modifications Adapted the generator code for the runtime changes. Also, since this was factored out of the big PR (#146) adding multiple content types, you can see the code was prepared for handling multiple content types, even though still in this PR it only ever gets N=1. ### Result The generated code uses the new simplified runtime functions, and prepares us for multiple content types. ### Test Plan Adapted file-based reference tests to generate the new syntax. Reviewed by: simonjbeaumont Builds: ✔︎ pull request validation (5.8) - Build finished. ✔︎ pull request validation (5.9) - Build finished. ✔︎ pull request validation (docc test) - Build finished. ✔︎ pull request validation (integration test) - Build finished. ✔︎ pull request validation (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. #152
1 parent 77e1553 commit b1d310f

File tree

5 files changed

+224
-276
lines changed

5 files changed

+224
-276
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ let package = Package(
7878
),
7979

8080
// Tests-only: Runtime library linked by generated code
81-
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.1.3")),
81+
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.1.6")),
8282

8383
// Build and preview docs
8484
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),

Sources/_OpenAPIGeneratorCore/Translator/RequestBody/translateRequestBody.swift

Lines changed: 50 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -175,63 +175,60 @@ extension ClientFileTranslator {
175175
requestVariableName: String,
176176
inputVariableName: String
177177
) throws -> Expression {
178+
let contents = [requestBody.content]
179+
var cases: [SwitchCaseDescription] = contents.map { typedContent in
180+
let content = typedContent.content
181+
let contentType = content.contentType
182+
let contentTypeIdentifier = contentSwiftName(contentType)
183+
let contentTypeHeaderValue = contentType.headerValueForSending
178184

179-
let typedContent = requestBody.content
180-
let content = typedContent.content
181-
let contentType = content.contentType
182-
let contentTypeIdentifier = contentSwiftName(contentType)
183-
let contentTypeHeaderValue = contentType.headerValueForSending
184-
185-
let transformReturnExpr: Expression = .return(
186-
.dot("init")
187-
.call([
188-
.init(
189-
label: "value",
190-
expression: .identifier("value")
191-
),
192-
.init(
193-
label: "contentType",
194-
expression: .literal(contentTypeHeaderValue)
195-
),
196-
])
197-
)
198-
let caseDecl: SwitchCaseDescription = .init(
199-
kind: .case(.dot(contentTypeIdentifier), ["value"]),
200-
body: [
201-
.expression(transformReturnExpr)
202-
]
203-
)
204-
let transformExpr: Expression = .closureInvocation(
205-
argumentNames: ["wrapped"],
206-
body: [
207-
.expression(
208-
.switch(
209-
switchedExpression: .identifier("wrapped"),
210-
cases: [
211-
caseDecl
212-
]
213-
)
185+
let bodyAssignExpr: Expression = .assignment(
186+
left: .identifier(requestVariableName).dot("body"),
187+
right: .try(
188+
.identifier("converter")
189+
.dot(
190+
"set\(requestBody.request.required ? "Required" : "Optional")RequestBodyAs\(contentType.codingStrategy.runtimeName)"
191+
)
192+
.call([
193+
.init(label: nil, expression: .identifier("value")),
194+
.init(
195+
label: "headerFields",
196+
expression: .inOut(
197+
.identifier(requestVariableName).dot("headerFields")
198+
)
199+
),
200+
.init(
201+
label: "contentType",
202+
expression: .literal(contentTypeHeaderValue)
203+
),
204+
])
214205
)
215-
]
216-
)
217-
return .assignment(
218-
left: .identifier(requestVariableName).dot("body"),
219-
right: .try(
220-
.identifier("converter")
221-
.dot(
222-
"set\(requestBody.request.required ? "Required" : "Optional")RequestBodyAs\(contentType.codingStrategy.runtimeName)"
206+
)
207+
let caseDesc: SwitchCaseDescription = .init(
208+
kind: .case(.dot(contentTypeIdentifier), ["value"]),
209+
body: [
210+
.expression(bodyAssignExpr)
211+
]
212+
)
213+
return caseDesc
214+
}
215+
if !requestBody.request.required {
216+
let noneCase: SwitchCaseDescription = .init(
217+
kind: .case(.dot("none")),
218+
body: [
219+
.expression(
220+
.assignment(
221+
left: .identifier(requestVariableName).dot("body"),
222+
right: .literal(.nil)
223+
)
223224
)
224-
.call([
225-
.init(label: nil, expression: .identifier(inputVariableName).dot("body")),
226-
.init(
227-
label: "headerFields",
228-
expression: .inOut(
229-
.identifier(requestVariableName).dot("headerFields")
230-
)
231-
),
232-
.init(label: "transforming", expression: transformExpr),
233-
])
225+
]
234226
)
227+
cases.insert(noneCase, at: 0)
228+
}
229+
return .switch(
230+
switchedExpression: .identifier(inputVariableName).dot("body"),
231+
cases: cases
235232
)
236233
}
237234
}

Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponseOutcome.swift

Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -306,73 +306,66 @@ extension ServerFileTranslator {
306306
}
307307
codeBlocks.append(contentsOf: headerExprs.map { .expression($0) })
308308

309-
if let typedContent = try bestSingleTypedContent(
310-
typedResponse.response.content,
311-
inParent: bodyTypeName
312-
) {
313-
let contentTypeHeaderValue = typedContent.content.contentType.headerValueForValidation
314-
let validateAcceptHeader: Expression = .try(
315-
.identifier("converter").dot("validateAcceptIfPresent")
316-
.call([
317-
.init(label: nil, expression: .literal(contentTypeHeaderValue)),
318-
.init(label: "in", expression: .identifier("request").dot("headerFields")),
319-
])
309+
let typedContents = [
310+
try bestSingleTypedContent(
311+
typedResponse.response.content,
312+
inParent: bodyTypeName
320313
)
321-
codeBlocks.append(.expression(validateAcceptHeader))
314+
]
315+
.compactMap { $0 }
322316

323-
let contentType = typedContent.content.contentType
324-
let switchContentCases: [SwitchCaseDescription] = [
325-
.init(
317+
if !typedContents.isEmpty {
318+
let switchContentCases: [SwitchCaseDescription] = typedContents.map { typedContent in
319+
320+
var caseCodeBlocks: [CodeBlock] = []
321+
322+
let contentTypeHeaderValue = typedContent.content.contentType.headerValueForValidation
323+
let validateAcceptHeader: Expression = .try(
324+
.identifier("converter").dot("validateAcceptIfPresent")
325+
.call([
326+
.init(label: nil, expression: .literal(contentTypeHeaderValue)),
327+
.init(label: "in", expression: .identifier("request").dot("headerFields")),
328+
])
329+
)
330+
caseCodeBlocks.append(.expression(validateAcceptHeader))
331+
332+
let contentType = typedContent.content.contentType
333+
let assignBodyExpr: Expression = .assignment(
334+
left: .identifier("response").dot("body"),
335+
right: .try(
336+
.identifier("converter")
337+
.dot("setResponseBodyAs\(contentType.codingStrategy.runtimeName)")
338+
.call([
339+
.init(label: nil, expression: .identifier("value")),
340+
.init(
341+
label: "headerFields",
342+
expression: .inOut(
343+
.identifier("response").dot("headerFields")
344+
)
345+
),
346+
.init(
347+
label: "contentType",
348+
expression: .literal(contentType.headerValueForSending)
349+
),
350+
])
351+
)
352+
)
353+
caseCodeBlocks.append(.expression(assignBodyExpr))
354+
355+
return .init(
326356
kind: .case(.dot(contentSwiftName(contentType)), ["value"]),
327-
body: [
328-
.expression(
329-
.return(
330-
.dot("init")
331-
.call([
332-
.init(
333-
label: "value",
334-
expression: .identifier("value")
335-
),
336-
.init(
337-
label: "contentType",
338-
expression: .literal(contentType.headerValueForSending)
339-
),
340-
])
341-
)
342-
)
343-
]
357+
body: caseCodeBlocks
344358
)
345-
]
359+
}
346360

347-
let transformExpr: Expression = .closureInvocation(
348-
argumentNames: ["wrapped"],
349-
body: [
350-
.expression(
351-
.switch(
352-
switchedExpression: .identifier("wrapped"),
353-
cases: switchContentCases
354-
)
361+
codeBlocks.append(
362+
.expression(
363+
.switch(
364+
switchedExpression: .identifier("value").dot("body"),
365+
cases: switchContentCases
355366
)
356-
]
357-
)
358-
let assignBodyExpr: Expression = .assignment(
359-
left: .identifier("response").dot("body"),
360-
right: .try(
361-
.identifier("converter")
362-
.dot("setResponseBodyAs\(contentType.codingStrategy.runtimeName)")
363-
.call([
364-
.init(label: nil, expression: .identifier("value").dot("body")),
365-
.init(
366-
label: "headerFields",
367-
expression: .inOut(
368-
.identifier("response").dot("headerFields")
369-
)
370-
),
371-
.init(label: "transforming", expression: transformExpr),
372-
])
373367
)
374368
)
375-
codeBlocks.append(.expression(assignBodyExpr))
376369
}
377370

378371
let returnExpr: Expression = .return(

Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,14 @@ public struct Client: APIProtocol {
151151
name: "accept",
152152
value: "application/json"
153153
)
154-
request.body = try converter.setRequiredRequestBodyAsJSON(
155-
input.body,
156-
headerFields: &request.headerFields,
157-
transforming: { wrapped in
158-
switch wrapped {
159-
case let .json(value):
160-
return .init(
161-
value: value,
162-
contentType: "application/json; charset=utf-8"
163-
)
164-
}
165-
}
166-
)
154+
switch input.body {
155+
case let .json(value):
156+
request.body = try converter.setRequiredRequestBodyAsJSON(
157+
value,
158+
headerFields: &request.headerFields,
159+
contentType: "application/json; charset=utf-8"
160+
)
161+
}
167162
return request
168163
},
169164
deserializer: { response in
@@ -257,19 +252,15 @@ public struct Client: APIProtocol {
257252
name: "accept",
258253
value: "application/json"
259254
)
260-
request.body = try converter.setOptionalRequestBodyAsJSON(
261-
input.body,
262-
headerFields: &request.headerFields,
263-
transforming: { wrapped in
264-
switch wrapped {
265-
case let .json(value):
266-
return .init(
267-
value: value,
268-
contentType: "application/json; charset=utf-8"
269-
)
270-
}
271-
}
272-
)
255+
switch input.body {
256+
case .none: request.body = nil
257+
case let .json(value):
258+
request.body = try converter.setOptionalRequestBodyAsJSON(
259+
value,
260+
headerFields: &request.headerFields,
261+
contentType: "application/json; charset=utf-8"
262+
)
263+
}
273264
return request
274265
},
275266
deserializer: { response in
@@ -317,16 +308,14 @@ public struct Client: APIProtocol {
317308
name: "accept",
318309
value: "application/octet-stream, application/json, text/plain"
319310
)
320-
request.body = try converter.setRequiredRequestBodyAsBinary(
321-
input.body,
322-
headerFields: &request.headerFields,
323-
transforming: { wrapped in
324-
switch wrapped {
325-
case let .binary(value):
326-
return .init(value: value, contentType: "application/octet-stream")
327-
}
328-
}
329-
)
311+
switch input.body {
312+
case let .binary(value):
313+
request.body = try converter.setRequiredRequestBodyAsBinary(
314+
value,
315+
headerFields: &request.headerFields,
316+
contentType: "application/octet-stream"
317+
)
318+
}
330319
return request
331320
},
332321
deserializer: { response in

0 commit comments

Comments
 (0)