Created
August 2, 2023 08:08
-
-
Save tkgstrator/12e6c3fec8addcb8153048a237bd1e01 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
// | |
// OrdableDictionary.swift | |
// OrdableDictionary | |
// | |
// Created by tkgstrator on 2023/08/02. | |
// Copyright © 2023 Magi Corporation. All rights reserved. | |
// | |
@_implementationOnly import Collections | |
import Foundation | |
/// A wrapper for a dictionary to be en/decoded as expected. | |
/// Videlicet, this is yet another workaround for [SR-7788](https://bugs.swift.org/browse/SR-7788). | |
internal struct OrdableDictionary<Key, Value> where Key: OrdableDictionaryKey, Value: Codable { | |
/// Wrapped dictionary. | |
var dictionary: OrderedDictionary<Key, Value> | |
private init(_dictionary: OrderedDictionary<Key, Value>) { | |
self.dictionary = _dictionary | |
} | |
/// Creates an empty dictionary. | |
init() { | |
self.init(_dictionary: [:]) | |
} | |
/// Creates an empty dictionary with preallocated space | |
/// for at least the specified number of elements. | |
init(minimumCapacity: Int) { | |
self.init(_dictionary: .init(minimumCapacity: minimumCapacity)) | |
} | |
/// Creates a new dictionary from the key-value pairs in the given sequence. | |
init<S>(uniqueKeysWithValues keysAndValues: S) where S: Sequence, S.Element == (Key, Value) { | |
let value = keysAndValues.reduce(into: OrdableDictionary<Key, Value>(), { $0[$1.0] = $1.1 }) | |
print(value) | |
self = value | |
} | |
/// Creates a new dictionary from the key-value pairs in the given sequence, | |
/// using a combining closure to determine the value for any duplicate keys. | |
init<S>(_ keysAndValues: S, | |
uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows where S: Sequence, S.Element == (Key, Value) { | |
self.init(_dictionary: try OrderedDictionary(keysAndValues, uniquingKeysWith: combine)) | |
} | |
/// Creates a new dictionary whose keys are the groupings returned by the given closure | |
/// and whose values are arrays of the elements that returned each key. | |
init<S>(grouping values: S, | |
by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S: Sequence { | |
self.init(_dictionary: try OrderedDictionary(grouping: values, by: keyForValue)) | |
} | |
var isEmpty: Bool { | |
self.dictionary.isEmpty | |
} | |
var count: Int { | |
self.dictionary.count | |
} | |
var underestimatedCount: Int { | |
self.dictionary.underestimatedCount | |
} | |
// var capacity: Int { | |
// self.dictionary.capacity | |
// } | |
subscript(key: Key) -> Value? { | |
get { | |
self.dictionary[key] | |
} | |
set { | |
self.dictionary[key] = newValue | |
} | |
} | |
subscript(key: Key, default defaultValue: @autoclosure () -> Value) -> Value { | |
get { | |
self.dictionary[key, default: defaultValue()] | |
} | |
set { | |
self.dictionary[key, default: defaultValue()] = newValue | |
} | |
} | |
} | |
extension OrdableDictionary: Encodable { | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: Key.self) | |
for (key, value) in self.dictionary.shuffled() { | |
print(key, value) | |
try container.encode(value, forKey: key) | |
} | |
} | |
} | |
extension OrdableDictionary: Decodable { | |
init(from decoder: Decoder) throws { | |
self.init() | |
let container = try decoder.container(keyedBy: Key.self) | |
for key in container.allKeys { | |
self[key] = try container.decode(Value.self, forKey: key) | |
} | |
} | |
} | |
extension OrdableDictionary: ExpressibleByDictionaryLiteral { | |
init(dictionaryLiteral elements: (Key, Value)...) { | |
self.init() | |
for (key, value) in elements { | |
self.dictionary[key] = value | |
} | |
} | |
} | |
extension OrdableDictionary: Sequence { | |
func makeIterator() -> OrderedDictionary<Key, Value>.Iterator { | |
self.dictionary.makeIterator() | |
} | |
} | |
// extension OrdableDictionary: Sequence { | |
// typealias Element = OrderedDictionary<Key, Value>.Element | |
// | |
// typealias Iterator = DictionaryIterator<Key, Value> | |
// | |
// func makeIterator() -> Iterator { | |
// self.dictionary.makeIterator() | |
// } | |
// } | |
// extension OrdableDictionary: Collection { | |
// typealias Index = DictionaryIndex<Key, Value> | |
// | |
// var startIndex: Index { | |
// self.dictionary.startIndex | |
// } | |
// | |
// var endIndex: Index { | |
// self.dictionary.endIndex | |
// } | |
// | |
// subscript(position: Index) -> (key: Key, value: Value) { | |
// self.dictionary[position] | |
// } | |
// | |
// func index(after i: Index) -> Index { | |
// self.dictionary.index(after: i) | |
// } | |
// } | |
extension OrdableDictionary: CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { | |
var description: String { | |
self.dictionary.description | |
} | |
var debugDescription: String { | |
self.dictionary.debugDescription | |
} | |
var customMirror: Mirror { | |
self.dictionary.customMirror | |
} | |
} | |
extension OrdableDictionary: Equatable where Value: Equatable {} | |
extension OrdableDictionary: Hashable where Value: Hashable {} | |
// MARK: - Other Methods | |
extension OrdableDictionary { | |
// typealias Keys = OrderedDictionary<Key, Value>.Keys | |
typealias Values = OrderedDictionary<Key, Value>.Values | |
// | |
// func index(forKey key: Key) -> Index? { | |
// self.dictionary.index(forKey: key) | |
// } | |
// | |
// var keys: Keys { | |
// self.dictionary.keys | |
// } | |
// | |
var values: Values { | |
self.dictionary.values | |
} | |
// | |
// var first: Element? { | |
// self.dictionary.first | |
// } | |
// | |
// /// Returns a random element of the collection. | |
// func randomElement() -> (key: Key, value: Value)? { | |
// self.dictionary.randomElement() | |
// } | |
// | |
// /// Returns a random element of the collection, using the given generator as a source for randomness. | |
// func randomElement<T>(using generator: inout T) -> (key: Key, value: Value)? where T: RandomNumberGenerator { | |
// self.dictionary.randomElement(using: &generator) | |
// } | |
// | |
// /// Updates the value stored in the dictionary for the given key, | |
// /// or adds a new key-value pair if the key does not exist. | |
// @discardableResult | |
// mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { | |
// self.dictionary.updateValue(value, forKey: key) | |
// } | |
// | |
// /// Merges the key-value pairs in the given sequence into the dictionary, | |
// /// using a combining closure to determine the value for any duplicate keys. | |
// mutating func merge<S>(_ other: S, | |
// uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows where S: Sequence, S.Element == (Key, Value) { | |
// try self.dictionary.merge(other, uniquingKeysWith: combine) | |
// } | |
// | |
// /// Creates a codable dictionary by merging key-value pairs in a sequence into the dictionary, | |
// /// using a combining closure to determine the value for duplicate keys. | |
// func merging<S>(_ other: S, | |
// uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows -> OrdableDictionary<Key, Value> where S: Sequence, S.Element == (Key, Value) { | |
// OrdableDictionary(_dictionary: try self.dictionary.merging(other, uniquingKeysWith: combine)) | |
// } | |
// | |
// mutating func reserveCapacity(_ minimumCapacity: Int) { | |
// self.dictionary.reserveCapacity(minimumCapacity) | |
// } | |
// | |
// @discardableResult | |
// mutating func removeValue(forKey key: Key) -> Value? { | |
// self.dictionary.removeValue(forKey: key) | |
// } | |
// | |
// @discardableResult | |
// mutating func remove(at index: Index) -> Element { | |
// self.dictionary.remove(at: index) | |
// } | |
// | |
// mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { | |
// self.dictionary.removeAll(keepingCapacity: keepCapacity) | |
// } | |
// | |
// /// Returns a new codable dictionary containing the keys of this dictionary | |
// /// with the values transformed by the given closure. | |
// func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> OrdableDictionary<Key, T> { | |
// OrdableDictionary<Key, T>(_dictionary: try self.dictionary.mapValues(transform)) | |
// } | |
} |
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
// | |
// OrdableDictionaryKey.swift | |
// OrdableDictionary | |
// | |
// Created by tkgstrator on 2023/08/02. | |
// Copyright © 2023 Magi Corporation. All rights reserved. | |
// | |
import Foundation | |
/// A type that can be a key of `OrdableDictionary`. | |
public protocol OrdableDictionaryKey: Hashable, CodingKey {} | |
// Note: `extension OrdableDictionaryKey where Self: LosslessStringConvertible` will cause ambiguity errors. | |
extension OrdableDictionaryKey where Self: FixedWidthInteger { | |
public var description: String { | |
// Workaround for https://github.com/YOCKOW/SwiftOrdableDictionary/issues/2 | |
func __description<B>(_ int: B) -> String where B: BinaryInteger { | |
int.description | |
} | |
return __description(self) | |
} | |
public var stringValue: String { | |
String(self, radix: 10) | |
} | |
public var intValue: Int? { | |
guard Int.min <= self && self <= Int.max else { return nil } | |
return Int(self) | |
} | |
public init?(stringValue: String) { | |
self.init(stringValue) | |
} | |
public init?(intValue: Int) { | |
guard Self.min <= intValue && intValue <= Self.max else { return nil } | |
self.init(intValue) | |
} | |
} | |
extension OrdableDictionaryKey where Self: RawRepresentable, Self.RawValue: OrdableDictionaryKey { | |
public var stringValue: String { | |
self.rawValue.stringValue | |
} | |
public var intValue: Int? { | |
self.rawValue.intValue | |
} | |
public init?(stringValue: String) { | |
guard let rawValue = Self.RawValue(stringValue: stringValue) else { return nil } | |
self.init(rawValue: rawValue) | |
} | |
public init?(intValue: Int) { | |
guard let rawValue = Self.RawValue(intValue: intValue) else { return nil } | |
self.init(rawValue: rawValue) | |
} | |
} | |
// Requires this because of https://github.com/apple/swift/blob/master/lib/Sema/DerivedConformanceCodingKey.cpp | |
extension OrdableDictionaryKey where Self: RawRepresentable, Self.RawValue == Int { | |
public var stringValue: String { | |
String(self.rawValue) | |
} | |
public var intValue: Int? { | |
self.rawValue | |
} | |
public init?(stringValue: String) { | |
guard let integer = Int(stringValue) else { return nil } | |
self.init(rawValue: integer) | |
} | |
public init?(intValue: Int) { | |
self.init(rawValue: intValue) | |
} | |
} | |
extension String: OrdableDictionaryKey { | |
public var stringValue: String { | |
self | |
} | |
public var intValue: Int? { | |
Int(self) | |
} | |
public init?(stringValue: String) { | |
self = stringValue | |
} | |
public init?(intValue: Int) { | |
self.init(intValue) | |
} | |
} | |
extension Substring: OrdableDictionaryKey { | |
public var stringValue: String { | |
String(self) | |
} | |
public var intValue: Int? { | |
Int(self) | |
} | |
public init?(stringValue: String) { | |
self = stringValue[...] | |
} | |
public init?(intValue: Int) { | |
self.init(stringValue: String(intValue)) | |
} | |
} | |
extension Unicode.Scalar: OrdableDictionaryKey { | |
public var stringValue: String { | |
String(self) | |
} | |
public var intValue: Int? { | |
Int(self.stringValue) | |
} | |
public init?(stringValue: String) { | |
// Complexity of `count` of `UnicodeScalarView` is O(n) | |
let scalars = stringValue.unicodeScalars | |
if scalars.isEmpty { return nil } | |
guard scalars.dropFirst().isEmpty else { return nil } | |
self = scalars.first! | |
} | |
public init?(intValue: Int) { | |
self.init(stringValue: String(intValue)) | |
} | |
} | |
extension Character: OrdableDictionaryKey { | |
public var stringValue: String { | |
String(self) | |
} | |
public var intValue: Int? { | |
Int(self.stringValue) | |
} | |
public init?(stringValue: String) { | |
// Complexity of `count` of `String` is O(n) | |
if stringValue.isEmpty { return nil } | |
guard stringValue.dropFirst().isEmpty else { return nil } | |
self = stringValue.first! | |
} | |
public init?(intValue: Int) { | |
self.init(stringValue: String(intValue)) | |
} | |
} | |
extension Int: OrdableDictionaryKey { | |
public var intValue: Int? { | |
self | |
} | |
public init?(intValue: Int) { | |
self = intValue | |
} | |
} | |
extension Int8: OrdableDictionaryKey {} | |
extension Int16: OrdableDictionaryKey {} | |
extension Int32: OrdableDictionaryKey {} | |
extension Int64: OrdableDictionaryKey {} | |
extension UInt8: OrdableDictionaryKey {} | |
extension UInt16: OrdableDictionaryKey {} | |
extension UInt32: OrdableDictionaryKey {} | |
extension UInt64: OrdableDictionaryKey {} | |
extension UInt: OrdableDictionaryKey {} | |
extension Float: OrdableDictionaryKey { | |
public var stringValue: String { | |
self.description | |
} | |
public var intValue: Int? { | |
Int(self) | |
} | |
public init?(stringValue: String) { | |
self.init(stringValue) | |
} | |
public init?(intValue: Int) { | |
self.init(intValue) | |
} | |
} | |
extension Double: OrdableDictionaryKey { | |
public var stringValue: String { | |
self.description | |
} | |
public var intValue: Int? { | |
Int(self) | |
} | |
public init?(stringValue: String) { | |
self.init(stringValue) | |
} | |
public init?(intValue: Int) { | |
self.init(intValue) | |
} | |
} | |
#if arch(i386) || arch(x86_64) | |
extension Float80: OrdableDictionaryKey { | |
public var stringValue: String { | |
self.description | |
} | |
public var intValue: Int? { | |
Int(self) | |
} | |
public init?(stringValue: String) { | |
self.init(stringValue) | |
} | |
public init?(intValue: Int) { | |
self.init(intValue) | |
} | |
} | |
#endif | |
extension Bool: OrdableDictionaryKey { | |
public var stringValue: String { | |
self ? "true" : "false" | |
} | |
public var intValue: Int? { | |
self ? 1 : 0 | |
} | |
public init?(stringValue: String) { | |
// Also supports YAML's Boolean: https://yaml.org/type/bool.html | |
switch stringValue.lowercased() { | |
case "y", "yes", "true", "on": | |
self = true | |
case "n", "no", "false", "off": | |
self = false | |
default: | |
return nil | |
} | |
} | |
public init?(intValue: Int) { | |
self = intValue != 0 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment