diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..4b35366d --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,17 @@ +disabled_rules: +- trailing_comma +- identifier_name +- private_over_fileprivate +- void_return +- operator_whitespace +- function_parameter_count + +line_length: +- 150 +- 200 + +function_body_length: +- 50 + +type_name: + min_length: 1 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 648f3c81..7582d08c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,10 @@ before_install: - gem install cocoapods --pre # Since Travis is not always on latest version - brew update - brew install swiftformat +- brew outdated swiftlint || brew upgrade swiftlint script: - swiftformat --lint --verbose . +- swiftlint - pod lib lint - xcodebuild test -enableCodeCoverage YES -scheme XMLCoder # this runs the tests the second time, but it's the only way to make sure diff --git a/Sources/XMLCoder/Box/BoolBox.swift b/Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift similarity index 100% rename from Sources/XMLCoder/Box/BoolBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/BoolBox.swift diff --git a/Sources/XMLCoder/Box/Box.swift b/Sources/XMLCoder/Auxiliaries/Box/Box.swift similarity index 100% rename from Sources/XMLCoder/Box/Box.swift rename to Sources/XMLCoder/Auxiliaries/Box/Box.swift diff --git a/Sources/XMLCoder/Box/DataBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DataBox.swift similarity index 100% rename from Sources/XMLCoder/Box/DataBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/DataBox.swift diff --git a/Sources/XMLCoder/Box/DateBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DateBox.swift similarity index 100% rename from Sources/XMLCoder/Box/DateBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/DateBox.swift diff --git a/Sources/XMLCoder/Box/DecimalBox.swift b/Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift similarity index 100% rename from Sources/XMLCoder/Box/DecimalBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/DecimalBox.swift diff --git a/Sources/XMLCoder/Box/FloatBox.swift b/Sources/XMLCoder/Auxiliaries/Box/FloatBox.swift similarity index 100% rename from Sources/XMLCoder/Box/FloatBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/FloatBox.swift diff --git a/Sources/XMLCoder/Box/IntBox.swift b/Sources/XMLCoder/Auxiliaries/Box/IntBox.swift similarity index 100% rename from Sources/XMLCoder/Box/IntBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/IntBox.swift diff --git a/Sources/XMLCoder/Box/KeyedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift similarity index 100% rename from Sources/XMLCoder/Box/KeyedBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift diff --git a/Sources/XMLCoder/Box/NullBox.swift b/Sources/XMLCoder/Auxiliaries/Box/NullBox.swift similarity index 100% rename from Sources/XMLCoder/Box/NullBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/NullBox.swift diff --git a/Sources/XMLCoder/Box/SharedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift similarity index 100% rename from Sources/XMLCoder/Box/SharedBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/SharedBox.swift diff --git a/Sources/XMLCoder/Box/StringBox.swift b/Sources/XMLCoder/Auxiliaries/Box/StringBox.swift similarity index 100% rename from Sources/XMLCoder/Box/StringBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/StringBox.swift diff --git a/Sources/XMLCoder/Box/UIntBox.swift b/Sources/XMLCoder/Auxiliaries/Box/UIntBox.swift similarity index 100% rename from Sources/XMLCoder/Box/UIntBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/UIntBox.swift diff --git a/Sources/XMLCoder/Box/URLBox.swift b/Sources/XMLCoder/Auxiliaries/Box/URLBox.swift similarity index 100% rename from Sources/XMLCoder/Box/URLBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/URLBox.swift diff --git a/Sources/XMLCoder/Box/UnkeyedBox.swift b/Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift similarity index 100% rename from Sources/XMLCoder/Box/UnkeyedBox.swift rename to Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift diff --git a/Sources/XMLCoder/Auxiliaries/XMLElement.swift b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift similarity index 55% rename from Sources/XMLCoder/Auxiliaries/XMLElement.swift rename to Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift index c7f1e5df..7301e3b9 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift @@ -1,5 +1,5 @@ // -// XMLElement.swift +// XMLCoderElement.swift // XMLCoder // // Created by Vincent Esche on 12/18/18. @@ -7,16 +7,27 @@ import Foundation -struct _XMLElement { +struct XMLCoderElement: Equatable { static let attributesKey = "___ATTRIBUTES" - static let escapedCharacterSet = [("&", "&"), ("<", "<"), (">", ">"), ("'", "'"), ("\"", """)] + static let escapedCharacterSet = [ + ("&", "&"), + ("<", "<"), + (">", ">"), + ("'", "'"), + ("\"", """), + ] var key: String var value: String? - var elements: [_XMLElement] = [] + var elements: [XMLCoderElement] = [] var attributes: [String: String] = [:] - init(key: String, value: String? = nil, elements: [_XMLElement] = [], attributes: [String: String] = [:]) { + init( + key: String, + value: String? = nil, + elements: [XMLCoderElement] = [], + attributes: [String: String] = [:] + ) { self.key = key self.value = value self.elements = elements @@ -25,43 +36,55 @@ struct _XMLElement { init(key: String, box: UnkeyedBox) { let elements = box.map { box in - _XMLElement(key: key, box: box) + XMLCoderElement(key: key, box: box) } self.init(key: key, elements: elements) } init(key: String, box: KeyedBox) { - var elements: [_XMLElement] = [] + var elements: [XMLCoderElement] = [] for (key, box) in box.elements { + let fail = { + preconditionFailure("Unclassified box: \(type(of: box))") + } + switch box { case let sharedUnkeyedBox as SharedBox: - let box = sharedUnkeyedBox.unbox() as! UnkeyedBox - elements.append(contentsOf: box.map { _XMLElement(key: key, box: $0) }) + guard let box = sharedUnkeyedBox.unbox() as? UnkeyedBox else { + fail() + } + elements.append(contentsOf: box.map { + XMLCoderElement(key: key, box: $0) + }) case let unkeyedBox as UnkeyedBox: // This basically injects the unkeyed children directly into self: elements.append(contentsOf: unkeyedBox.map { - _XMLElement(key: key, box: $0) + XMLCoderElement(key: key, box: $0) }) case let sharedKeyedBox as SharedBox: - let box = sharedKeyedBox.unbox() as! KeyedBox - elements.append(_XMLElement(key: key, box: box)) + guard let box = sharedKeyedBox.unbox() as? KeyedBox else { + fail() + } + elements.append(XMLCoderElement(key: key, box: box)) case let keyedBox as KeyedBox: - elements.append(_XMLElement(key: key, box: keyedBox)) + elements.append(XMLCoderElement(key: key, box: keyedBox)) case let simpleBox as SimpleBox: - elements.append(_XMLElement(key: key, box: simpleBox)) - case let box: - preconditionFailure("Unclassified box: \(type(of: box))") + elements.append(XMLCoderElement(key: key, box: simpleBox)) + default: + fail() } } - let attributes: [String: String] = Dictionary(uniqueKeysWithValues: box.attributes.compactMap { key, box in - guard let value = box.xmlString() else { - return nil + let attributes: [String: String] = Dictionary( + uniqueKeysWithValues: box.attributes.compactMap { key, box in + guard let value = box.xmlString() else { + return nil + } + return (key, value) } - return (key, value) - }) + ) self.init(key: key, elements: elements, attributes: attributes) } @@ -94,7 +117,7 @@ struct _XMLElement { self.value = value } - mutating func append(element: _XMLElement, forKey key: String) { + mutating func append(element: XMLCoderElement, forKey key: String) { elements.append(element) } @@ -152,53 +175,109 @@ struct _XMLElement { return keyedBox } - func toXMLString(with header: XMLHeader? = nil, withCDATA cdata: Bool, formatting: XMLEncoder.OutputFormatting, ignoreEscaping _: Bool = false) -> String { + func toXMLString(with header: XMLHeader? = nil, + withCDATA cdata: Bool, + formatting: XMLEncoder.OutputFormatting, + ignoreEscaping _: Bool = false) -> String { if let header = header, let headerXML = header.toXML() { return headerXML + _toXMLString(withCDATA: cdata, formatting: formatting) } return _toXMLString(withCDATA: cdata, formatting: formatting) } - fileprivate func formatUnsortedXMLElements(_ string: inout String, _ level: Int, _ cdata: Bool, _ formatting: XMLEncoder.OutputFormatting, _ prettyPrinted: Bool) { - formatXMLElements(from: elements, into: &string, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) + private func formatUnsortedXMLElements( + _ string: inout String, + _ level: Int, + _ cdata: Bool, + _ formatting: XMLEncoder.OutputFormatting, + _ prettyPrinted: Bool + ) { + formatXMLElements( + from: elements, + into: &string, + at: level, + cdata: cdata, + formatting: formatting, + prettyPrinted: prettyPrinted + ) } - fileprivate func elementString(for element: _XMLElement, at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) -> String { + fileprivate func elementString( + for element: XMLCoderElement, + at level: Int, + cdata: Bool, + formatting: XMLEncoder.OutputFormatting, + prettyPrinted: Bool + ) -> String { var string = "" - string += element._toXMLString(indented: level + 1, withCDATA: cdata, formatting: formatting) + string += element._toXMLString( + indented: level + 1, withCDATA: cdata, formatting: formatting + ) string += prettyPrinted ? "\n" : "" return string } - fileprivate func formatSortedXMLElements(_ string: inout String, _ level: Int, _ cdata: Bool, _ formatting: XMLEncoder.OutputFormatting, _ prettyPrinted: Bool) { - formatXMLElements(from: elements.sorted { $0.key < $1.key }, into: &string, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) + fileprivate func formatSortedXMLElements( + _ string: inout String, + _ level: Int, + _ cdata: Bool, + _ formatting: XMLEncoder.OutputFormatting, + _ prettyPrinted: Bool + ) { + formatXMLElements(from: elements.sorted { $0.key < $1.key }, + into: &string, + at: level, + cdata: cdata, + formatting: formatting, + prettyPrinted: prettyPrinted) } fileprivate func attributeString(key: String, value: String) -> String { - return " \(key)=\"\(value.escape(_XMLElement.escapedCharacterSet))\"" + return " \(key)=\"\(value.escape(XMLCoderElement.escapedCharacterSet))\"" } - fileprivate func formatXMLAttributes(from keyValuePairs: [(key: String, value: String)], into string: inout String) { + fileprivate func formatXMLAttributes( + from keyValuePairs: [(key: String, value: String)], + into string: inout String + ) { for (key, value) in keyValuePairs { string += attributeString(key: key, value: value) } } - fileprivate func formatXMLElements(from elements: [_XMLElement], into string: inout String, at level: Int, cdata: Bool, formatting: XMLEncoder.OutputFormatting, prettyPrinted: Bool) { + fileprivate func formatXMLElements( + from elements: [XMLCoderElement], + into string: inout String, + at level: Int, + cdata: Bool, + formatting: XMLEncoder.OutputFormatting, + prettyPrinted: Bool + ) { for element in elements { - string += elementString(for: element, at: level, cdata: cdata, formatting: formatting, prettyPrinted: prettyPrinted) + string += elementString(for: element, + at: level, + cdata: cdata, + formatting: formatting, + prettyPrinted: prettyPrinted) } } fileprivate func formatSortedXMLAttributes(_ string: inout String) { - formatXMLAttributes(from: attributes.sorted(by: { $0.key < $1.key }), into: &string) + formatXMLAttributes( + from: attributes.sorted(by: { $0.key < $1.key }), into: &string + ) } fileprivate func formatUnsortedXMLAttributes(_ string: inout String) { - formatXMLAttributes(from: attributes.map { (key: $0, value: $1) }, into: &string) + formatXMLAttributes( + from: attributes.map { (key: $0, value: $1) }, into: &string + ) } - fileprivate func formatXMLAttributes(_ formatting: XMLEncoder.OutputFormatting, _ string: inout String) { + private func formatXMLAttributes( + _ formatting: XMLEncoder.OutputFormatting, + _ string: inout String + ) { if formatting.contains(.sortedKeys) { formatSortedXMLAttributes(&string) return @@ -206,17 +285,34 @@ struct _XMLElement { formatUnsortedXMLAttributes(&string) } - fileprivate func formatXMLElements(_ formatting: XMLEncoder.OutputFormatting, _ string: inout String, _ level: Int, _ cdata: Bool, _ prettyPrinted: Bool) { + private func formatXMLElements( + _ formatting: XMLEncoder.OutputFormatting, + _ string: inout String, + _ level: Int, + _ cdata: Bool, + _ prettyPrinted: Bool + ) { if formatting.contains(.sortedKeys) { - formatSortedXMLElements(&string, level, cdata, formatting, prettyPrinted) + formatSortedXMLElements( + &string, level, cdata, formatting, prettyPrinted + ) return } - formatUnsortedXMLElements(&string, level, cdata, formatting, prettyPrinted) + formatUnsortedXMLElements( + &string, level, cdata, formatting, prettyPrinted + ) } - fileprivate func _toXMLString(indented level: Int = 0, withCDATA cdata: Bool, formatting: XMLEncoder.OutputFormatting, ignoreEscaping: Bool = false) -> String { + private func _toXMLString( + indented level: Int = 0, + withCDATA cdata: Bool, + formatting: XMLEncoder.OutputFormatting, + ignoreEscaping: Bool = false + ) -> String { let prettyPrinted = formatting.contains(.prettyPrinted) - let indentation = String(repeating: " ", count: (prettyPrinted ? level : 0) * 4) + let indentation = String( + repeating: " ", count: (prettyPrinted ? level : 0) * 4 + ) var string = indentation string += "<\(key)" @@ -225,7 +321,8 @@ struct _XMLElement { if let value = value { string += ">" if !ignoreEscaping { - string += (cdata == true ? "" : "\(value.escape(_XMLElement.escapedCharacterSet))") + string += (cdata == true ? "" : + "\(value.escape(XMLCoderElement.escapedCharacterSet))") } else { string += "\(value)" } @@ -243,5 +340,3 @@ struct _XMLElement { return string } } - -extension _XMLElement: Equatable {} diff --git a/Sources/XMLCoder/Auxiliaries/XMLKey.swift b/Sources/XMLCoder/Auxiliaries/XMLKey.swift index 7c8d9832..a3d99621 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLKey.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLKey.swift @@ -9,7 +9,7 @@ import Foundation /// Shared Key Types -struct _XMLKey: CodingKey { +struct XMLKey: CodingKey { public let stringValue: String public let intValue: Int? @@ -34,5 +34,5 @@ struct _XMLKey: CodingKey { self.init(stringValue: "\(index)", intValue: index) } - static let `super` = _XMLKey(stringValue: "super")! + static let `super` = XMLKey(stringValue: "super")! } diff --git a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift index b520bd69..c9bf9699 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLStackParser.swift @@ -10,13 +10,13 @@ import Foundation struct XMLElementContext {} -class _XMLStackParser: NSObject { - var root: _XMLElement? - private var stack: [_XMLElement] = [] +class XMLStackParser: NSObject { + var root: XMLCoderElement? + private var stack: [XMLCoderElement] = [] static func parse(with data: Data, errorContextLength length: UInt) throws -> KeyedBox { - let parser = _XMLStackParser() + let parser = XMLStackParser() let node = try parser.parse(with: data, errorContextLength: length) @@ -24,7 +24,7 @@ class _XMLStackParser: NSObject { } func parse(with data: Data, - errorContextLength: UInt) throws -> _XMLElement { + errorContextLength: UInt) throws -> XMLCoderElement { let xmlParser = XMLParser(data: data) xmlParser.delegate = self @@ -78,7 +78,7 @@ class _XMLStackParser: NSObject { )) } - func withCurrentElement(_ body: (inout _XMLElement) throws -> ()) rethrows { + func withCurrentElement(_ body: (inout XMLCoderElement) throws -> ()) rethrows { guard !stack.isEmpty else { return } @@ -86,7 +86,7 @@ class _XMLStackParser: NSObject { } } -extension _XMLStackParser: XMLParserDelegate { +extension XMLStackParser: XMLParserDelegate { func parserDidStartDocument(_: XMLParser) { root = nil stack = [] @@ -97,7 +97,7 @@ extension _XMLStackParser: XMLParserDelegate { namespaceURI _: String?, qualifiedName _: String?, attributes attributeDict: [String: String] = [:]) { - let element = _XMLElement(key: elementName, attributes: attributeDict) + let element = XMLCoderElement(key: elementName, attributes: attributeDict) stack.append(element) } diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index a3ff939c..0d8c2bc4 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -38,7 +38,9 @@ open class XMLDecoder { case custom((_ decoder: Decoder) throws -> Date) /// Decode the `Date` as a string parsed by the given formatter for the give key. - static func keyFormatted(_ formatterForKey: @escaping (CodingKey) throws -> DateFormatter?) -> XMLDecoder.DateDecodingStrategy { + static func keyFormatted( + _ formatterForKey: @escaping (CodingKey) throws -> DateFormatter? + ) -> XMLDecoder.DateDecodingStrategy { return .custom({ (decoder) -> Date in guard let codingKey = decoder.codingPath.last else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -56,13 +58,19 @@ open class XMLDecoder { } guard let dateFormatter = try formatterForKey(codingKey) else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "No date formatter for date text") + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "No date formatter for date text" + ) } if let date = dateFormatter.date(from: text) { return date } else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(text)") + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Cannot decode date string \(text)" + ) } }) } @@ -80,7 +88,9 @@ open class XMLDecoder { case custom((_ decoder: Decoder) throws -> Data) /// Decode the `Data` as a custom box by the given closure for the give key. - static func keyFormatted(_ formatterForKey: @escaping (CodingKey) throws -> Data?) -> XMLDecoder.DataDecodingStrategy { + static func keyFormatted( + _ formatterForKey: @escaping (CodingKey) throws -> Data? + ) -> XMLDecoder.DataDecodingStrategy { return .custom({ (decoder) -> Data in guard let codingKey = decoder.codingPath.last else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -98,7 +108,10 @@ open class XMLDecoder { } guard let data = try formatterForKey(codingKey) else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode data string \(text)") + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Cannot decode data string \(text)" + ) } return data @@ -120,9 +133,12 @@ open class XMLDecoder { /// Use the keys specified by each type. This is the default strategy. case useDefaultKeys - /// Convert from "snake_case_keys" to "camelCaseKeys" before attempting to match a key with the one specified by each type. + /// Convert from "snake_case_keys" to "camelCaseKeys" before attempting + /// to match a key with the one specified by each type. /// - /// The conversion to upper case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences. + /// The conversion to upper case uses `Locale.system`, also known as + /// the ICU "root" locale. This means the result is consistent + /// regardless of the current user's locale and language preferences. /// /// Converting from snake case to camel case: /// 1. Capitalizes the word starting after each `_` @@ -136,9 +152,14 @@ open class XMLDecoder { /// Convert from "CodingKey" to "codingKey" case convertFromCapitalized - /// Provide a custom conversion from the key in the encoded XML to the keys specified by the decoded types. - /// The full path to the current decoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before decoding. - /// If the result of the conversion is a duplicate key, then only one box will be present in the container for the type to decode from. + /// Provide a custom conversion from the key in the encoded XML to the + /// keys specified by the decoded types. + /// The full path to the current decoding position is provided for + /// context (in case you need to locate this key within the payload). + /// The returned key is used in place of the last component in the + /// coding path before decoding. + /// If the result of the conversion is a duplicate key, then only one + /// box will be present in the container for the type to decode from. case custom((_ codingPath: [CodingKey]) -> CodingKey) static func _convertFromCapitalized(_ stringKey: String) -> String { @@ -218,7 +239,7 @@ open class XMLDecoder { open var errorContextLength: UInt = 0 /// Options set on the top-level encoder to pass down the decoding hierarchy. - struct _Options { + struct Options { let dateDecodingStrategy: DateDecodingStrategy let dataDecodingStrategy: DataDecodingStrategy let nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy @@ -227,12 +248,12 @@ open class XMLDecoder { } /// The options set on the top-level decoder. - var options: _Options { - return _Options(dateDecodingStrategy: dateDecodingStrategy, - dataDecodingStrategy: dataDecodingStrategy, - nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy, - keyDecodingStrategy: keyDecodingStrategy, - userInfo: userInfo) + var options: Options { + return Options(dateDecodingStrategy: dateDecodingStrategy, + dataDecodingStrategy: dataDecodingStrategy, + nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy, + keyDecodingStrategy: keyDecodingStrategy, + userInfo: userInfo) } // MARK: - Constructing a XML Decoder @@ -249,13 +270,16 @@ open class XMLDecoder { /// - returns: A box of the requested type. /// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not valid XML. /// - throws: An error if any box throws an error during decoding. - open func decode(_ type: T.Type, from data: Data) throws -> T { - let topLevel: Box = try _XMLStackParser.parse( + open func decode( + _ type: T.Type, + from data: Data + ) throws -> T { + let topLevel: Box = try XMLStackParser.parse( with: data, errorContextLength: errorContextLength ) - let decoder = _XMLDecoder(referencing: topLevel, options: options) + let decoder = XMLDecoderImplementation(referencing: topLevel, options: options) guard let box: T = try decoder.unbox(topLevel) else { throw DecodingError.valueNotFound(type, DecodingError.Context( @@ -268,16 +292,14 @@ open class XMLDecoder { } } -// MARK: - _XMLDecoder - -class _XMLDecoder: Decoder { +class XMLDecoderImplementation: Decoder { // MARK: Properties /// The decoder's storage. - var storage: _XMLDecodingStorage + var storage: XMLDecodingStorage /// Options set on the top-level decoder. - let options: XMLDecoder._Options + let options: XMLDecoder.Options /// The path to the current point in encoding. public internal(set) var codingPath: [CodingKey] @@ -293,8 +315,8 @@ class _XMLDecoder: Decoder { // MARK: - Initialization /// Initializes `self` with the given top-level container and options. - init(referencing container: Box, at codingPath: [CodingKey] = [], options: XMLDecoder._Options) { - storage = _XMLDecodingStorage() + init(referencing container: Box, at codingPath: [CodingKey] = [], options: XMLDecoder.Options) { + storage = XMLDecodingStorage() storage.push(container: container) self.codingPath = codingPath self.options = options @@ -333,10 +355,14 @@ class _XMLDecoder: Decoder { } guard let keyed = topContainer as? SharedBox else { - throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: topContainer) + throw DecodingError._typeMismatch( + at: codingPath, + expectation: [String: Any].self, + reality: topContainer + ) } - let container = _XMLKeyedDecodingContainer(referencing: self, wrapping: keyed) + let container = XMLKeyedDecodingContainer(referencing: self, wrapping: keyed) return KeyedDecodingContainer(container) } @@ -352,7 +378,7 @@ class _XMLDecoder: Decoder { let unkeyed = (topContainer as? SharedBox) ?? SharedBox(UnkeyedBox([topContainer])) - return _XMLUnkeyedDecodingContainer(referencing: self, wrapping: unkeyed) + return XMLUnkeyedDecodingContainer(referencing: self, wrapping: unkeyed) } public func singleValueContainer() throws -> SingleValueDecodingContainer { @@ -360,7 +386,7 @@ class _XMLDecoder: Decoder { } } -extension _XMLDecoder: SingleValueDecodingContainer { +extension XMLDecoderImplementation: SingleValueDecodingContainer { // MARK: SingleValueDecodingContainer Methods public func decodeNil() -> Bool { @@ -406,7 +432,7 @@ extension _XMLDecoder: SingleValueDecodingContainer { // MARK: - Concrete Value Representations -extension _XMLDecoder { +extension XMLDecoderImplementation { /// Returns the given box unboxed from a container. private func typedBox(_ box: Box, for valueType: T.Type) throws -> B { @@ -603,28 +629,37 @@ extension _XMLDecoder { return urlBox.unbox() } + private struct TypeMismatch: Error {} + func unbox(_ box: Box) throws -> T { - let decoded: T + let decoded: T? let type = T.self if type == Date.self || type == NSDate.self { let date: Date = try unbox(box) - decoded = date as! T + + decoded = date as? T } else if type == Data.self || type == NSData.self { let data: Data = try unbox(box) - decoded = data as! T + decoded = data as? T } else if type == URL.self || type == NSURL.self { let data: URL = try unbox(box) - decoded = data as! T + decoded = data as? T } else if type == Decimal.self || type == NSDecimalNumber.self { let decimal: Decimal = try unbox(box) - decoded = decimal as! T + decoded = decimal as? T } else { storage.push(container: box) decoded = try type.init(from: self) storage.popContainer() } - return decoded + guard let result = decoded else { + throw DecodingError._typeMismatch( + at: codingPath, expectation: type, reality: box + ) + } + + return result } } diff --git a/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift b/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift index 69fde464..6c8d7571 100644 --- a/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift +++ b/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift @@ -10,7 +10,7 @@ import Foundation // MARK: - Decoding Storage -struct _XMLDecodingStorage { +struct XMLDecodingStorage { // MARK: Properties /// The container stack. diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift index 2afb85bb..b1f4d55d 100644 --- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift @@ -10,7 +10,7 @@ import Foundation // MARK: Decoding Containers -struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { +struct XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { typealias Key = K typealias KeyedContainer = SharedBox typealias UnkeyedContainer = SharedBox @@ -18,7 +18,7 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol // MARK: Properties /// A reference to the decoder we're reading from. - private let decoder: _XMLDecoder + private let decoder: XMLDecoderImplementation /// A reference to the container we're reading from. private let container: KeyedContainer @@ -29,7 +29,7 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: _XMLDecoder, wrapping container: KeyedContainer) { + init(referencing decoder: XMLDecoderImplementation, wrapping container: KeyedContainer) { self.decoder = decoder func mapKeys(_ container: KeyedContainer, closure: (String) -> String) -> KeyedContainer { @@ -59,7 +59,7 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } case let .custom(converter): self.container = mapKeys(container) { key in - let codingPath = decoder.codingPath + [_XMLKey(stringValue: key, intValue: nil)] + let codingPath = decoder.codingPath + [XMLKey(stringValue: key, intValue: nil)] return converter(codingPath).stringValue } } @@ -123,7 +123,9 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol return box?.isNull ?? true } - public func decode(_ type: T.Type, forKey key: Key) throws -> T { + public func decode( + _ type: T.Type, forKey key: Key + ) throws -> T { let attributeNotFound = container.withShared { keyedBox in keyedBox.attributes[key.stringValue] == nil } @@ -131,8 +133,9 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol keyedBox.elements[key.stringValue] == nil } - if let type = type as? AnyEmptySequence.Type, attributeNotFound, elementNotFound { - return type.init() as! T + if let type = type as? AnyEmptySequence.Type, attributeNotFound, + elementNotFound, let result = type.init() as? T { + return result } return try decodeConcrete(type, forKey: key) @@ -222,14 +225,14 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol )) } - let container: _XMLKeyedDecodingContainer + let container: XMLKeyedDecodingContainer if let keyedContainer = value as? KeyedContainer { - container = _XMLKeyedDecodingContainer( + container = XMLKeyedDecodingContainer( referencing: decoder, wrapping: keyedContainer ) } else if let keyedContainer = value as? KeyedBox { - container = _XMLKeyedDecodingContainer( + container = XMLKeyedDecodingContainer( referencing: decoder, wrapping: SharedBox(keyedContainer) ) @@ -264,9 +267,9 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } if let unkeyedContainer = value as? UnkeyedContainer { - return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) + return XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) } else if let unkeyedContainer = value as? UnkeyedBox { - return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: SharedBox(unkeyedContainer)) + return XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: SharedBox(unkeyedContainer)) } else { throw DecodingError._typeMismatch(at: codingPath, expectation: [Any].self, reality: value) } @@ -285,11 +288,11 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } let box: Box = elementOrNil ?? attributeOrNil ?? NullBox() - return _XMLDecoder(referencing: box, at: decoder.codingPath, options: decoder.options) + return XMLDecoderImplementation(referencing: box, at: decoder.codingPath, options: decoder.options) } public func superDecoder() throws -> Decoder { - return try _superDecoder(forKey: _XMLKey.super) + return try _superDecoder(forKey: XMLKey.super) } public func superDecoder(forKey key: Key) throws -> Decoder { diff --git a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift index c9b5612f..5a01f610 100644 --- a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift @@ -8,14 +8,14 @@ import Foundation -struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { +struct XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { typealias KeyedContainer = SharedBox typealias UnkeyedContainer = SharedBox // MARK: Properties /// A reference to the decoder we're reading from. - private let decoder: _XMLDecoder + private let decoder: XMLDecoderImplementation /// A reference to the container we're reading from. private let container: UnkeyedContainer @@ -29,7 +29,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: _XMLDecoder, wrapping container: UnkeyedContainer) { + init(referencing decoder: XMLDecoderImplementation, wrapping container: UnkeyedContainer) { self.decoder = decoder self.container = container codingPath = decoder.codingPath @@ -51,7 +51,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { public mutating func decodeNil() throws -> Bool { guard !isAtEnd else { throw DecodingError.valueNotFound(Any?.self, DecodingError.Context( - codingPath: decoder.codingPath + [_XMLKey(index: self.currentIndex)], + codingPath: decoder.codingPath + [XMLKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end." )) } @@ -76,16 +76,16 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { private mutating func decode( _ type: T.Type, - decode: (_XMLDecoder, Box) throws -> T? + decode: (XMLDecoderImplementation, Box) throws -> T? ) throws -> T { guard !isAtEnd else { throw DecodingError.valueNotFound(type, DecodingError.Context( - codingPath: decoder.codingPath + [_XMLKey(index: self.currentIndex)], + codingPath: decoder.codingPath + [XMLKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end." )) } - decoder.codingPath.append(_XMLKey(index: currentIndex)) + decoder.codingPath.append(XMLKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } let box: Box @@ -109,13 +109,14 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { defer { currentIndex += 1 } - if value == nil, let type = type as? AnyOptional.Type { - return type.init() as! T + if value == nil, let type = type as? AnyOptional.Type, + let result = type.init() as? T { + return result } guard let decoded: T = value else { throw DecodingError.valueNotFound(type, DecodingError.Context( - codingPath: decoder.codingPath + [_XMLKey(index: self.currentIndex)], + codingPath: decoder.codingPath + [XMLKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead." )) } @@ -126,7 +127,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { public mutating func nestedContainer( keyedBy _: NestedKey.Type ) throws -> KeyedDecodingContainer { - decoder.codingPath.append(_XMLKey(index: currentIndex)) + decoder.codingPath.append(XMLKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !isAtEnd else { @@ -155,7 +156,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } currentIndex += 1 - let container = _XMLKeyedDecodingContainer( + let container = XMLKeyedDecodingContainer( referencing: decoder, wrapping: keyedContainer ) @@ -163,7 +164,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { - decoder.codingPath.append(_XMLKey(index: currentIndex)) + decoder.codingPath.append(XMLKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !isAtEnd else { @@ -192,11 +193,11 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { } currentIndex += 1 - return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) + return XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) } public mutating func superDecoder() throws -> Decoder { - decoder.codingPath.append(_XMLKey(index: currentIndex)) + decoder.codingPath.append(XMLKey(index: currentIndex)) defer { self.decoder.codingPath.removeLast() } guard !isAtEnd else { @@ -210,8 +211,8 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { unkeyedBox[self.currentIndex] } currentIndex += 1 - return _XMLDecoder(referencing: value, - at: decoder.codingPath, - options: decoder.options) + return XMLDecoderImplementation(referencing: value, + at: decoder.codingPath, + options: decoder.options) } } diff --git a/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift b/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift index e22cae3f..fcda79c0 100644 --- a/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift +++ b/Sources/XMLCoder/Encoder/EncodingErrorExtension.swift @@ -26,7 +26,11 @@ extension EncodingError { valueDescription = "\(T.self).nan" } - let debugDescription = "Unable to encode \(valueDescription) directly in XML. Use XMLEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded." + let debugDescription = """ + Unable to encode \(valueDescription) directly in XML. \ + Use XMLEncoder.NonConformingFloatEncodingStrategy.convertToString \ + to specify how the value should be encoded. + """ return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription)) } } diff --git a/Sources/XMLCoder/Encoder/XMLEncoder.swift b/Sources/XMLCoder/Encoder/XMLEncoder.swift index 64787f3b..eeb12d0a 100644 --- a/Sources/XMLCoder/Encoder/XMLEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLEncoder.swift @@ -104,8 +104,12 @@ open class XMLEncoder { /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key to XML payload. /// - /// Capital characters are determined by testing membership in `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` (Unicode General Categories Lu and Lt). - /// The conversion to lower case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences. + /// Capital characters are determined by testing membership in + /// `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` + /// (Unicode General Categories Lu and Lt). + /// The conversion to lower case uses `Locale.system`, also known as + /// the ICU "root" locale. This means the result is consistent + /// regardless of the current user's locale and language preferences. /// /// Converting from camel case to snake case: /// 1. Splits words at the boundary of lower-case to upper-case @@ -118,9 +122,14 @@ open class XMLEncoder { /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted. case convertToSnakeCase - /// Provide a custom conversion to the key in the encoded XML from the keys specified by the encoded types. - /// The full path to the current encoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before encoding. - /// If the result of the conversion is a duplicate key, then only one value will be present in the result. + /// Provide a custom conversion to the key in the encoded XML from the + /// keys specified by the encoded types. + /// The full path to the current encoding position is provided for + /// context (in case you need to locate this key within the payload). + /// The returned key is used in place of the last component in the + /// coding path before encoding. + /// If the result of the conversion is a duplicate key, then only one + /// value will be present in the result. case custom((_ codingPath: [CodingKey]) -> CodingKey) static func _convertToSnakeCase(_ stringKey: String) -> String { @@ -129,7 +138,9 @@ open class XMLEncoder { } var words: [Range] = [] - // The general idea of this algorithm is to split words on transition from lower to upper case, then on transition of >1 upper case characters to lowercase + // The general idea of this algorithm is to split words on + // transition from lower to upper case, then on transition of >1 + // upper case characters to lowercase // // myProperty -> my_property // myURLProperty -> my_url_property @@ -151,7 +162,9 @@ open class XMLEncoder { break } - // Is the next lowercase letter more than 1 after the uppercase? If so, we encountered a group of uppercase letters that we should treat as its own word + // Is the next lowercase letter more than 1 after the uppercase? + // If so, we encountered a group of uppercase letters that we + // should treat as its own word let nextCharacterAfterCapital = stringKey.index(after: upperCaseRange.lowerBound) if lowerCaseRange.lowerBound == nextCharacterAfterCapital { // The next character after capital is a lower case character and therefore not a word boundary. @@ -224,7 +237,7 @@ open class XMLEncoder { open var userInfo: [CodingUserInfoKey: Any] = [:] /// Options set on the top-level encoder to pass down the encoding hierarchy. - struct _Options { + struct Options { let dateEncodingStrategy: DateEncodingStrategy let dataEncodingStrategy: DataEncodingStrategy let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy @@ -235,14 +248,14 @@ open class XMLEncoder { } /// The options set on the top-level encoder. - var options: _Options { - return _Options(dateEncodingStrategy: dateEncodingStrategy, - dataEncodingStrategy: dataEncodingStrategy, - nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy, - keyEncodingStrategy: keyEncodingStrategy, - nodeEncodingStrategy: nodeEncodingStrategy, - stringEncodingStrategy: stringEncodingStrategy, - userInfo: userInfo) + var options: Options { + return Options(dateEncodingStrategy: dateEncodingStrategy, + dataEncodingStrategy: dataEncodingStrategy, + nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy, + keyEncodingStrategy: keyEncodingStrategy, + nodeEncodingStrategy: nodeEncodingStrategy, + stringEncodingStrategy: stringEncodingStrategy, + userInfo: userInfo) } // MARK: - Constructing a XML Encoder @@ -257,10 +270,12 @@ open class XMLEncoder { /// - parameter value: The value to encode. /// - parameter withRootKey: the key used to wrap the encoded values. /// - returns: A new `Data` value containing the encoded XML data. - /// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`. + /// - throws: `EncodingError.invalidValue` if a non-conforming + /// floating-point value is encountered during encoding, and the encoding + /// strategy is `.throw`. /// - throws: An error if any value throws an error during encoding. open func encode(_ value: T, withRootKey rootKey: String, header: XMLHeader? = nil) throws -> Data { - let encoder = _XMLEncoder( + let encoder = XMLEncoderImplementation( options: options, nodeEncodings: [] ) @@ -268,12 +283,12 @@ open class XMLEncoder { let topLevel = try encoder.box(value) - let elementOrNone: _XMLElement? + let elementOrNone: XMLCoderElement? if let keyedBox = topLevel as? KeyedBox { - elementOrNone = _XMLElement(key: rootKey, box: keyedBox) + elementOrNone = XMLCoderElement(key: rootKey, box: keyedBox) } else if let unkeyedBox = topLevel as? UnkeyedBox { - elementOrNone = _XMLElement(key: rootKey, box: unkeyedBox) + elementOrNone = XMLCoderElement(key: rootKey, box: unkeyedBox) } else { fatalError("Unrecognized top-level element of type: \(type(of: topLevel))") } @@ -286,18 +301,21 @@ open class XMLEncoder { } let withCDATA = stringEncodingStrategy != .deferredToString - return element.toXMLString(with: header, withCDATA: withCDATA, formatting: outputFormatting).data(using: .utf8, allowLossyConversion: true)! + return element.toXMLString(with: header, + withCDATA: withCDATA, + formatting: outputFormatting) + .data(using: .utf8, allowLossyConversion: true)! } } -class _XMLEncoder: Encoder { +class XMLEncoderImplementation: Encoder { // MARK: Properties /// The encoder's storage. - var storage: _XMLEncodingStorage + var storage: XMLEncodingStorage /// Options set on the top-level encoder. - let options: XMLEncoder._Options + let options: XMLEncoder.Options /// The path to the current point in encoding. public var codingPath: [CodingKey] @@ -313,12 +331,12 @@ class _XMLEncoder: Encoder { /// Initializes `self` with the given top-level encoder options. init( - options: XMLEncoder._Options, + options: XMLEncoder.Options, nodeEncodings: [(CodingKey) -> XMLEncoder.NodeEncoding], codingPath: [CodingKey] = [] ) { self.options = options - storage = _XMLEncodingStorage() + storage = XMLEncodingStorage() self.codingPath = codingPath self.nodeEncodings = nodeEncodings } @@ -327,12 +345,19 @@ class _XMLEncoder: Encoder { /// /// `true` if an element has not yet been encoded at this coding path; `false` otherwise. var canEncodeNewValue: Bool { - // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). - // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. - // If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition. + // Every time a new value gets encoded, the key it's encoded for is + // pushed onto the coding path (even if it's a nil key from an unkeyed container). + // At the same time, every time a container is requested, a new value + // gets pushed onto the storage stack. + // If there are more values on the storage stack than on the coding path, + // it means the value is requesting more than one container, which + // violates the precondition. // - // This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path. - // Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here). + // This means that anytime something that can request a new container + // goes onto the stack, we MUST push a key onto the coding path. + // Things which will not request containers do not need to have the + // coding path extended for them (but it doesn't matter if it is, + // because they will not reach here). return storage.count == codingPath.count } @@ -352,7 +377,7 @@ class _XMLEncoder: Encoder { topContainer = container } - let container = _XMLKeyedEncodingContainer(referencing: self, codingPath: codingPath, wrapping: topContainer) + let container = XMLKeyedEncodingContainer(referencing: self, codingPath: codingPath, wrapping: topContainer) return KeyedEncodingContainer(container) } @@ -370,7 +395,7 @@ class _XMLEncoder: Encoder { topContainer = container } - return _XMLUnkeyedEncodingContainer(referencing: self, codingPath: codingPath, wrapping: topContainer) + return XMLUnkeyedEncodingContainer(referencing: self, codingPath: codingPath, wrapping: topContainer) } public func singleValueContainer() -> SingleValueEncodingContainer { @@ -378,7 +403,7 @@ class _XMLEncoder: Encoder { } } -extension _XMLEncoder: SingleValueEncodingContainer { +extension XMLEncoderImplementation: SingleValueEncodingContainer { // MARK: - SingleValueEncodingContainer Methods func assertCanEncodeNewValue() { @@ -466,7 +491,7 @@ extension _XMLEncoder: SingleValueEncodingContainer { } } -extension _XMLEncoder { +extension XMLEncoderImplementation { /// Returns the given value boxed in a container appropriate for pushing onto the container stack. func box() -> SimpleBox { return NullBox() @@ -492,7 +517,9 @@ extension _XMLEncoder { guard value.isInfinite || value.isNaN else { return FloatBox(value) } - guard case let .convertToString(positiveInfinity: posInfString, negativeInfinity: negInfString, nan: nanString) = options.nonConformingFloatEncodingStrategy else { + guard case let .convertToString(positiveInfinity: posInfString, + negativeInfinity: negInfString, + nan: nanString) = options.nonConformingFloatEncodingStrategy else { throw EncodingError._invalidFloatingPointValue(value, at: codingPath) } if value == T.infinity { @@ -556,15 +583,19 @@ extension _XMLEncoder { return URLBox(value) } - internal func box(_ value: T) throws -> Box { - if T.self == Date.self || T.self == NSDate.self { - return try self.box(value as! Date) - } else if T.self == Data.self || T.self == NSData.self { - return try self.box(value as! Data) - } else if T.self == URL.self || T.self == NSURL.self { - return self.box(value as! URL) - } else if T.self == Decimal.self || T.self == NSDecimalNumber.self { - return self.box(value as! Decimal) + func box(_ value: T) throws -> Box { + if T.self == Date.self || T.self == NSDate.self, + let value = value as? Date { + return try box(value) + } else if T.self == Data.self || T.self == NSData.self, + let value = value as? Data { + return try box(value) + } else if T.self == URL.self || T.self == NSURL.self, + let value = value as? URL { + return box(value) + } else if T.self == Decimal.self || T.self == NSDecimalNumber.self, + let value = value as? Decimal { + return box(value) } let depth = storage.count @@ -575,10 +606,10 @@ extension _XMLEncoder { return KeyedBox() } - let box = storage.popContainer() + let lastContainer = storage.popContainer() - guard let sharedBox = box as? SharedBoxProtocol else { - return box + guard let sharedBox = lastContainer as? SharedBoxProtocol else { + return lastContainer } return sharedBox.unbox() diff --git a/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift b/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift index 78ec2a56..8a6f4c9e 100644 --- a/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift +++ b/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift @@ -1,4 +1,3 @@ - // // XMLEncodingStorage.swift // XMLCoder @@ -11,7 +10,7 @@ import Foundation // MARK: - Encoding Storage and Containers -struct _XMLEncodingStorage { +struct XMLEncodingStorage { // MARK: Properties /// The container stack. diff --git a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift index 4da777b0..7279a6b7 100644 --- a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift @@ -7,13 +7,13 @@ import Foundation -struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol { +struct XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol { typealias Key = K // MARK: Properties /// A reference to the encoder we're writing to. - private let encoder: _XMLEncoder + private let encoder: XMLEncoderImplementation /// A reference to the container we're writing to. private var container: SharedBox @@ -24,7 +24,11 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol // MARK: - Initialization /// Initializes `self` with the given references. - init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: SharedBox) { + init( + referencing encoder: XMLEncoderImplementation, + codingPath: [CodingKey], + wrapping container: SharedBox + ) { self.encoder = encoder self.codingPath = codingPath self.container = container @@ -37,8 +41,9 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol case .useDefaultKeys: return key case .convertToSnakeCase: - let newKeyString = XMLEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue) - return _XMLKey(stringValue: newKeyString, intValue: key.intValue) + let newKeyString = XMLEncoder.KeyEncodingStrategy + ._convertToSnakeCase(key.stringValue) + return XMLKey(stringValue: newKeyString, intValue: key.intValue) case let .custom(converter): return converter(codingPath + [key]) } @@ -52,7 +57,10 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol } } - public mutating func encode(_ value: T, forKey key: Key) throws { + public mutating func encode( + _ value: T, + forKey key: Key + ) throws { return try encode(value, forKey: key) { encoder, value in try encoder.box(value) } @@ -61,14 +69,16 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol private mutating func encode( _ value: T, forKey key: Key, - encode: (_XMLEncoder, T) throws -> Box + encode: (XMLEncoderImplementation, T) throws -> Box ) throws { defer { _ = self.encoder.nodeEncodings.removeLast() self.encoder.codingPath.removeLast() } guard let strategy = self.encoder.nodeEncodings.last else { - preconditionFailure("Attempt to access node encoding strategy from empty stack.") + preconditionFailure( + "Attempt to access node encoding strategy from empty stack." + ) } encoder.codingPath.append(key) let nodeEncodings = encoder.options.nodeEncodingStrategy.nodeEncodings( @@ -95,7 +105,10 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol } } - public mutating func nestedContainer(keyedBy _: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + public mutating func nestedContainer( + keyedBy _: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer { let sharedKeyed = SharedBox(KeyedBox()) self.container.withShared { container in @@ -105,11 +118,17 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol codingPath.append(key) defer { self.codingPath.removeLast() } - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedKeyed) + let container = XMLKeyedEncodingContainer( + referencing: encoder, + codingPath: codingPath, + wrapping: sharedKeyed + ) return KeyedEncodingContainer(container) } - public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + public mutating func nestedUnkeyedContainer( + forKey key: Key + ) -> UnkeyedEncodingContainer { let sharedUnkeyed = SharedBox(UnkeyedBox()) container.withShared { container in @@ -118,14 +137,28 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol codingPath.append(key) defer { self.codingPath.removeLast() } - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedUnkeyed) + return XMLUnkeyedEncodingContainer( + referencing: encoder, + codingPath: codingPath, + wrapping: sharedUnkeyed + ) } public mutating func superEncoder() -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, key: _XMLKey.super, convertedKey: _converted(_XMLKey.super), wrapping: container) + return XMLReferencingEncoder( + referencing: encoder, + key: XMLKey.super, + convertedKey: _converted(XMLKey.super), + wrapping: container + ) } public mutating func superEncoder(forKey key: Key) -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, key: key, convertedKey: _converted(key), wrapping: container) + return XMLReferencingEncoder( + referencing: encoder, + key: key, + convertedKey: _converted(key), + wrapping: container + ) } } diff --git a/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift b/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift index 096ac26d..3209589f 100644 --- a/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift @@ -8,11 +8,13 @@ import Foundation -// MARK: - _XMLReferencingEncoder - -/// _XMLReferencingEncoder is a special subclass of _XMLEncoder which has its own storage, but references the contents of a different encoder. -/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container). -class _XMLReferencingEncoder: _XMLEncoder { +/// XMLReferencingEncoder is a special subclass of _XMLEncoder which has its +/// own storage, but references the contents of a different encoder. +/// It's used in superEncoder(), which returns a new encoder for encoding a +// superclass -- the lifetime of the encoder should not escape the scope it's +/// created in, but it doesn't necessarily know when it's done being used +/// (to write to the original container). +class XMLReferencingEncoder: XMLEncoderImplementation { // MARK: Reference types. /// The type of container we're referencing. @@ -27,7 +29,7 @@ class _XMLReferencingEncoder: _XMLEncoder { // MARK: - Properties /// The encoder we're referencing. - let encoder: _XMLEncoder + let encoder: XMLEncoderImplementation /// The container reference itself. private let reference: Reference @@ -36,7 +38,7 @@ class _XMLReferencingEncoder: _XMLEncoder { /// Initializes `self` by referencing the given array container in the given encoder. init( - referencing encoder: _XMLEncoder, + referencing encoder: XMLEncoderImplementation, at index: Int, wrapping sharedUnkeyed: SharedBox ) { @@ -48,12 +50,12 @@ class _XMLReferencingEncoder: _XMLEncoder { codingPath: encoder.codingPath ) - codingPath.append(_XMLKey(index: index)) + codingPath.append(XMLKey(index: index)) } /// Initializes `self` by referencing the given dictionary container in the given encoder. init( - referencing encoder: _XMLEncoder, + referencing encoder: XMLEncoderImplementation, key: CodingKey, convertedKey: CodingKey, wrapping sharedKeyed: SharedBox diff --git a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift index d48b7937..5309e91c 100644 --- a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift @@ -7,11 +7,11 @@ import Foundation -struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { +struct XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { // MARK: Properties /// A reference to the encoder we're writing to. - private let encoder: _XMLEncoder + private let encoder: XMLEncoderImplementation /// A reference to the container we're writing to. private let container: SharedBox @@ -27,7 +27,11 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { // MARK: - Initialization /// Initializes `self` with the given references. - init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: SharedBox) { + init( + referencing encoder: XMLEncoderImplementation, + codingPath: [CodingKey], + wrapping container: SharedBox + ) { self.encoder = encoder self.codingPath = codingPath self.container = container @@ -49,17 +53,19 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { private mutating func encode( _ value: T, - encode: (_XMLEncoder, T) throws -> Box + encode: (XMLEncoderImplementation, T) throws -> Box ) rethrows { - encoder.codingPath.append(_XMLKey(index: count)) + encoder.codingPath.append(XMLKey(index: count)) defer { self.encoder.codingPath.removeLast() } try container.withShared { container in container.append(try encode(encoder, value)) } } - public mutating func nestedContainer(keyedBy _: NestedKey.Type) -> KeyedEncodingContainer { - codingPath.append(_XMLKey(index: count)) + public mutating func nestedContainer( + keyedBy _: NestedKey.Type + ) -> KeyedEncodingContainer { + codingPath.append(XMLKey(index: count)) defer { self.codingPath.removeLast() } let sharedKeyed = SharedBox(KeyedBox()) @@ -67,12 +73,16 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { container.append(sharedKeyed) } - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedKeyed) + let container = XMLKeyedEncodingContainer( + referencing: encoder, + codingPath: codingPath, + wrapping: sharedKeyed + ) return KeyedEncodingContainer(container) } public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - codingPath.append(_XMLKey(index: count)) + codingPath.append(XMLKey(index: count)) defer { self.codingPath.removeLast() } let sharedUnkeyed = SharedBox(UnkeyedBox()) @@ -80,10 +90,18 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { container.append(sharedUnkeyed) } - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedUnkeyed) + return XMLUnkeyedEncodingContainer( + referencing: encoder, + codingPath: codingPath, + wrapping: sharedUnkeyed + ) } public mutating func superEncoder() -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, at: count, wrapping: container) + return XMLReferencingEncoder( + referencing: encoder, + at: count, + wrapping: container + ) } } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift index fff99f9b..b9c89b43 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLElementTests.swift @@ -10,7 +10,7 @@ import XCTest class XMLElementTests: XCTestCase { func testInitNull() { - let null = _XMLElement(key: "foo") + let null = XMLCoderElement(key: "foo") XCTAssertEqual(null.key, "foo") XCTAssertNil(null.value) @@ -19,7 +19,7 @@ class XMLElementTests: XCTestCase { } func testInitUnkeyed() { - let keyed = _XMLElement(key: "foo", box: UnkeyedBox()) + let keyed = XMLCoderElement(key: "foo", box: UnkeyedBox()) XCTAssertEqual(keyed.key, "foo") XCTAssertNil(keyed.value) @@ -28,7 +28,7 @@ class XMLElementTests: XCTestCase { } func testInitKeyed() { - let keyed = _XMLElement(key: "foo", box: KeyedBox( + let keyed = XMLCoderElement(key: "foo", box: KeyedBox( elements: [:], attributes: ["baz": NullBox(), "blee": IntBox(42)] )) @@ -40,7 +40,7 @@ class XMLElementTests: XCTestCase { } func testInitSimple() { - let keyed = _XMLElement(key: "foo", box: StringBox("bar")) + let keyed = XMLCoderElement(key: "foo", box: StringBox("bar")) XCTAssertEqual(keyed.key, "foo") XCTAssertEqual(keyed.value, "bar") diff --git a/Tests/XMLCoderTests/Auxiliary/XMLKeyTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLKeyTests.swift index 0719b2af..f9c162a1 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLKeyTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLKeyTests.swift @@ -10,19 +10,19 @@ import XCTest class XMLKeyTests: XCTestCase { func testInitStringValue() { - let key = _XMLKey(stringValue: "foo") + let key = XMLKey(stringValue: "foo") XCTAssertEqual(key?.stringValue, "foo") XCTAssertEqual(key?.intValue, nil) } func testInitIntValue() { - let key = _XMLKey(intValue: 42) + let key = XMLKey(intValue: 42) XCTAssertEqual(key?.stringValue, "42") XCTAssertEqual(key?.intValue, 42) } func testInitStringValueIntValue() { - let key = _XMLKey(stringValue: "foo", intValue: 42) + let key = XMLKey(stringValue: "foo", intValue: 42) XCTAssertEqual(key.stringValue, "foo") XCTAssertEqual(key.intValue, 42) } diff --git a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift index 1b8a2879..958f3d1e 100644 --- a/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift +++ b/Tests/XMLCoderTests/Auxiliary/XMLStackParserTests.swift @@ -10,7 +10,7 @@ import XCTest class XMLStackParserTests: XCTestCase { func testParseWith() throws { - let parser = _XMLStackParser() + let parser = XMLStackParser() let xmlString = """ @@ -21,17 +21,17 @@ class XMLStackParserTests: XCTestCase { """ let xmlData = xmlString.data(using: .utf8)! - let root: _XMLElement? = try parser.parse(with: xmlData, - errorContextLength: 0) + let root: XMLCoderElement? = try parser.parse(with: xmlData, + errorContextLength: 0) - let expected = _XMLElement( + let expected = XMLCoderElement( key: "container", elements: [ - _XMLElement( + XMLCoderElement( key: "value", value: "42" ), - _XMLElement( + XMLCoderElement( key: "data", value: "lorem ipsum" ), @@ -41,7 +41,7 @@ class XMLStackParserTests: XCTestCase { } func testParseWithThrow() throws { - let parser = _XMLStackParser() + let parser = XMLStackParser() let xmlString = "lorem ipsum" let xmlData = xmlString.data(using: .utf8)! diff --git a/Tests/XMLCoderTests/RJISample.swift b/Tests/XMLCoderTests/RJISample.swift index 54c51143..c16620d8 100644 --- a/Tests/XMLCoderTests/RJISample.swift +++ b/Tests/XMLCoderTests/RJISample.swift @@ -5,6 +5,8 @@ // Created by Max Desiatov on 17/12/2018. // +// swiftlint:disable line_length file_length + let rjiSampleXML = """