Skip to content

Commit

Permalink
Add prettyPrintIndentation property on XMLEncoder (CoreOffice#186)
Browse files Browse the repository at this point in the history
New property can take `XMLEncoder.PrettyPrintIndentation` values such as `.tabs(1)` or `.spaces(2)`.

Resolve CoreOffice#183.
  • Loading branch information
MaxDesiatov authored May 31, 2020
1 parent 388ba82 commit 0d1c1b9
Show file tree
Hide file tree
Showing 31 changed files with 256 additions and 121 deletions.
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
"label": "swift test",
"type": "shell",
"command": "swift test"
"command": "swift test --parallel"
}
]
}
6 changes: 6 additions & 0 deletions Dangerfile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@
import Danger

SwiftLint.lint(inline: true, configFile: ".swiftlint.yml", strict: true)

let danger = Danger()

print("Calling SwiftFormat...")

danger.utils.exec("swiftformat", arguments: ["."])
48 changes: 32 additions & 16 deletions Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,30 @@ struct XMLCoderElement: Equatable {
return KeyedBox(elements: elements, attributes: attributes)
}

func toXMLString(with header: XMLHeader? = nil,
formatting: XMLEncoder.OutputFormatting) -> String {
func toXMLString(
with header: XMLHeader? = nil,
formatting: XMLEncoder.OutputFormatting,
indentation: XMLEncoder.PrettyPrintIndentation
) -> String {
if let header = header, let headerXML = header.toXML() {
return headerXML + _toXMLString(formatting: formatting)
return headerXML + _toXMLString(formatting, indentation)
}
return _toXMLString(formatting: formatting)
return _toXMLString(formatting, indentation)
}

private func formatUnsortedXMLElements(
_ string: inout String,
_ level: Int,
_ formatting: XMLEncoder.OutputFormatting,
_ indentation: XMLEncoder.PrettyPrintIndentation,
_ prettyPrinted: Bool
) {
formatXMLElements(
from: elements,
into: &string,
at: level,
formatting: formatting,
indentation: indentation,
prettyPrinted: prettyPrinted
)
}
Expand All @@ -149,6 +154,7 @@ struct XMLCoderElement: Equatable {
for element: XMLCoderElement,
at level: Int,
formatting: XMLEncoder.OutputFormatting,
indentation: XMLEncoder.PrettyPrintIndentation,
prettyPrinted: Bool
) -> String {
if let stringValue = element.stringValue {
Expand All @@ -160,9 +166,7 @@ struct XMLCoderElement: Equatable {
}

var string = ""
string += element._toXMLString(
indented: level + 1, formatting: formatting
)
string += element._toXMLString(indented: level + 1, formatting, indentation)
string += prettyPrinted ? "\n" : ""
return string
}
Expand All @@ -171,12 +175,14 @@ struct XMLCoderElement: Equatable {
_ string: inout String,
_ level: Int,
_ formatting: XMLEncoder.OutputFormatting,
_ indentation: XMLEncoder.PrettyPrintIndentation,
_ prettyPrinted: Bool
) {
formatXMLElements(from: elements.sorted { $0.key < $1.key },
into: &string,
at: level,
formatting: formatting,
indentation: indentation,
prettyPrinted: prettyPrinted)
}

Expand All @@ -198,12 +204,14 @@ struct XMLCoderElement: Equatable {
into string: inout String,
at level: Int,
formatting: XMLEncoder.OutputFormatting,
indentation: XMLEncoder.PrettyPrintIndentation,
prettyPrinted: Bool
) {
for element in elements {
string += elementString(for: element,
at: level,
formatting: formatting,
indentation: indentation,
prettyPrinted: prettyPrinted && !containsTextNodes)
}
}
Expand Down Expand Up @@ -231,30 +239,38 @@ struct XMLCoderElement: Equatable {

private func formatXMLElements(
_ formatting: XMLEncoder.OutputFormatting,
_ indentation: XMLEncoder.PrettyPrintIndentation,
_ string: inout String,
_ level: Int,
_ prettyPrinted: Bool
) {
if formatting.contains(.sortedKeys) {
formatSortedXMLElements(
&string, level, formatting, prettyPrinted
&string, level, formatting, indentation, prettyPrinted
)
return
}
formatUnsortedXMLElements(
&string, level, formatting, prettyPrinted
&string, level, formatting, indentation, prettyPrinted
)
}

private func _toXMLString(
indented level: Int = 0,
formatting: XMLEncoder.OutputFormatting
_ formatting: XMLEncoder.OutputFormatting,
_ indentation: XMLEncoder.PrettyPrintIndentation
) -> String {
let prettyPrinted = formatting.contains(.prettyPrinted)
let indentation = String(
repeating: " ", count: (prettyPrinted ? level : 0) * 4
)
var string = indentation
let prefix: String
switch indentation {
case let .spaces(count) where prettyPrinted:
prefix = String(repeating: " ", count: level * count)
case let .tabs(count) where prettyPrinted:
prefix = String(repeating: "\t", count: level * count)
default:
prefix = ""
}
var string = prefix

if !key.isEmpty {
string += "<\(key)"
Expand All @@ -267,9 +283,9 @@ struct XMLCoderElement: Equatable {
if !key.isEmpty {
string += prettyPrintElements ? ">\n" : ">"
}
formatXMLElements(formatting, &string, level, prettyPrintElements)
formatXMLElements(formatting, indentation, &string, level, prettyPrintElements)

if prettyPrintElements { string += indentation }
if prettyPrintElements { string += prefix }
if !key.isEmpty {
string += "</\(key)>"
}
Expand Down
16 changes: 14 additions & 2 deletions Sources/XMLCoder/Encoder/XMLEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ open class XMLEncoder {
public static let sortedKeys = OutputFormatting(rawValue: 1 << 1)
}

/// The identation to use when XML is pretty-printed.
public enum PrettyPrintIndentation {
case spaces(Int)
case tabs(Int)
}

/// A node's encoding type
public enum NodeEncoding {
case attribute
Expand Down Expand Up @@ -264,6 +270,9 @@ open class XMLEncoder {
/// The output format to produce. Defaults to `[]`.
open var outputFormatting: OutputFormatting = []

/// The indentation to use when XML is printed. Defaults to `.spaces(4)`.
open var prettyPrintIndentation: PrettyPrintIndentation = .spaces(4)

/// The strategy to use in encoding dates. Defaults to `.deferredToDate`.
open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate

Expand Down Expand Up @@ -373,8 +382,11 @@ open class XMLEncoder {
))
}

return element.toXMLString(with: header, formatting: outputFormatting)
.data(using: .utf8, allowLossyConversion: true)!
return element.toXMLString(
with: header,
formatting: outputFormatting,
indentation: prettyPrintIndentation
).data(using: .utf8, allowLossyConversion: true)!
}

// MARK: - TopLevelEncoder
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
78 changes: 78 additions & 0 deletions Tests/XMLCoderTests/PrettyPrintTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) 2020 XMLCoder contributors
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

import XCTest
import XMLCoder

private struct TopContainer: Encodable {
let nested: NestedContainer
}

private struct NestedContainer: Encodable {
let values: [String]
}

final class PrettyPrintTest: XCTestCase {
private let testContainer = TopContainer(nested: NestedContainer(values: ["foor", "bar"]))

func testDefaultIndentation() throws {
let encoder = XMLEncoder()
encoder.outputFormatting = [.prettyPrinted]

let encoded = try encoder.encode(testContainer)

XCTAssertEqual(
String(data: encoded, encoding: .utf8)!,
"""
<TopContainer>
<nested>
<values>foor</values>
<values>bar</values>
</nested>
</TopContainer>
"""
)
}

func testSpaces() throws {
let encoder = XMLEncoder()
encoder.outputFormatting = [.prettyPrinted]
encoder.prettyPrintIndentation = .spaces(3)

let encoded = try encoder.encode(testContainer)

XCTAssertEqual(
String(data: encoded, encoding: .utf8)!,
"""
<TopContainer>
<nested>
<values>foor</values>
<values>bar</values>
</nested>
</TopContainer>
"""
)
}

func testTabs() throws {
let encoder = XMLEncoder()
encoder.outputFormatting = [.prettyPrinted]
encoder.prettyPrintIndentation = .tabs(2)

let encoded = try encoder.encode(testContainer)

XCTAssertEqual(
String(data: encoded, encoding: .utf8)!,
"""
<TopContainer>
\t\t<nested>
\t\t\t\t<values>foor</values>
\t\t\t\t<values>bar</values>
\t\t</nested>
</TopContainer>
"""
)
}
}
13 changes: 13 additions & 0 deletions Tests/XMLCoderTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ extension CDATATest {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__CDATATest = [
("testCDataTypes", testCDataTypes),
("testXML", testXML),
]
}
Expand Down Expand Up @@ -585,6 +586,17 @@ extension PlantTest {
]
}

extension PrettyPrintTest {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__PrettyPrintTest = [
("testDefaultIndentation", testDefaultIndentation),
("testSpaces", testSpaces),
("testTabs", testTabs),
]
}

extension QuoteDecodingTest {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
Expand Down Expand Up @@ -898,6 +910,7 @@ public func __allTests() -> [XCTestCaseEntry] {
testCase(NullTests.__allTests__NullTests),
testCase(OptionalTests.__allTests__OptionalTests),
testCase(PlantTest.__allTests__PlantTest),
testCase(PrettyPrintTest.__allTests__PrettyPrintTest),
testCase(QuoteDecodingTest.__allTests__QuoteDecodingTest),
testCase(RJITest.__allTests__RJITest),
testCase(RelationshipsTest.__allTests__RelationshipsTest),
Expand Down
Loading

0 comments on commit 0d1c1b9

Please sign in to comment.