|
1 | 1 | import Foundation
|
2 | 2 |
|
3 |
| -/// A row of data returned from a database. Various database packages |
4 |
| -/// can use this as an abstraction around their internal row types. |
5 |
| -public protocol SQLRow { |
6 |
| - /// The `String` names of all columns that have values in this row. |
7 |
| - var columns: Set<String> { get } |
| 3 | +public struct SQLField: Equatable { |
| 4 | + public let column: String |
| 5 | + public let value: SQLValue |
| 6 | +} |
| 7 | + |
| 8 | +/// A row of data returned by an SQL query. |
| 9 | +public struct SQLRow { |
| 10 | + public let fields: [SQLField] |
| 11 | + public let lookupTable: [String: Int] |
8 | 12 |
|
9 |
| - /// Get the `SQLValue` of a column from this row. |
10 |
| - /// |
11 |
| - /// - Parameter column: The column to get the value for. |
12 |
| - /// - Throws: A `DatabaseError` if the column does not exist on |
13 |
| - /// this row. |
14 |
| - /// - Returns: The value at `column`. |
15 |
| - func get(_ column: String) throws -> SQLValue |
| 13 | + public var fieldDictionary: [String: SQLValue] { |
| 14 | + Dictionary(fields.map { ($0.column, $0.value) }, uniquingKeysWith: { current, _ in current }) |
| 15 | + } |
| 16 | + |
| 17 | + init(fields: [SQLField]) { |
| 18 | + self.fields = fields |
| 19 | + self.lookupTable = Dictionary(fields.enumerated().map { ($1.column, $0) }, uniquingKeysWith: { current, _ in current }) |
| 20 | + } |
| 21 | + |
| 22 | + public func contains(_ column: String) -> Bool { |
| 23 | + lookupTable[column] != nil |
| 24 | + } |
16 | 25 |
|
| 26 | + public subscript(_ index: Int) -> SQLValue { |
| 27 | + fields[index].value |
| 28 | + } |
| 29 | + |
| 30 | + public subscript(_ column: String) -> SQLValue? { |
| 31 | + guard let index = lookupTable[column] else { return nil } |
| 32 | + return fields[index].value |
| 33 | + } |
| 34 | + |
| 35 | + public func require(_ column: String) throws -> SQLValue { |
| 36 | + try self[column].unwrap(or: DatabaseError.missingColumn(column)) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +extension SQLRow: ExpressibleByDictionaryLiteral { |
| 41 | + public init(dictionaryLiteral elements: (String, SQLValueConvertible)...) { |
| 42 | + self.init(fields: elements.enumerated().map { SQLField(column: $1.0, value: $1.1.sqlValue) }) |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +extension SQLRow { |
17 | 47 | /// Decode a `Model` type `D` from this row.
|
18 | 48 | ///
|
19 | 49 | /// The default implementation of this function populates the
|
20 | 50 | /// properties of `D` with the value of the column named the
|
21 | 51 | /// same as the property.
|
22 | 52 | ///
|
23 | 53 | /// - Parameter type: The type to decode from this row.
|
24 |
| - func decode<D: Model>(_ type: D.Type) throws -> D |
25 |
| -} |
26 |
| - |
27 |
| -extension SQLRow { |
| 54 | + public func decode<M: Model>(_ type: M.Type) throws -> M { |
| 55 | + try M(from: SQLRowDecoder(row: self, keyMapping: M.keyMapping, jsonDecoder: M.jsonDecoder)) |
| 56 | + } |
| 57 | + |
28 | 58 | public func decode<D: Decodable>(
|
29 | 59 | _ type: D.Type,
|
30 | 60 | keyMapping: DatabaseKeyMapping = .useDefaultKeys,
|
31 | 61 | jsonDecoder: JSONDecoder = JSONDecoder()
|
32 | 62 | ) throws -> D {
|
33 | 63 | try D(from: SQLRowDecoder(row: self, keyMapping: keyMapping, jsonDecoder: jsonDecoder))
|
34 | 64 | }
|
35 |
| - |
36 |
| - public func decode<M: Model>(_ type: M.Type) throws -> M { |
37 |
| - try M(from: SQLRowDecoder(row: self, keyMapping: M.keyMapping, jsonDecoder: M.jsonDecoder)) |
38 |
| - } |
39 |
| - |
40 |
| - /// Subscript for convenience access. |
41 |
| - public subscript(column: String) -> SQLValue? { |
42 |
| - columns.contains(column) ? try? get(column) : nil |
43 |
| - } |
44 | 65 | }
|
0 commit comments