Created
June 24, 2019 23:14
-
-
Save michelf/3684d49333d724deec20f9ebf288d4d6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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 {} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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