Path: blob/main/Signal/src/ViewControllers/MediaGallery/JournalingOrderedDictionary.swift
1 views
//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public import SignalServiceKit
public enum JournalingOrderedDictionaryChange<ChangeType: Equatable>: Equatable, CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .prepend:
return "prepend"
case .append:
return "append"
case .modify(index: let index, changes: let changes):
return "modify index=\(index) changes=\(changes.map { String(reflecting: $0) })"
case .remove(index: let i):
return "remove index=\(i)"
case .removeAll:
return "removeAll"
}
}
/// Add one key to the beginning
case prepend
/// Add one key to the end.
case append
/// Modify the value of a key
case modify(index: Int, changes: [ChangeType])
/// Remove by index
case remove(index: Int)
/// Delete all keys
case removeAll
}
/// Like OrderedDictionary but it creates a journal of mutations.
/// This is designed for the benefit of MediaGallerySections and is not quite as capable as OrderedDictionary.
/// `ChangeType` describes a modification to an instance of `ValueType`, which is opaque to this object.
public struct JournalingOrderedDictionary<KeyType: Hashable, ValueType, ChangeType: Equatable> {
public private(set) var orderedDictionary = OrderedDictionary<KeyType, ValueType>()
public private(set) var journal = [Change]()
public typealias Change = JournalingOrderedDictionaryChange<ChangeType>
public var orderedKeys: [KeyType] { orderedDictionary.orderedKeys }
public var isEmpty: Bool { orderedDictionary.isEmpty }
public mutating func prepend(key: KeyType, value: ValueType) {
journal.append(.prepend)
orderedDictionary.prepend(key: key, value: value)
}
public mutating func append(key: KeyType, value: ValueType) {
journal.append(.append)
orderedDictionary.append(key: key, value: value)
}
@discardableResult
public mutating func replaceValue(at i: Int, value: ValueType, changes: [ChangeType]) -> ValueType {
if !changes.isEmpty {
journal.append(.modify(index: i, changes: changes))
}
let key = orderedDictionary[i].key
return orderedDictionary.replace(key: key, value: value)
}
public subscript(key: KeyType) -> ValueType? {
return orderedDictionary[key]
}
public mutating func remove(at index: Int) {
journal.append(.remove(index: index))
orderedDictionary.remove(at: index)
}
public mutating func takeJournal() -> [Change] {
defer {
journal = []
}
return journal
}
public mutating func removeAll() {
journal = [.removeAll]
orderedDictionary.removeAll()
}
}
extension JournalingOrderedDictionary: RandomAccessCollection {
public var startIndex: Int { orderedDictionary.startIndex }
public var endIndex: Int { orderedDictionary.endIndex }
public func entry(atPosition position: Int) -> (key: KeyType, value: ValueType) {
return orderedDictionary[position]
}
public subscript(position: Int) -> (key: KeyType, value: ValueType) {
return orderedDictionary[position]
}
}