Skip to content

Commit 217b069

Browse files
committed
WIP - more adapter updates
1 parent a743119 commit 217b069

13 files changed

Lines changed: 503 additions & 217 deletions

Sources/Compiler/Gen/Language.swift

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,11 @@ extension Language {
5151
schema: Schema
5252
) throws -> String {
5353
let values = try assemble(queries: queries, schema: schema)
54-
55-
let builtinAdapters = builtinAdapterTypes
56-
.map(adapterName(from:))
57-
58-
// Get a list of all adapters used. Right now we only have to look at the
59-
// tables since any output would inhereit the encoding of the source table.
60-
let adapters: Set<String> = values.tables.reduce(into: []) { adapters, table in
61-
for field in table.fields.values {
62-
guard let adapter = field.type.adapter else { continue }
63-
adapters.insert(adapter)
64-
}
65-
}
66-
6754
return try file(
6855
migrations: migrations,
6956
tables: values.tables,
7057
queries: values.queries,
71-
adapters: adapters.subtracting(builtinAdapters).sorted()
58+
adapters: values.adapters
7259
)
7360
}
7461

@@ -77,11 +64,25 @@ extension Language {
7764
schema: Schema
7865
) throws -> (
7966
tables: [GeneratedModel],
80-
queries: [(String?, [GeneratedQuery])]
67+
queries: [(String?, [GeneratedQuery])],
68+
adapters: [String]
8169
) {
8270
let tables = Dictionary(schema.tables.map { ($0.key.name, model(for: $0.value)) }, uniquingKeysWith: { $1 })
8371
let queries = queries.map { ($0.map { "\($0)Queries" }, $1.map { query(for: $0, tables: tables) }) }
84-
return (tables.values.sorted{ $0.name < $1.name }, queries)
72+
73+
let builtinAdapters = builtinAdapterTypes
74+
.map(adapterName(from:))
75+
76+
// Get a list of all adapters used. Right now we only have to look at the
77+
// tables since any output would inhereit the encoding of the source table.
78+
let adapters: Set<String> = tables.reduce(into: []) { adapters, table in
79+
for field in table.value.fields.values {
80+
guard let adapter = field.type.adapter else { continue }
81+
adapters.insert(adapter)
82+
}
83+
}
84+
85+
return (tables.values.sorted{ $0.name < $1.name }, queries, adapters.subtracting(builtinAdapters).sorted())
8586
}
8687

