Skip to content

Commit

Permalink
Moved _XML…EncodingContainer into their own files, matching decoder (
Browse files Browse the repository at this point in the history
…#4)

The `XMLEncoder.swift` is far too large as it is right now.

I thus moved `_XMLUnkeyedEncodingContainer` and `_XMLKeyedEncodingContainer` into their own respective files.

Note: The `Decoder` directory already does the same.

* Moved `_XML(Unk|K)eyedEncodingContainer` into their own files, matching decoder
* Fix `testUnkeyedContainer` in NodeEncodingStrategy
* Add Int key to NodeEncodingStrategyTests
* Increase XMLKeyedEncodingContainer test coverage
* Cleanup integers in XMLKeyedEncodingContainer
* Cleanup integers in XMLUnkeyedEncodingContainer
  • Loading branch information
regexident authored and MaxDesiatov committed Dec 14, 2018
1 parent 0f07759 commit 7e13f4b
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 556 deletions.
4 changes: 1 addition & 3 deletions Sources/XMLCoder/Encoder/EncodingErrorExtension.swift
Expand Up @@ -8,9 +8,7 @@

import Foundation

//===----------------------------------------------------------------------===//
// Error Utilities
//===----------------------------------------------------------------------===//
/// Error Utilities
internal extension EncodingError {
/// Returns a `.invalidValue` error describing the given invalid floating-point value.
///
Expand Down
505 changes: 20 additions & 485 deletions Sources/XMLCoder/Encoder/XMLEncoder.swift

Large diffs are not rendered by default.

196 changes: 196 additions & 0 deletions Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift
@@ -0,0 +1,196 @@
//
// XMLKeyedEncodingContainer.swift
// XMLCoder
//
// Created by Vincent Esche on 11/20/18.
//

import Foundation

internal struct _XMLKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
typealias Key = K

// MARK: Properties

/// A reference to the encoder we're writing to.
private let encoder: _XMLEncoder

/// A reference to the container we're writing to.
private let container: NSMutableDictionary

/// The path of coding keys taken to get to this point in encoding.
private(set) public var codingPath: [CodingKey]

// MARK: - Initialization

/// Initializes `self` with the given references.
internal init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
}

// MARK: - Coding Path Operations

private func _converted(_ key: CodingKey) -> CodingKey {
switch encoder.options.keyEncodingStrategy {
case .useDefaultKeys:
return key
case .convertToSnakeCase:
let newKeyString = XMLEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue)
return _XMLKey(stringValue: newKeyString, intValue: key.intValue)
case .custom(let converter):
return converter(codingPath + [key])
}
}

// MARK: - KeyedEncodingContainerProtocol Methods

public mutating func encodeNil(forKey key: Key) throws {
self.container[_converted(key).stringValue] = NSNull()
}

public mutating func encode(_ value: Bool, forKey key: Key) throws {
self.encoder.codingPath.append(key)
defer { self.encoder.codingPath.removeLast() }
guard let strategy = self.encoder.nodeEncodings.last else {
preconditionFailure("Attempt to access node encoding strategy from empty stack.")
}
switch strategy(key) {
case .attribute:
if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary {
attributesContainer[_converted(key).stringValue] = self.encoder.box(value)
} else {
let attributesContainer = NSMutableDictionary()
attributesContainer[_converted(key).stringValue] = self.encoder.box(value)
self.container[_XMLElement.attributesKey] = attributesContainer
}
case .element:
self.container[_converted(key).stringValue] = self.encoder.box(value)
}
}

public mutating func encode<T: FixedWidthInteger & Encodable>(_ value: T, forKey key: Key) throws {
self.encoder.codingPath.append(key)
defer { self.encoder.codingPath.removeLast() }
guard let strategy = self.encoder.nodeEncodings.last else {
preconditionFailure("Attempt to access node encoding strategy from empty stack.")
}
switch strategy(key) {
case .attribute:
if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary {
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
} else {
let attributesContainer = NSMutableDictionary()
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
self.container[_XMLElement.attributesKey] = attributesContainer
}
case .element:
self.container[_converted(key).stringValue] = try self.encoder.box(value)
}
}

public mutating func encode(_ value: String, forKey key: Key) throws {
self.encoder.codingPath.append(key)
defer { self.encoder.codingPath.removeLast() }
guard let strategy = self.encoder.nodeEncodings.last else {
preconditionFailure("Attempt to access node encoding strategy from empty stack.")
}
switch strategy(key) {
case .attribute:
if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary {
attributesContainer[_converted(key).stringValue] = self.encoder.box(value)
} else {
let attributesContainer = NSMutableDictionary()
attributesContainer[_converted(key).stringValue] = self.encoder.box(value)
self.container[_XMLElement.attributesKey] = attributesContainer
}
case .element:
self.container[_converted(key).stringValue] = self.encoder.box(value)
}
}

public mutating func encode(_ value: Float, forKey key: Key) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
self.encoder.codingPath.append(key)
defer { self.encoder.codingPath.removeLast() }
self.container[_converted(key).stringValue] = try self.encoder.box(value)
}

public mutating func encode(_ value: Double, forKey key: Key) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
self.encoder.codingPath.append(key)
defer { self.encoder.codingPath.removeLast() }
guard let strategy = self.encoder.nodeEncodings.last else {
preconditionFailure("Attempt to access node encoding strategy from empty stack.")
}
switch strategy(key) {
case .attribute:
if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary {
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
} else {
let attributesContainer = NSMutableDictionary()
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
self.container[_XMLElement.attributesKey] = attributesContainer
}
case .element:
self.container[_converted(key).stringValue] = try self.encoder.box(value)
}
}

