@@ -12,17 +12,13 @@ import SwiftSyntaxBuilder
1212public protocol Language {
1313 init ( options: GenerationOptions )
1414
15- func queryTypeName ( input : String , output : String ) -> String
15+ var boolName : String { get }
1616
17- func inputTypeName ( input: BuiltinOrGenerated ? ) -> String
17+ func queryTypeName ( input: String , output : String ) -> String
1818
19- func outputTypeName(
20- output: BuiltinOrGenerated ? ,
21- cardinality: Cardinality
22- ) -> String
19+ func typeName( for type: GenerationType ) -> String
2320
24- /// Returns the Language builtin for the given SQL type
25- func builtinType( for type: Type ) -> String
21+ func builtinType( named type: Substring ) -> String
2622
2723 /// A file source code containing all of the generated tables, queries and migrations.
2824 func file(
@@ -94,8 +90,9 @@ extension Language {
9490 }
9591 } . joined ( )
9692
97- let inputTypeName = inputTypeName ( input: input)
98- let outputTypeName = outputTypeName ( output: output, cardinality: statement. outputCardinality)
93+ let inputTypeName = typeName ( for: input)
94+ let outputTypeName = typeName ( for: output)
95+ var startIndex = 1
9996
10097 return GeneratedQuery (
10198 name: definition. name. description,
@@ -109,25 +106,47 @@ extension Language {
109106 outputCardinality: statement. outputCardinality,
110107 sourceSql: sql,
111108 isReadOnly: statement. isReadOnly,
112- usedTableNames: statement. usedTableNames. sorted ( )
109+ usedTableNames: statement. usedTableNames. sorted ( ) ,
110+ bindings: bindings ( for: input, index: & startIndex)
113111 )
114112 }
115113
114+ private func bindings(
115+ for input: GenerationType ,
116+ index: inout Int ,
117+ owner: String ? = nil
118+ ) -> [ GeneratedQuery . Binding ] {
119+ var result : [ GeneratedQuery . Binding ] = [ ]
120+
121+ switch input {
122+ case . void:
123+ break
124+ case . builtin, . optional:
125+ result. append ( . value( index: index, name: " input " , owner: owner) )
126+ index += 1
127+ case . model( let model) :
128+ for field in model. fields. values {
129+ result. append ( contentsOf: bindings ( for: field. type, index: & index, owner: field. name) )
130+ }
131+ case . array( let values) :
132+ result. append ( . arrayStart( name: " input " , elementName: " element " ) )
133+ result. append ( contentsOf: bindings ( for: values, index: & index, owner: " element " ) )
134+ result. append ( . arrayEnd)
135+ case . encoded( _, _, let coder) :
136+ result. append ( . value( index: index, name: " input " , owner: owner, coder: coder) )
137+ index += 1
138+ }
139+
140+ return result
141+ }
142+
116143 private func model( for table: Table ) -> GeneratedModel {
117144 GeneratedModel (
118145 name: table. name. name. capitalizedFirst,
119146 fields: table. columns. reduce ( into: [ : ] ) { fields, column in
120147 let name = column. key. description
121148 let type = column. value. type
122- fields [ name] = GeneratedField (
123- name: name,
124- type: . builtin(
125- builtinType ( for: type) ,
126- isArray: false ,
127- encodedAs: builtinForAliasedType ( for: type)
128- ) ,
129- isArray: type. isRow
130- )
149+ fields [ name] = field ( named: name, with: type)
131150 } ,
132151 isTable: true ,
133152 nonOptionalIndices: table. columns. enumerated ( )
@@ -138,76 +157,89 @@ extension Language {
138157 )
139158 }
140159
141- /// If the column type was aliased then this will return the `builtin`
142- /// type for the root type of the alias.
143- private func builtinForAliasedType( for type: Type ) -> String ? {
144- guard case let . alias( root, _) = type else { return nil }
145- return builtinType ( for: root)
160+ private func generationType( for type: Type ) -> GenerationType {
161+ switch type {
162+ case let . nominal( name) :
163+ return . builtin( builtinType ( named: name) )
164+ case let . alias( root, alias) :
165+ let alias = switch alias {
166+ case . explicit( let e) : e. description
167+ case . hint( let hint) :
168+ switch hint {
169+ case . bool: boolName
170+ }
171+ }
172+
173+ return . encoded( generationType ( for: root) , alias: alias, coder: " \( alias) DatabaseValueCoder " )
174+ case let . optional( type) :
175+ return . optional( generationType ( for: type) )
176+ case let . row( . unknown( type) ) :
177+ return . array( generationType ( for: type) )
178+ case . error, . fn, . row( . fixed) , . var:
179+ fatalError ( " Upstream error not caught " )
180+ }
146181 }
147182
183+ private func field( named name: String , with type: Type ) -> GeneratedField {
184+ let type = generationType ( for: type)
185+ return GeneratedField ( name: name, type: type, typeName: typeName ( for: type) )
186+ }
187+
148188 private func inputTypeIfNeeded(
149189 statement: Statement ,
150190 definition: Definition
151- ) -> BuiltinOrGenerated ? {
152- guard let firstParameter = statement. parameters. first else { return nil }
191+ ) -> GenerationType {
192+ guard let firstParameter = statement. parameters. first else { return . void }
153193
154194 guard statement. parameters. count > 1 else {
155- return . builtin(
156- builtinType ( for: firstParameter. type) ,
157- isArray: firstParameter. type. isRow,
158- encodedAs: builtinForAliasedType ( for: firstParameter. type)
159- )
195+ return generationType ( for: firstParameter. type)
160196 }
161197
162198 let inputTypeName = definition. input? . description ?? " \( definition. name. capitalizedFirst) Input "
163199
164200 let model = GeneratedModel (
165201 name: inputTypeName,
166202 fields: statement. parameters. reduce ( into: [ : ] ) { fields, parameter in
167- fields [ parameter. name] = GeneratedField (
168- name: parameter. name,
169- type: . builtin(
170- builtinType ( for: parameter. type) ,
171- isArray: false ,
172- encodedAs: builtinForAliasedType ( for: parameter. type)
173- ) ,
174- isArray: parameter. type. isRow
175- )
203+ fields [ parameter. name] = field ( named: parameter. name, with: parameter. type)
176204 } ,
177205 isTable: false ,
178206 nonOptionalIndices: [ ]
179207 )
180208
181- return . model( model, isOptional : false )
209+ return . model( model)
182210 }
183211
184212 private func outputTypeIfNeeded(
185213 statement: Statement ,
186214 definition: Definition ,
187215 tables: [ Substring : GeneratedModel ]
188- ) -> BuiltinOrGenerated ? {
189- guard let firstResultColumns = statement. resultColumns. chunks. first else { return nil }
216+ ) -> GenerationType {
217+ guard let firstResultColumns = statement. resultColumns. chunks. first else { return . void }
218+
219+ // Will return an array if it returns many or optional if its a single result
220+ let singleOrMany : ( GenerationType ) -> GenerationType = {
221+ switch statement. outputCardinality {
222+ case . single: . optional( $0)
223+ case . many: . array( $0)
224+ }
225+ }
190226
191227 // Output can be mapped to a table struct
192228 if statement. resultColumns. chunks. count == 1 ,
193229 let tableName = firstResultColumns. table,
194230 let table = tables [ tableName]
195231 {
196- return . model( table, isOptional : false )
232+ return singleOrMany ( . model( table) )
197233 }
198234
199235 // Make sure there is at least one column else return void
200236 guard let firstColumn = firstResultColumns. columns. values. first? . type else {
201- return nil
237+ return . void
202238 }
203239
204240 // Only one column returned, just use it's type
205241 guard statement. resultColumns. count > 1 else {
206- return . builtin(
207- builtinType ( for: firstColumn) ,
208- isArray: firstColumn. isRow,
209- encodedAs: builtinForAliasedType ( for: firstColumn)
210- )
242+ return singleOrMany ( generationType ( for: firstColumn) )
211243 }
212244
213245 let outputTypeName = definition. output? . description ?? " \( definition. name. capitalizedFirst) Output "
@@ -217,23 +249,20 @@ extension Language {
217249 fields: statement. resultColumns. chunks. reduce ( into: [ : ] ) { fields, chunk in
218250 if let tableName = chunk. table, let table = tables [ tableName] {
219251 let name = tableName. description
252+ let type : GenerationType = chunk. isTableOptional ? . optional( . model( table) ) : . model( table)
220253 fields [ name] = GeneratedField (
221254 name: name,
222- type: . model ( table , isOptional : chunk . isTableOptional ) ,
223- isArray : false
255+ type: type ,
256+ typeName : typeName ( for : type )
224257 )
225258 } else {
226259 for column in chunk. columns {
227260 let name = column. key. description
228- let type = column. value. type
261+ let type = generationType ( for : column. value. type)
229262 fields [ name] = GeneratedField (
230263 name: name,
231- type: . builtin(
232- builtinType ( for: type) ,
233- isArray: false ,
234- encodedAs: builtinForAliasedType ( for: type)
235- ) ,
236- isArray: type. isRow
264+ type: type,
265+ typeName: typeName ( for: type)
237266 )
238267 }
239268 }
@@ -242,7 +271,7 @@ extension Language {
242271 nonOptionalIndices: [ ]
243272 )
244273
245- return . model( model, isOptional : false )
274+ return singleOrMany ( . model( model) )
246275 }
247276}
248277
@@ -259,62 +288,69 @@ public struct GenerationOptions: Sendable {
259288 }
260289}
261290
262- public struct GeneratedModel {
291+ public struct GeneratedModel : Equatable {
263292 let name : String
264293 let fields : OrderedDictionary < String , GeneratedField >
265294 /// Whether or not this was generated for a table
266295 let isTable : Bool
267296 let nonOptionalIndices : [ Int ]
268297}
269298
270- public struct GeneratedField {
299+ public struct GeneratedField : Equatable {
271300 /// The column name
272301 let name : String
273302 /// The type of the field.
274303 /// If it is a `model` that means the user selected
275304 /// all columns from a table `foo.*`
276- let type : BuiltinOrGenerated
277- /// Whether or not it is an array. Some fields can take a list
278- /// as an input for a query like `foo IN :bar`
279- let isArray : Bool
280-
281- /// The underlying storage type if it is aliased
282- var encodedAsType : String ? {
283- guard case let . builtin( _, _, encodedAs) = type else { return nil }
284- return encodedAs
285- }
305+ let type : GenerationType
306+ /// The types name to use in the codegen.
307+ /// The name is accessed many times so we can just calculate
308+ /// it once and reuse it.
309+ let typeName : String
286310}
287311
288312public struct GeneratedQuery {
289313 let name : String
290314 let variableName : String
291315 let typeName : String
292316 let typealiasName : String
293- let input : BuiltinOrGenerated ?
317+ let input : GenerationType
294318 let inputName : String
295- let output : BuiltinOrGenerated ?
319+ let output : GenerationType
296320 let outputName : String
297321 let outputCardinality : Cardinality
298322 let sourceSql : String
299323 let isReadOnly : Bool
300324 let usedTableNames : [ Substring ]
325+ let bindings : [ Binding ]
326+
327+ public enum Binding {
328+ case value( index: Int , name: String , owner: String ? = nil , coder: String ? = nil )
329+ case arrayStart( name: String , elementName: String )
330+ case arrayEnd
331+ }
301332}
302333
303- public enum BuiltinOrGenerated : CustomStringConvertible {
304- /// Types can be aliased. So `TEXT AS UUID`. `encodedAs`
305- /// would be the `TEXT`. It will allow us to tell the
306- /// `bind` functions to actually encode to the underlying
307- /// type rather than just having `UUID` always go to `TEXT`
308- /// when some users may want a `BLOB`.
309- case builtin( String , isArray: Bool , encodedAs: String ? )
310- case model( GeneratedModel , isOptional: Bool )
334+ public enum GenerationType : Equatable {
335+ case void
336+ case builtin( String )
337+ case model( GeneratedModel )
338+ indirect case optional( Self )
339+ indirect case array( Self )
340+ indirect case encoded( Self , alias: String , coder: String )
311341
312- public var description : String {
342+ var model : GeneratedModel ? {
313343 switch self {
314- case let . builtin( builtin, isArray, _) :
315- isArray ? " [ \( builtin) ] " : builtin
316- case let . model( model, isOptional) :
317- isOptional ? " \( model. name) ? " : model. name
344+ case . void, . builtin: nil
345+ case . model( let model) : model
346+ case . optional( let optional) : optional. model
347+ case . array( let array) : array. model
348+ case . encoded( let encoded, _, _) : encoded. model
318349 }
319350 }
320351}
352+
353+ public struct RequiredCoder : Hashable {
354+ public let sourceType : String
355+ public let storage : String
356+ }
0 commit comments