Skip to content

Commit 6a81c26

Browse files
authored
Stop generating empty input/output structs (query, headers, ...) (#235)
Stop generating empty input/output structs (query, headers, ...) ### Motivation Fixes #229. ### Modifications Stops generating the structs and properties when they're empty. ### Result About 10% reduction in generated code, more clear generated code. ### Test Plan Adapted tests, check those out first to see how the generated code is changing. Reviewed by: glbrntt 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 (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. ✖︎ pull request validation (integration test) - Build finished. #235
1 parent add7803 commit 6a81c26

File tree

10 files changed

+220
-687
lines changed

10 files changed

+220
-687
lines changed

Sources/_OpenAPIGeneratorCore/Translator/RequestBody/translateRequestBody.swift

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -76,42 +76,31 @@ extension TypesFileTranslator {
7676
/// - Parameters:
7777
/// - unresolvedRequestBody: An unresolved request body.
7878
/// - parent: The type name of the parent structure.
79+
/// - Returns: The property blueprint; nil if no body is specified.
7980
func parseRequestBodyAsProperty(
8081
for unresolvedRequestBody: UnresolvedRequest?,
8182
inParent parent: TypeName
82-
) throws -> PropertyBlueprint {
83-
let bodyEnumTypeName: TypeName
84-
let isRequestBodyOptional: Bool
85-
let extraDecls: [Declaration]
86-
if let _requestBody = unresolvedRequestBody,
83+
) throws -> PropertyBlueprint? {
84+
guard let _requestBody = unresolvedRequestBody,
8785
let requestBody = try typedRequestBody(
8886
from: _requestBody,
8987
inParent: parent
9088
)
91-
{
92-
isRequestBodyOptional = !requestBody.request.required
93-
bodyEnumTypeName = requestBody.typeUsage.typeName
94-
if requestBody.isInlined {
95-
extraDecls = [
96-
try translateRequestBodyInTypes(
97-
requestBody: requestBody
98-
)
99-
]
100-
} else {
101-
extraDecls = []
102-
}
103-
} else {
104-
isRequestBodyOptional = true
105-
bodyEnumTypeName = parent.appending(
106-
swiftComponent: Constants.Operation.Body.typeName,
107-
jsonComponent: "requestBody"
108-
)
89+
else {
90+
return nil
91+
}
92+
93+
let isRequestBodyOptional = !requestBody.request.required
94+
let bodyEnumTypeName = requestBody.typeUsage.typeName
95+
let extraDecls: [Declaration]
96+
if requestBody.isInlined {
10997
extraDecls = [
110-
translateRequestBodyInTypes(
111-
typeName: bodyEnumTypeName,
112-
members: []
98+
try translateRequestBodyInTypes(
99+
requestBody: requestBody
113100
)
114101
]
102+
} else {
103+
extraDecls = []
115104
}
116105

117106
let bodyEnumTypeUsage = bodyEnumTypeName.asUsage

Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift

Lines changed: 83 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extension TypesFileTranslator {
2424
response: TypedResponse
2525
) throws -> Declaration {
2626
let response = response.response
27+
2728
let headersTypeName = typeName.appending(
2829
swiftComponent: Constants.Operation.Output.Payload.Headers.typeName,
2930
jsonComponent: "headers"
@@ -32,35 +33,40 @@ extension TypesFileTranslator {
3233
from: response,
3334
inParent: headersTypeName
3435
)
35-
let headerProperties: [PropertyBlueprint] = try headers.map { header in
36-
try parseResponseHeaderAsProperty(
37-
for: header,
38-
parent: headersTypeName
36+
let headersProperty: PropertyBlueprint?
37+
if !headers.isEmpty {
38+
let headerProperties: [PropertyBlueprint] = try headers.map { header in
39+
try parseResponseHeaderAsProperty(
40+
for: header,
41+
parent: headersTypeName
42+
)
43+
}
44+
let headerStructComment: Comment? =
45+
headersTypeName
46+
.docCommentWithUserDescription(nil)
47+
let headersStructBlueprint: StructBlueprint = .init(
48+
comment: headerStructComment,
49+
access: config.access,
50+
typeName: headersTypeName,
51+
conformances: Constants.Operation.Output.Payload.Headers.conformances,
52+
properties: headerProperties
53+
)
54+
let headersStructDecl = translateStructBlueprint(
55+
headersStructBlueprint
3956
)
57+
headersProperty = PropertyBlueprint(
58+
comment: .doc("Received HTTP response headers"),
59+
originalName: Constants.Operation.Output.Payload.Headers.variableName,
60+
typeUsage: headersTypeName.asUsage,
61+
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
62+
associatedDeclarations: [
63+
headersStructDecl
64+
],
65+
asSwiftSafeName: swiftSafeName
66+
)
67+
} else {
68+
headersProperty = nil
4069
}
41-
let headerStructComment: Comment? =
42-
headersTypeName
43-
.docCommentWithUserDescription(nil)
44-
let headersStructBlueprint: StructBlueprint = .init(
45-
comment: headerStructComment,
46-
access: config.access,
47-
typeName: headersTypeName,
48-
conformances: Constants.Operation.Output.Payload.Headers.conformances,
49-
properties: headerProperties
50-
)
51-
let headersStructDecl = translateStructBlueprint(
52-
headersStructBlueprint
53-
)
54-
let headersProperty = PropertyBlueprint(
55-
comment: .doc("Received HTTP response headers"),
56-
originalName: Constants.Operation.Output.Payload.Headers.variableName,
57-
typeUsage: headersTypeName.asUsage,
58-
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
59-
associatedDeclarations: [
60-
headersStructDecl
61-
],
62-
asSwiftSafeName: swiftSafeName
63-
)
6470

6571
let bodyTypeName = typeName.appending(
6672
swiftComponent: Constants.Operation.Body.typeName,
@@ -70,54 +76,60 @@ extension TypesFileTranslator {
7076
response.content,
7177
inParent: bodyTypeName
7278
)
73-
var bodyCases: [Declaration] = []
74-
for typedContent in typedContents {
75-
let contentType = typedContent.content.contentType
76-
let identifier = contentSwiftName(contentType)
77-
let associatedType = typedContent.resolvedTypeUsage
78-
if TypeMatcher.isInlinable(typedContent.content.schema), let inlineType = typedContent.typeUsage {
79-
let inlineTypeDecls = try translateSchema(
80-
typeName: inlineType.typeName,
81-
schema: typedContent.content.schema,
82-
overrides: .none
79+
80+
let contentProperty: PropertyBlueprint?
81+
if !typedContents.isEmpty {
82+
var bodyCases: [Declaration] = []
83+
for typedContent in typedContents {
84+
let contentType = typedContent.content.contentType
85+
let identifier = contentSwiftName(contentType)
86+
let associatedType = typedContent.resolvedTypeUsage
87+
if TypeMatcher.isInlinable(typedContent.content.schema), let inlineType = typedContent.typeUsage {
88+
let inlineTypeDecls = try translateSchema(
89+
typeName: inlineType.typeName,
90+
schema: typedContent.content.schema,
91+
overrides: .none
92+
)
93+
bodyCases.append(contentsOf: inlineTypeDecls)
94+
}
95+
96+
let bodyCase: Declaration = .commentable(
97+
contentType.docComment(typeName: bodyTypeName),
98+
.enumCase(
99+
name: identifier,
100+
kind: .nameWithAssociatedValues([
101+
.init(type: associatedType.fullyQualifiedSwiftName)
102+
])
103+
)
83104
)
84-
bodyCases.append(contentsOf: inlineTypeDecls)
105+
bodyCases.append(bodyCase)
85106
}
86-
87-
let bodyCase: Declaration = .commentable(
88-
contentType.docComment(typeName: bodyTypeName),
89-
.enumCase(
90-
name: identifier,
91-
kind: .nameWithAssociatedValues([
92-
.init(type: associatedType.fullyQualifiedSwiftName)
93-
])
107+
let hasNoContent: Bool = bodyCases.isEmpty
108+
let contentEnumDecl: Declaration = .commentable(
109+
bodyTypeName.docCommentWithUserDescription(nil),
110+
.enum(
111+
isFrozen: true,
112+
accessModifier: config.access,
113+
name: bodyTypeName.shortSwiftName,
114+
conformances: Constants.Operation.Body.conformances,
115+
members: bodyCases
94116
)
95117
)
96-
bodyCases.append(bodyCase)
97-
}
98-
let hasNoContent: Bool = bodyCases.isEmpty
99-
let contentEnumDecl: Declaration = .commentable(
100-
bodyTypeName.docCommentWithUserDescription(nil),
101-
.enum(
102-
isFrozen: true,
103-
accessModifier: config.access,
104-
name: bodyTypeName.shortSwiftName,
105-
conformances: Constants.Operation.Body.conformances,
106-
members: bodyCases
107-
)
108-
)
109118

110-
let contentTypeUsage = bodyTypeName.asUsage.withOptional(hasNoContent)
111-
let contentProperty = PropertyBlueprint(
112-
comment: .doc("Received HTTP response body"),
113-
originalName: Constants.Operation.Body.variableName,
114-
typeUsage: contentTypeUsage,
115-
default: hasNoContent ? .nil : nil,
116-
associatedDeclarations: [
117-
contentEnumDecl
118-
],
119-
asSwiftSafeName: swiftSafeName
120-
)
119+
let contentTypeUsage = bodyTypeName.asUsage.withOptional(hasNoContent)
120+
contentProperty = PropertyBlueprint(
121+
comment: .doc("Received HTTP response body"),
122+
originalName: Constants.Operation.Body.variableName,
123+
typeUsage: contentTypeUsage,
124+
default: hasNoContent ? .nil : nil,
125+
associatedDeclarations: [
126+
contentEnumDecl
127+
],
128+
asSwiftSafeName: swiftSafeName
129+
)
130+
} else {
131+
contentProperty = nil
132+
}
121133

122134
let responseStructDecl = translateStructBlueprint(
123135
.init(
@@ -129,6 +141,7 @@ extension TypesFileTranslator {
129141
headersProperty,
130142
contentProperty,
131143
]
144+
.compactMap { $0 }
132145
)
133146
)
134147

Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponseOutcome.swift

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -126,27 +126,32 @@ extension ClientFileTranslator {
126126
from: typedResponse.response,
127127
inParent: headersTypeName
128128
)
129-
let headerInitArgs: [FunctionArgumentDescription] = try headers.map { header in
130-
try translateResponseHeaderInClient(
131-
header,
132-
responseVariableName: "response"
129+
let headersVarExpr: Expression?
130+
if !headers.isEmpty {
131+
let headerInitArgs: [FunctionArgumentDescription] = try headers.map { header in
132+
try translateResponseHeaderInClient(
133+
header,
134+
responseVariableName: "response"
135+
)
136+
}
137+
let headersInitExpr: Expression = .dot("init").call(headerInitArgs)
138+
let headersVarDecl: Declaration = .variable(
139+
kind: .let,
140+
left: "headers",
141+
type: headersTypeName.fullyQualifiedSwiftName,
142+
right: headersInitExpr
133143
)
144+
codeBlocks.append(.declaration(headersVarDecl))
145+
headersVarExpr = .identifier("headers")
146+
} else {
147+
headersVarExpr = nil
134148
}
135-
let headersInitExpr: Expression = .dot("init").call(headerInitArgs)
136-
let headersVarDecl: Declaration = .variable(
137-
kind: .let,
138-
left: "headers",
139-
type: headersTypeName.fullyQualifiedSwiftName,
140-
right: headersInitExpr
141-
)
142-
codeBlocks.append(.declaration(headersVarDecl))
143-
let headersVarExpr: Expression = .identifier("headers")
144149

145150
let typedContents = try supportedTypedContents(
146151
typedResponse.response.content,
147152
inParent: bodyTypeName
148153
)
149-
let bodyVarExpr: Expression
154+
let bodyVarExpr: Expression?
150155
if !typedContents.isEmpty {
151156

152157
let contentTypeDecl: Declaration = .variable(
@@ -283,20 +288,27 @@ extension ClientFileTranslator {
283288

284289
bodyVarExpr = .identifier("body")
285290
} else {
286-
bodyVarExpr = .literal(.nil)
291+
bodyVarExpr = nil
287292
}
288293

289294
let initExpr: Expression = .dot("init")
290-
.call([
291-
.init(
292-
label: Constants.Operation.Output.Payload.Headers.variableName,
293-
expression: headersVarExpr
294-
),
295-
.init(
296-
label: Constants.Operation.Body.variableName,
297-
expression: bodyVarExpr
298-
),
299-
])
295+
.call(
296+
[
297+
headersVarExpr.map { headersVarExpr in
298+
.init(
299+
label: Constants.Operation.Output.Payload.Headers.variableName,
300+
expression: headersVarExpr
301+
)
302+
},
303+
bodyVarExpr.map { bodyVarExpr in
304+
.init(
305+
label: Constants.Operation.Body.variableName,
306+
expression: bodyVarExpr
307+
)
308+
},
309+
]
310+
.compactMap { $0 }
311+
)
300312

301313
let optionalStatusCode: [FunctionArgumentDescription]
302314
if responseKind.wantsStatusCode {

0 commit comments

Comments
 (0)