Make `Node.Mapping` behave like a `Dictionary<Node, Node>`

This commit is contained in:
Norio Nomura 2017-02-03 23:11:58 +09:00
parent 71e81e9fbc
commit c625d66880
No known key found for this signature in database
GPG Key ID: D4A7318EB7F7138D
3 changed files with 135 additions and 4 deletions

View File

@ -344,6 +344,98 @@ extension Node: ExpressibleByStringLiteral {
}
}
// MARK: - Node.Mapping
extension Node.Mapping: Equatable {
public static func == (lhs: Node.Mapping, rhs: Node.Mapping) -> Bool {
return lhs.pairs == rhs.pairs
}
}
extension Node.Mapping: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (Node, Node)...) {
self.init(pairs: elements.map(Pair.init), tag: .implicit, style: .any)
}
}
extension Node.Mapping: MutableCollection {
public typealias Element = (key: Node, value: Node)
// Sequence
public func makeIterator() -> Array<Element>.Iterator {
let iterator = pairs.map({ (key: $0.key, value: $0.value) }).makeIterator()
return iterator
}
// Collection
public typealias Index = Array<Element>.Index
public var startIndex: Int {
return pairs.startIndex
}
public var endIndex: Int {
return pairs.endIndex
}
public func index(after index: Int) -> Int {
return pairs.index(after:index)
}
public subscript(index: Int) -> Element {
get {
return (key: pairs[index].key, value: pairs[index].value)
}
// MutableCollection
set {
pairs[index] = Pair(newValue.key, newValue.value)
}
}
}
extension Node.Mapping {
public var keys: LazyMapCollection<Node.Mapping, Node> {
return lazy.map { $0.key }
}
public var values: LazyMapCollection<Node.Mapping, Node> {
return lazy.map { $0.value }
}
public subscript(string: String) -> Node? {
get {
return self[Node(string)]
}
set {
self[Node(string)] = newValue
}
}
public subscript(node: Node) -> Node? {
get {
let v = pairs.reversed().first(where: { $0.key == node })
return v?.value
}
set {
if let newValue = newValue {
if let index = pairs.reversed().index(where: { $0.key == node }) {
let actualIndex = pairs.index(before: index.base)
pairs[actualIndex] = Pair(pairs[actualIndex].key, newValue)
} else {
pairs.append(Pair(node, newValue))
}
} else {
if let index = pairs.reversed().index(where: { $0.key == node }) {
let actualIndex = pairs.index(before: index.base)
pairs.remove(at: actualIndex)
}
}
}
}
}
// MARK: - internal
extension Node {
// MARK: Internal convenience accessors
var isScalar: Bool {
@ -367,3 +459,5 @@ extension Node {
return false
}
}
// swiftlint:disable:this file_length

View File

@ -120,6 +120,38 @@ class NodeTests: XCTestCase {
let valueAtSecond = mapping[1]?.string
XCTAssertEqual(valueAtSecond, "value2")
}
func testMappingBehavesLikeADictionary() {
let node: Node = ["key1": "value1", "key2": "value2"]
let mapping = node.mapping!
XCTAssertEqual(mapping.count, 2)
XCTAssertEqual(mapping.endIndex, 2)
XCTAssertTrue(mapping.first! == ("key1", "value1"))
XCTAssertFalse(mapping.isEmpty)
XCTAssertEqual(Set(mapping.keys), ["key1", "key2"])
XCTAssertEqual(mapping.lazy.count, 2)
XCTAssertEqual(mapping.startIndex, 0)
XCTAssertEqual(mapping.underestimatedCount, 2)
XCTAssertEqual(Set(mapping.values), ["value1", "value2"])
// subscript
var mutableMapping = mapping
mutableMapping["key3"] = "value3"
XCTAssertEqual(mutableMapping["key3"], "value3")
mutableMapping["key3"] = "value3changed"
XCTAssertEqual(mutableMapping["key3"], "value3changed")
mutableMapping["key3"] = nil
XCTAssertNil(mutableMapping["key3"])
// iterator
var iterator = mapping.makeIterator()
XCTAssertTrue(iterator.next()! == ("key1", "value1"))
XCTAssertTrue(iterator.next()! == ("key2", "value2"))
XCTAssertNil(iterator.next())
// ExpressibleByDictionaryLiteral
XCTAssertEqual(mapping, ["key1": "value1", "key2": "value2"])
}
}
extension NodeTests {
@ -130,9 +162,11 @@ extension NodeTests {
("testExpressibleByFloatLiteral", testExpressibleByFloatLiteral),
("testExpressibleByIntegerLiteral", testExpressibleByIntegerLiteral),
("testExpressibleByStringLiteral", testExpressibleByStringLiteral),
("testTypedAccessorProperties", testTypedAccessorProperties),
("testArray", testArray),
("testSubscriptMapping", testSubscriptMapping),
("testSubscriptSequence", testSubscriptSequence),
("testArray", testArray)
("testMappingBehavesLikeADictionary", testMappingBehavesLikeADictionary)
]
}
}

View File

@ -62,11 +62,12 @@ class PerformanceTests: XCTestCase {
XCTFail("Can't load \(filename)")
return
}
let spmName = "SourceKittenFramework"
self.measure {
do {
guard let yaml = try Yams.load(yaml: yamlString) as? [String:Any],
let commands = yaml["commands"] as? [String:Any],
let moduleCommand = commands["<SourceKittenFramework.module>"] as? [String:Any],
let commands = (yaml["commands"] as? [String:[String:Any]])?.values,
let moduleCommand = commands.first(where: { ($0["module-name"] as? String ?? "") == spmName }),
let imports = moduleCommand["import-paths"] as? [String],
let otherArguments = moduleCommand["other-args"] as? [String],
let sources = moduleCommand["sources"] as? [String] else {
@ -87,10 +88,12 @@ class PerformanceTests: XCTestCase {
XCTFail("Can't load \(filename)")
return
}
let spmName = "SourceKittenFramework"
self.measure {
do {
guard let yaml = try Yams.compose(yaml: yamlString),
let moduleCommand = yaml["commands"]?["<SourceKittenFramework.module>"],
let commands = yaml["commands"]?.mapping?.values,
let moduleCommand = commands.first(where: { $0["module-name"]?.string == spmName }),
let imports = moduleCommand["import-paths"]?.array(of: String.self),
let otherArguments = moduleCommand["other-args"]?.array(of: String.self),
let sources = moduleCommand["sources"]?.array(of: String.self) else {