8788
private func query(
@@ -385,6 +386,21 @@ public struct GeneratedModel: Equatable {
385386
/// Whether or not this was generated for a table
386387
let isTable: Bool
387388
let nonOptionalIndices: [Int]
389+
/// Whether this model can be decoded with the adapters or not
390+
let requiresAdapters: Bool
391+
392+
init(
393+
name: String,
394+
fields: OrderedDictionary<String, GeneratedField>,
395+
isTable: Bool,
396+
nonOptionalIndices: [Int]
397+
) {
398+
self.name = name
399+
self.fields = fields
400+
self.isTable = isTable
401+
self.nonOptionalIndices = nonOptionalIndices
402+
self.requiresAdapters = fields.contains{ $0.value.type.requiresAdapters }
403+
}
388404
}
389405

390406
public struct GeneratedField: Equatable {
@@ -398,6 +414,11 @@ public struct GeneratedField: Equatable {
398414
/// The name is accessed many times so we can just calculate
399415
/// it once and reuse it.
400416
let typeName: String
417+
418+
/// Whether this type can be decoded with an adapter or not
419+
var requiresAdapter: Bool {
420+
return type.adapter != nil
421+
}
401422
}
402423

403424
public struct GeneratedQuery {
@@ -454,4 +475,14 @@ public enum GenerationType: Equatable {
454475
case .encoded(_, _, let adapter): adapter
455476
}
456477
}
478+
479+
var requiresAdapters: Bool {
480+
switch self {
481+
case .void, .builtin: false
482+
case .optional(let optional): optional.requiresAdapters
483+
case .array(let array): array.requiresAdapters
484+
case .encoded: true
485+
case .model(let model): model.requiresAdapters
486+
}
487+
}
457488
}

Sources/Compiler/Gen/SwiftLanguage.swift

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ public struct SwiftLanguage: Language {
129129
databaseName: String,
130130
tables: [GeneratedModel],
131131
queries: [GeneratedQuery],
132-
addConnection: Bool
132+
addConnection: Bool,
133+
adapters: [String]
133134
) -> [String] {
134135
var decls: [String] = []
135136

@@ -142,6 +143,8 @@ public struct SwiftLanguage: Language {
142143

143144
writer.write("let connection: any Otter.Connection")
144145
take()
146+
self.adapters(adapters: adapters)
147+
take()
145148

146149
for table in tables {
147150
declaration(for: table, isOutput: true)
@@ -173,20 +176,7 @@ public struct SwiftLanguage: Language {
173176

174177
writer.braces {
175178
writer.write(line: "let connection: any Otter.Connection")
176-
177-
writer.write(line: "let adapters: Adapters")
178-
// Don't require initialization if there are no custom adapters
179-
if adapters.isEmpty {
180-
writer.write(" = Adapters()")
181-
}
182-
writer.newline()
183-
184-
writer.write(line: "struct Adapters ")
185-
writer.braces {
186-
for adapter in adapters {
187-
writer.write(line: "let ", adapter, ": DatabaseValueAdapter")
188-
}
189-
}
179+
self.adapters(adapters: adapters)
190180

191181
writer.newline()
192182

@@ -224,6 +214,22 @@ public struct SwiftLanguage: Language {
224214
}
225215
}
226216

217+
private func adapters(adapters: [String]) {
218+
writer.write(line: "let adapters: Adapters")
219+
writer.newline()
220+
221+
if adapters.isEmpty {
222+
writer.write(line: "typealias Adapters = DefaultAdapters")
223+
} else {
224+
writer.write(line: "struct Adapters: Otter.Adapters ")
225+
writer.braces {
226+
for adapter in adapters {
227+
writer.write(line: "let ", adapter, ": any DatabaseValueAdapter")
228+
}
229+
}
230+
}
231+
}
232+
227233
private func queries(name: String, queries: [GeneratedQuery]) {
228234
writer.write(line: "struct ", name, " {")
229235
writer.indent()
@@ -348,10 +354,16 @@ public struct SwiftLanguage: Language {
348354
} else {
349355
switch query.outputCardinality {
350356
case .single:
351-
writer.write(line: "return try statement.fetchOne()")
357+
writer.write(line: "return try statement.fetchOne(")
352358
case .many:
353-
writer.write(line: "return try statement.fetchAll()")
359+
writer.write(line: "return try statement.fetchAll(")
360+
}
361+
362+
if query.output.requiresAdapters {
363+
writer.write("adapters: adapters")
354364
}
365+
366+
writer.write(")")
355367
}
356368

357369
writer.unindent()
@@ -407,7 +419,11 @@ public struct SwiftLanguage: Language {
407419
}
408420

409421
if isOutput {
410-
writer.write(", RowDecodable")
422+
if model.requiresAdapters {
423+
writer.write(", RowDecodableWithAdapters")
424+
} else {
425+
writer.write(", RowDecodable")
426+
}
411427
}
412428

413429
writer.write(" {")
@@ -491,6 +507,12 @@ public struct SwiftLanguage: Language {
491507
writer.indent()
492508
writer.write(line: "row: borrowing Otter.Row,")
493509
writer.write(line: "startingAt start: Int32")
510+
511+
if model.requiresAdapters {
512+
writer.write(",")
513+
writer.write(line: "adapters: ", options.databaseName, ".Adapters")
514+
}
515+
494516
writer.unindent()
495517
writer.write(line: ") throws(Otter.OtterError) {")
496518

@@ -506,10 +528,18 @@ public struct SwiftLanguage: Language {
506528
writer.write("row.value(at: start + ", index.description, ")")
507529
index += 1
508530
case let .model(model):
509-
writer.write("row.embedded(at: start + ", index.description, ")")
531+
writer.write("row.embedded(at: start + ", index.description)
532+
if model.requiresAdapters {
533+
writer.write(", adapters: adapters")
534+
}
535+
writer.write(")")
510536
index += model.fields.count
511537
case let .optional(.model(model)):
512-
writer.write("row.optionallyEmbedded(at: start + ", index.description, ")")
538+
writer.write("row.optionallyEmbedded(at: start + ", index.description)
539+
if model.requiresAdapters {
540+
writer.write(", adapters: adapters")
541+
}
542+
writer.write(")")
513543
index += model.fields.count
514544
case let .encoded(storage, _, adapter):
515545
writer.write("row.value(at: start + ", index.description, ", using: adapters.", adapter, ", storage: ", typeName(for: storage), ".self)")

Sources/Otter/Cursor.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77

88
import SQLite3
99

10-
public struct Cursor<Element: RowDecodable>: ~Copyable {
10+
public struct Cursor<Element>: ~Copyable {
1111
private let statement: Statement
1212

1313
public init(of statement: consuming Statement) {
1414
self.statement = statement
1515
}
16+
}
1617

18+
extension Cursor where Element: RowDecodable {
1719
public mutating func next() throws(OtterError) -> Element? {
1820
switch try statement.step() {
1921
case .row:
@@ -25,6 +27,18 @@ public struct Cursor<Element: RowDecodable>: ~Copyable {
2527
}
2628
}
2729

30+
extension Cursor where Element: RowDecodableWithAdapters {
31+
public mutating func next(adapters: Element.Adapters) throws(OtterError) -> Element? {
32+
switch try statement.step() {
33+
case .row:
34+
let row = Row(sqliteStatement: statement.raw)
35+
return try Element(row: row, startingAt: 0, adapters: adapters)
36+
case .done:
37+
return nil
38+
}
39+
}
40+
}
41+
2842
public struct Row: ~Copyable {
2943
@usableFromInline let sqliteStatement: OpaquePointer
3044

@@ -39,7 +53,7 @@ public struct Row: ~Copyable {
3953
/// Decodes the column at the index as the `Storage.Value` type
4054
@inlinable public func value<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
4155
at column: Int32,
42-
using adapter: Coder.Type,
56+
using adapter: Coder,
4357
storage: Storage.Type
4458
) throws(OtterError) -> Coder.Value {
4559
let storage = try Storage(from: sqliteStatement, at: column)
@@ -50,7 +64,7 @@ public struct Row: ~Copyable {
5064
/// if it has a value.
5165
@inlinable public func optionalValue<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
5266
at column: Int32,
53-
using adapter: Coder.Type,
67+
using adapter: Coder,
5468
storage: Storage.Type
5569
) throws(OtterError) -> Coder.Value? {
5670
guard let storage = try Storage?(from: sqliteStatement, at: column) else { return nil}

Sources/Otter/Database.swift

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import Foundation
99

1010
/// The base protocol every generated database conforms too.
1111
public protocol Database: ConnectionWrapper {
12+
associatedtype Adapters
13+
1214
/// The connection to use
13-
init(connection: any Connection)
15+
init(connection: any Connection, adapters: Adapters)
1416
/// An ordered list of migrations to be run.
1517
static var migrations: [String] { get }
1618
}
@@ -19,22 +21,22 @@ public extension Database {
1921
/// Opens a connection pool to the database at the given URL.
2022
///
2123
/// - Parameter url: The url of the database file
22-
init(url: URL) throws {
23-
self = try Self(path: url.path)
24+
init(url: URL, adapters: Adapters) throws {
25+
self = try Self(path: url.path, adapters: adapters)
2426
}
2527

2628
/// Opens a connection pool to the database at the given path.
2729
///
2830
/// - Parameter path: The path of the database file
29-
init(path: String) throws {
30-
self = try Self(config: DatabaseConfig(path: path))
31+
init(path: String, adapters: Adapters) throws {
32+
self = try Self(config: DatabaseConfig(path: path), adapters: adapters)
3133
}
3234

3335
/// Opens a connection pool to the database
3436
///
3537
/// - Parameter config: The configuration specifying any info
3638
/// needed to open the database.
37-
init(config: DatabaseConfig) throws {
39+
init(config: DatabaseConfig, adapters: Adapters) throws {
3840
let connection: any Connection = if let path = config.path {
3941
try ConnectionPool(
4042
path: path,
@@ -49,11 +51,49 @@ public extension Database {
4951
)
5052
}
5153

52-
self = Self(connection: connection)
54+
self = Self(connection: connection, adapters: adapters)
5355
}
5456

57+
/// Creates an in memory database.
58+
static func inMemory(adapters: Adapters) throws -> Self {
59+
return try Self(config: DatabaseConfig(path: nil), adapters: adapters)
60+
}
61+
}
62+
63+
64+
extension Database where Adapters == DefaultAdapters {
5565
/// Creates an in memory database.
5666
static func inMemory() throws -> Self {
57-
return try Self(config: DatabaseConfig(path: nil))
67+
return try Self(config: DatabaseConfig(path: nil), adapters: DefaultAdapters())
5868
}
5969
}
70+
71+
/// A marker protocol for the adapters for a database.
72+
/// Need a protocol so we can do extensions on the Database.Adapters
73+
public protocol Adapters: Sendable {}
74+
75+
/// The default type for adapters when the database is generated
76+
/// if there are no adapters
77+
public struct DefaultAdapters: Adapters {
78+
public init() {}
79+
}
80+
81+
extension Adapters {
82+
public var bool: BoolDatabaseValueAdapter { BoolDatabaseValueAdapter() }
83+
public var int8: Int8DatabaseValueAdapter { Int8DatabaseValueAdapter() }
84+
public var int16: Int16DatabaseValueAdapter { Int16DatabaseValueAdapter() }
85+
public var int32: Int32DatabaseValueAdapter { Int32DatabaseValueAdapter() }
86+
public var int64: Int64DatabaseValueAdapter { Int64DatabaseValueAdapter() }
87+
public var uint8: UInt8DatabaseValueAdapter { UInt8DatabaseValueAdapter() }
88+
public var uint16: UInt16DatabaseValueAdapter { UInt16DatabaseValueAdapter() }
89+
public var uint32: UInt32DatabaseValueAdapter { UInt32DatabaseValueAdapter() }
90+
public var uint64: UInt64DatabaseValueAdapter { UInt64DatabaseValueAdapter() }
91+
public var uint: UIntDatabaseValueAdapter { UIntDatabaseValueAdapter() }
92+
public var float: FloatDatabaseValueAdapter { FloatDatabaseValueAdapter() }
93+
@available(macOS 11.0, *)
94+
public var float16: Float16DatabaseValueAdapter { Float16DatabaseValueAdapter() }
95+
public var uuid: UUIDDatabaseValueAdapter { UUIDDatabaseValueAdapter() }
96+
public var decimal: DecimalDatabaseValueAdapter { DecimalDatabaseValueAdapter() }
97+
public var date: DateDatabaseValueAdapter { DateDatabaseValueAdapter() }
98+
public var url: URLDatabaseValueAdapter { URLDatabaseValueAdapter() }
99+
}

0 commit comments

Comments
 (0)