public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
guard let strategy = self.encoder.nodeEncodings.last else {
preconditionFailure("Attempt to access node encoding strategy from empty stack.")
}
self.encoder.codingPath.append(key)
let nodeEncodings = self.encoder.options.nodeEncodingStrategy.nodeEncodings(
forType: T.self,
with: self.encoder
)
self.encoder.nodeEncodings.append(nodeEncodings)
defer {
let _ = self.encoder.nodeEncodings.removeLast()
self.encoder.codingPath.removeLast()
}
switch strategy(key) {
case .attribute:
if let attributesContainer = self.container[_XMLElement.attributesKey] as? NSMutableDictionary {
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
} else {
let attributesContainer = NSMutableDictionary()
attributesContainer[_converted(key).stringValue] = try self.encoder.box(value)
self.container[_XMLElement.attributesKey] = attributesContainer
}
case .element:
self.container[_converted(key).stringValue] = try self.encoder.box(value)
}
}

public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
let dictionary = NSMutableDictionary()
self.container[_converted(key).stringValue] = dictionary

self.codingPath.append(key)
defer { self.codingPath.removeLast() }

let container = _XMLKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
return KeyedEncodingContainer(container)
}

public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
let array = NSMutableArray()
self.container[_converted(key).stringValue] = array

self.codingPath.append(key)
defer { self.codingPath.removeLast() }
return _XMLUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
}

public mutating func superEncoder() -> Encoder {
return _XMLReferencingEncoder(referencing: self.encoder, key: _XMLKey.super, convertedKey: _converted(_XMLKey.super), wrapping: self.container)
}

public mutating func superEncoder(forKey key: Key) -> Encoder {
return _XMLReferencingEncoder(referencing: self.encoder, key: key, convertedKey: _converted(key), wrapping: self.container)
}
}
90 changes: 90 additions & 0 deletions Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift
@@ -0,0 +1,90 @@
//
// XMLUnkeyedEncodingContainer.swift
// XMLCoder
//
// Created by Vincent Esche on 11/20/18.
//

import Foundation

internal struct _XMLUnkeyedEncodingContainer : UnkeyedEncodingContainer {
// MARK: Properties

/// A reference to the encoder we're writing to.
private let encoder: _XMLEncoder

/// A reference to the container we're writing to.
private let container: NSMutableArray

/// The path of coding keys taken to get to this point in encoding.
private(set) public var codingPath: [CodingKey]

/// The number of elements encoded into the container.
public var count: Int {
return self.container.count
}

// MARK: - Initialization

/// Initializes `self` with the given references.
internal init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
}

// MARK: - UnkeyedEncodingContainer Methods

public mutating func encodeNil() throws { self.container.add(NSNull()) }
public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) }

public mutating func encode<T: FixedWidthInteger & Encodable>(_ value: T) throws {
try self.container.add(self.encoder.box(value))
}

public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) }

public mutating func encode(_ value: Float) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
self.encoder.codingPath.append(_XMLKey(index: self.count))
defer { self.encoder.codingPath.removeLast() }
self.container.add(try self.encoder.box(value))
}

public mutating func encode(_ value: Double) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
self.encoder.codingPath.append(_XMLKey(index: self.count))
defer { self.encoder.codingPath.removeLast() }
self.container.add(try self.encoder.box(value))
}

public mutating func encode<T : Encodable>(_ value: T) throws {
self.encoder.codingPath.append(_XMLKey(index: self.count))
defer { self.encoder.codingPath.removeLast() }
self.container.add(try self.encoder.box(value))
}

public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
self.codingPath.append(_XMLKey(index: self.count))
defer { self.codingPath.removeLast() }

let dictionary = NSMutableDictionary()
self.container.add(dictionary)

let container = _XMLKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
return KeyedEncodingContainer(container)
}

public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
self.codingPath.append(_XMLKey(index: self.count))
defer { self.codingPath.removeLast() }

let array = NSMutableArray()
self.container.add(array)
return _XMLUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
}

public mutating func superEncoder() -> Encoder {
return _XMLReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container)
}
}
10 changes: 5 additions & 5 deletions Sources/XMLCoder/ISO8601DateFormatter.swift
Expand Up @@ -8,11 +8,11 @@

import Foundation

//===----------------------------------------------------------------------===//
// Shared ISO8601 Date Formatter
//===----------------------------------------------------------------------===//

// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS.
/// Shared ISO8601 Date Formatter
/// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled
/// against the latest SDK (w/ ISO8601DateFormatter), but linked against
/// whichever Foundation the user has. ISO8601DateFormatter might not exist, so
/// we better not hit this code path on an older OS.
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
internal var _iso8601Formatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
Expand Down
9 changes: 3 additions & 6 deletions Sources/XMLCoder/XMLKey.swift
Expand Up @@ -8,13 +8,10 @@

import Foundation

//===----------------------------------------------------------------------===//
// Shared Key Types
//===----------------------------------------------------------------------===//

/// Shared Key Types
internal struct _XMLKey: CodingKey {
public var stringValue: String
public var intValue: Int?
public let stringValue: String
public let intValue: Int?

public init?(stringValue: String) {
self.stringValue = stringValue
Expand Down
2 changes: 1 addition & 1 deletion Tests/LinuxMain.swift
Expand Up @@ -6,6 +6,6 @@ XCTMain([
testCase(BreakfastTest.allTests),
testCase(NodeEncodingStrategyTests.allTests),
testCase(BooksTest.allTests),
testCase(NoteTest.allTests)
testCase(NoteTest.allTests),
testCase(PlantTest.allTests),
])

0 comments on commit 7e13f4b

Please sign in to comment.