This commit is contained in:
zeus
2025-01-28 12:28:03 +08:00
parent 738c373a77
commit ec96756800
2534 changed files with 486292 additions and 0 deletions
@@ -0,0 +1,98 @@
extension ValueObservation {
/// Notifies only values that dont match the previously observed value, as
/// evaluated by a provided closure.
///
/// - parameter predicate: A closure to evaluate whether two values are
/// equivalent, for purposes of filtering. Return true from this closure
/// to indicate that the second element is a duplicate of the first.
public func removeDuplicates(by predicate: @escaping (Reducer.Value, Reducer.Value) -> Bool)
-> ValueObservation<ValueReducers.RemoveDuplicates<Reducer>>
{
mapReducer { ValueReducers.RemoveDuplicates($0, predicate: predicate) }
}
}
extension ValueObservation where Reducer.Value: Equatable {
/// Notifies only values that dont match the previously observed value.
///
/// For example:
///
/// ```swift
/// // An observation of distinct Player?
/// let observation = ValueObservation
/// .tracking { db in try Player.fetchOne(db, id: 42) }
/// .removeDuplicates()
/// ```
///
/// > Tip: When the observed value does not adopt `Equatable`, and it is
/// > impractical to provide a custom comparison function, you can observe
/// > distinct raw database values such as ``Row`` or ``DatabaseValue``,
/// > before converting them to the desired type. For example, the previous
/// > observation can be rewritten as below:
/// >
/// > ```swift
/// > // An observation of distinct `Player?`
/// > let request = Player.filter(id: 42)
/// > let observation = ValueObservation
/// > .tracking { db in try Row.fetchOne(db, request) }
/// > .removeDuplicates()
/// > .map { row in try row.map(Player.init(row:)) }
/// > ```
/// >
/// > This technique is also available for requests that
/// > involve associations:
/// >
/// > ```swift
/// > struct TeamInfo: Decodable, FetchableRecord {
/// > var team: Team
/// > var players: [Player]
/// > }
/// >
/// > // An observation of distinct `[TeamInfo]`
/// > let request = Team.including(all: Team.players)
/// > let observation = ValueObservation
/// > .tracking { db in try Row.fetchAll(db, request) }
/// > .removeDuplicates() // Row adopts Equatable
/// > .map { rows in try rows.map(TeamInfo.init(row:)) }
/// > ```
public func removeDuplicates()
-> ValueObservation<ValueReducers.RemoveDuplicates<Reducer>>
{
mapReducer { ValueReducers.RemoveDuplicates($0, predicate: ==) }
}
}
extension ValueReducers {
/// A `ValueReducer` that notifies only values that dont match the
/// previously observed value.
///
/// See ``ValueObservation/removeDuplicates()``.
public struct RemoveDuplicates<Base: _ValueReducer>: _ValueReducer {
private var base: Base
private var previousValue: Base.Value?
private var predicate: (Base.Value, Base.Value) -> Bool
init(_ base: Base, predicate: @escaping (Base.Value, Base.Value) -> Bool) {
self.base = base
self.predicate = predicate
}
public mutating func _value(_ fetched: Base.Fetched) throws -> Base.Value? {
guard let value = try base._value(fetched) else {
return nil
}
if let previousValue, predicate(previousValue, value) {
// Don't notify consecutive identical values
return nil
}
self.previousValue = value
return value
}
}
}
extension ValueReducers.RemoveDuplicates: ValueReducer where Base: ValueReducer {
public func _fetch(_ db: Database) throws -> Base.Fetched {
try base._fetch(db)
}
}