Skip to content

Instantly share code, notes, and snippets.

@michelf
Created June 24, 2019 23:14
Show Gist options
  • Save michelf/3684d49333d724deec20f9ebf288d4d6 to your computer and use it in GitHub Desktop.
Save michelf/3684d49333d724deec20f9ebf288d4d6 to your computer and use it in GitHub Desktop.
/// An type with a default form.
protocol DefaultFormRepresentable {
static var defaultForm: Self { get }
var isDefaultForm: Bool { get }
}
// MARK: Generic conformance based on other protocols
// generic implementation for array-style collections
extension DefaultFormRepresentable where Self: ExpressibleByArrayLiteral, Self: Collection {
static var defaultForm: Self { return [] }
var isDefaultForm: Bool { return isEmpty }
}
// generic implementation for dictionary-style collections
extension DefaultFormRepresentable where Self: ExpressibleByDictionaryLiteral, Self: Collection {
static var defaultForm: Self { return [:] }
var isDefaultForm: Bool { return isEmpty }
}
// generic implementation for integers
extension DefaultFormRepresentable where Self: ExpressibleByIntegerLiteral, Self: Equatable {
static var defaultForm: Self { return 0 }
var isDefaultForm: Bool { return self == 0 }
}
// MARK: Conformance for standard types
extension Optional: DefaultFormRepresentable {
static var defaultForm: Optional { return nil }
var isDefaultForm: Bool { return self == nil }
}
extension Int: DefaultFormRepresentable {}
extension Set: DefaultFormRepresentable {}
extension Array: DefaultFormRepresentable {}
extension ArraySlice: DefaultFormRepresentable {}
extension Dictionary: DefaultFormRepresentable {}
// MARK: Conformance for Foundation types
import Foundation
extension IndexSet: DefaultFormRepresentable {}
/// Protocol for a collection that maps keys to values. Used as the storage
/// for DefaultMapping.
protocol MappingCollection: Collection {
associatedtype Key
associatedtype Value
init()
subscript (key: Key) -> Value? { get set }
mutating func removeValue(forKey key: Key) -> Value?
mutating func removeAll(keepingCapacity: Bool)
}
extension Dictionary: MappingCollection {}
/// A sparce collection of elements having a default value. The non-default
/// elements are stored in a mapping collection.
struct DefaultMapping<Collection: MappingCollection> where Collection.Value: DefaultFormRepresentable {
typealias Key = Collection.Key
typealias Value = Collection.Value
/// Collection of non-default elements
fileprivate(set) var nonDefault: Collection
/// Initialization of a collection where all values are set to the default
init() {
self.nonDefault = Collection()
}
/// Initialization of a collection with initial values
init(initial: Collection) {
self.nonDefault = initial
}
/// On first access, or when the weak reference to the last generated
/// object is `nil`, generate the object anew. Always return the last
/// generated object.
subscript (key: Key) -> Value {
get {
return nonDefault[key] ?? .defaultForm
}
set {
if !newValue.isDefaultForm {
nonDefault[key] = newValue
} else {
nonDefault[key] = nil
}
}
}
/// Reset the value for key to its default value
/// - Returns: the previous value at the given key
mutating func resetValue(forKey key: Key) -> Value {
return nonDefault.removeValue(forKey: key) ?? .defaultForm
}
/// Reset all content to its default value
mutating func resetAll(keepingCapacity: Bool = true) {
nonDefault.removeAll(keepingCapacity: keepingCapacity)
}
}
extension DefaultMapping: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Key, Value)...) {
self.nonDefault = Collection()
for element in elements where !element.1.isDefaultForm {
assert(nonDefault[element.0] == nil, "Duplicate key \(element.0) in \(type(of: self)) dictionary litteral.")
nonDefault[element.0] = element.1
}
}
}
extension DefaultMapping: DefaultFormRepresentable {
static var defaultForm: DefaultMapping { return [:] }
var isDefaultForm: Bool { return nonDefault.isEmpty }
}
typealias DefaultValueDictionary<K: Hashable, V: DefaultFormRepresentable> = DefaultMapping<Dictionary<K, V>>
extension DefaultMapping: Equatable where Collection: Equatable {
static func ==(a: DefaultMapping, b: DefaultMapping) -> Bool {
return a.nonDefault == b.nonDefault
}
}
extension DefaultMapping: Hashable where Collection: Hashable {
func hash(into hasher: inout Hasher) {
nonDefault.hash(into: &hasher)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment