126 lines
3.1 KiB
Swift
126 lines
3.1 KiB
Swift
//
|
|
// Copyright 2019 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Foundation
|
|
public import GRDB
|
|
|
|
public protocol SDSModel: TSYapDatabaseObject, SDSIdentifiableModel {
|
|
var sdsTableName: String { get }
|
|
|
|
func asRecord() -> SDSRecord
|
|
|
|
var serializer: SDSSerializer { get }
|
|
|
|
func anyInsert(transaction: DBWriteTransaction)
|
|
|
|
static var table: SDSTableMetadata { get }
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
public extension SDSModel {
|
|
func sdsSave(saveMode: SDSSaveMode, transaction tx: DBWriteTransaction) {
|
|
guard shouldBeSaved else {
|
|
Logger.warn("Skipping save of: \(type(of: self))")
|
|
return
|
|
}
|
|
|
|
switch saveMode {
|
|
case .insert:
|
|
anyWillInsert(with: tx)
|
|
case .update:
|
|
anyWillUpdate(with: tx)
|
|
}
|
|
|
|
let record = asRecord()
|
|
record.sdsSave(saveMode: saveMode, transaction: tx)
|
|
|
|
switch saveMode {
|
|
case .insert:
|
|
anyDidInsert(with: tx)
|
|
case .update:
|
|
anyDidUpdate(with: tx)
|
|
}
|
|
}
|
|
|
|
func sdsRemove(transaction tx: DBWriteTransaction) {
|
|
guard shouldBeSaved else {
|
|
// Skipping remove.
|
|
return
|
|
}
|
|
|
|
anyWillRemove(with: tx)
|
|
|
|
// Don't use a record to delete the record;
|
|
// asRecord() is expensive.
|
|
let sql = """
|
|
DELETE
|
|
FROM \(sdsTableName)
|
|
WHERE uniqueId == ?
|
|
"""
|
|
failIfThrows {
|
|
try tx.database.execute(
|
|
sql: sql,
|
|
arguments: [uniqueId],
|
|
)
|
|
}
|
|
|
|
anyDidRemove(with: tx)
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
public extension TableRecord {
|
|
static func ows_fetchCount(_ db: Database) -> UInt {
|
|
do {
|
|
let result = try fetchCount(db)
|
|
guard result >= 0 else {
|
|
owsFailDebug("Invalid result: \(result)")
|
|
return 0
|
|
}
|
|
guard result <= UInt.max else {
|
|
owsFailDebug("Invalid result: \(result)")
|
|
return UInt.max
|
|
}
|
|
return UInt(result)
|
|
} catch {
|
|
owsFailDebug("Read failed: \(error)")
|
|
return 0
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Cursors
|
|
|
|
public protocol SDSCursor {
|
|
associatedtype Model: SDSModel
|
|
mutating func next() throws -> Model?
|
|
}
|
|
|
|
public struct SDSMappedCursor<Cursor: SDSCursor, Element> {
|
|
fileprivate var cursor: Cursor
|
|
fileprivate let transform: (Cursor.Model) throws -> Element?
|
|
|
|
public mutating func next() throws -> Element? {
|
|
while let next = try cursor.next() {
|
|
if let transformed = try transform(next) {
|
|
return transformed
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public extension SDSCursor {
|
|
func map<Element>(transform: @escaping (Model) throws -> Element) -> SDSMappedCursor<Self, Element> {
|
|
return compactMap(transform: transform)
|
|
}
|
|
|
|
func compactMap<Element>(transform: @escaping (Model) throws -> Element?) -> SDSMappedCursor<Self, Element> {
|
|
return SDSMappedCursor(cursor: self, transform: transform)
|
|
}
|
|
}
|