Skip to content

Commit 53cd0b6

Browse files
committed
generate extensions for input
1 parent 21ae3d1 commit 53cd0b6

2 files changed

Lines changed: 100 additions & 49 deletions

File tree

Sources/Compiler/Gen/SwiftGenerator.swift

Lines changed: 99 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,25 @@ import SwiftSyntaxBuilder
1111
public struct SwiftGenerator: Language {
1212
public typealias Table = DeclSyntax
1313
public typealias File = SourceFileSyntax
14-
public typealias Query = [DeclSyntax]
1514
public typealias Migration = StringLiteralExprSyntax
1615

16+
public struct Query {
17+
public let statement: Statement
18+
public let input: GeneratedStruct?
19+
public let output: GeneratedStruct?
20+
public let query: DeclSyntax
21+
22+
public var decls: [DeclSyntax] {
23+
[input?.decl, output?.decl, query].compactMap(\.self)
24+
}
25+
}
26+
27+
public struct GeneratedStruct {
28+
let decl: DeclSyntax
29+
let name: String
30+
let fields: [(name: String, type: String)]
31+
}
32+
1733
public static func migration(
1834
source: String
1935
) throws -> StringLiteralExprSyntax {
@@ -26,20 +42,19 @@ public struct SwiftGenerator: Language {
2642
) throws -> DeclSyntax {
2743
return try structDecl(
2844
name: name,
29-
columns: columns,
45+
fields: columns.map{ ($0.key.description, $0.value) },
3046
rowDecodable: true
31-
)
47+
).decl
3248
}
3349

3450
public static func query(
3551
statement: Statement,
3652
name: Substring
37-
) throws -> [DeclSyntax] {
53+
) throws -> Query {
3854
let parameters = statement.parameters
39-
var declarations: [DeclSyntax] = []
4055

41-
let inputTypeName = inputType(statement: statement, name: name, declarations: &declarations)
42-
let outputTypeName = try outputType(statement: statement, name: name, declarations: &declarations)
56+
let (inputTypeName, inputDecl) = try inputType(statement: statement, name: name)
57+
let (outputTypeName, outputDecl) = try outputType(statement: statement, name: name)
4358

4459
let queryType: String = if statement.noOutput {
4560
"VoidQuery<\(inputTypeName)>"
@@ -101,64 +116,65 @@ public struct SwiftGenerator: Language {
101116
)
102117
}
103118

104-
declarations.append(DeclSyntax(query))
105-
106-
return declarations
119+
return Query(
120+
statement: statement,
121+
input: inputDecl,
122+
output: outputDecl,
123+
query: DeclSyntax(query)
124+
)
107125
}
108126

109127
private static func inputType(
110128
statement: Statement,
111-
name: Substring,
112-
declarations: inout [DeclSyntax]
113-
) -> String {
129+
name: Substring
130+
) throws -> (String, GeneratedStruct?) {
114131
guard let firstParam = statement.parameters.first else {
115-
return "()"
132+
return ("()", nil)
116133
}
117134

118135
if statement.parameters.count > 1 {
119136
let inputTypeName = "\(name.capitalizedFirst)Input"
120-
let inputType = DeclSyntax(StructDeclSyntax(name: "\(raw: inputTypeName)") {
121-
for input in statement.parameters {
122-
"let \(raw: input.name): \(raw: swiftType(for: input.type))"
123-
}
124-
})
125-
declarations.append(inputType)
126-
return inputTypeName
137+
138+
let inputType = try structDecl(
139+
name: inputTypeName,
140+
fields: statement.parameters.map { ($0.name, $0.type) },
141+
rowDecodable: false
142+
)
143+
144+
return (inputTypeName, inputType)
127145
} else {
128146
// Single input parameter, just use the single value as the parameter type
129-
return swiftType(for: firstParam.type)
147+
return (swiftType(for: firstParam.type), nil)
130148
}
131149
}
132150

133151
private static func outputType(
134152
statement: Statement,
135-
name: Substring,
136-
declarations: inout [DeclSyntax]
137-
) throws -> String {
153+
name: Substring
154+
) throws -> (String, GeneratedStruct?) {
138155
// Make sure there is at least one column else return void
139156
guard let first = statement.resultColumns
140-
.columns.values.first else { return "()" }
157+
.columns.values.first else { return ("()", nil) }
141158

142159
// Output can be mapped to a table struct
143160
if let table = statement.resultColumns.table {
144-
return table.capitalizedFirst
161+
return (table.capitalizedFirst, nil)
145162
}
146163

147164
// Only one column returned, just use it's type
148165
guard statement.resultColumns.columns.count > 1 else {
149-
return swiftType(for: first)
166+
return (swiftType(for: first), nil)
150167
}
151168

152169
let outputTypeName = "\(name.capitalizedFirst)Output"
153170

154171
let outputType = try structDecl(
155172
name: outputTypeName,
156-
columns: statement.resultColumns.columns,
173+
fields: statement.resultColumns.columns.map{ ($0.key.description, $0.value) },
157174
rowDecodable: true
158175
)
159176

160-
declarations.append(outputType)
161-
return outputTypeName
177+
return (outputTypeName, outputType)
162178
}
163179

164180
public static func file(
@@ -181,8 +197,34 @@ public struct SwiftGenerator: Language {
181197
}
182198

183199
for query in queries {
184-
for decl in query {
185-
decl
200+
if let input = query.input?.decl {
201+
input
202+
}
203+
204+
if let output = query.output?.decl {
205+
output
206+
}
207+
208+
query.query
209+
}
210+
}
211+
212+
for query in queries {
213+
if let input = query.input {
214+
try ExtensionDeclSyntax("extension Query where Input == DB.\(raw: input.name)") {
215+
let parameters = input.fields.map { parameter in
216+
"\(parameter.name): \(parameter.type)"
217+
}.joined(separator: ", ")
218+
219+
let args = input.fields.map { parameter in
220+
"\(parameter.name): \(parameter.name)"
221+
}.joined(separator: ", ")
222+
223+
"""
224+
func execute(\(raw: parameters)) async throws -> Output {
225+
try await execute(with: DB.\(raw: input.name)(\(raw: args)))
226+
}
227+
"""
186228
}
187229
}
188230
}
@@ -195,24 +237,30 @@ public struct SwiftGenerator: Language {
195237

196238
public static func structDecl<Name: StringProtocol>(
197239
name: Name,
198-
columns: Columns,
240+
fields unresolvedFields: [(name: String, type: Type)],
199241
rowDecodable: Bool
200-
) throws -> DeclSyntax {
242+
) throws -> GeneratedStruct {
201243
var declName = "\(name.capitalizedFirst): Hashable"
202244

203-
if columns["id"] != nil {
245+
var hasId = false
246+
var fields: [(name: String, type: String)] = []
247+
for field in unresolvedFields {
248+
if field.name == "id" {
249+
hasId = true
250+
}
251+
252+
fields.append((field.name.description, swiftType(for: field.type)))
253+
}
254+
255+
if hasId {
204256
declName.append(", Identifiable")
205257
}
206258

207259
if rowDecodable {
208260
declName.append(", RowDecodable")
209261
}
210262

211-
let fields = columns.map { (name, type) in
212-
(name: name, type: swiftType(for: type))
213-
}
214-
215-
return try DeclSyntax(StructDeclSyntax(name: "\(raw: declName)") {
263+
let decl = try DeclSyntax(StructDeclSyntax(name: "\(raw: declName)") {
216264
for (column, type) in fields {
217265
"let \(raw: column): \(raw: type)"
218266
}
@@ -221,18 +269,21 @@ public struct SwiftGenerator: Language {
221269
try InitializerDeclSyntax("init(row: borrowing Feather.Row) throws(FeatherError)") {
222270
"var columns = row.columnIterator()"
223271

224-
for (column, _) in columns {
225-
"self.\(raw: column) = try columns.next()"
272+
for (name, _) in fields {
273+
"self.\(raw: name) = try columns.next()"
226274
}
227275
}
228-
}
229-
230-
try InitializerDeclSyntax("init(\(raw: fields.map{ "\($0.name): \($0.type)" }.joined(separator: ", ")))") {
231-
for field in fields {
232-
"self.\(raw: field.name) = \(raw: field.name)"
276+
277+
// Only generate the memberwise init if needed
278+
try InitializerDeclSyntax("init(\(raw: fields.map{ "\($0.name): \($0.type)" }.joined(separator: ", ")))") {
279+
for field in fields {
280+
"self.\(raw: field.name) = \(raw: field.name)"
281+
}
233282
}
234283
}
235284
})
285+
286+
return GeneratedStruct(decl: decl, name: name.description, fields: fields)
236287
}
237288

238289
private static func swiftType(for type: Type) -> String {

Sources/FeatherMacros/SQLMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public struct DatabaseMacro: MemberMacro {
6767
}
6868
}
6969

70-
return queries.flatMap(\.self)
70+
return queries.map(\.decls).flatMap(\.self)
7171
}
7272

7373
private static func arrayStrings(

0 commit comments

Comments
 (0)