@@ -11,9 +11,25 @@ import SwiftSyntaxBuilder
1111public 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 {
0 commit comments