-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathPropertyList.swift
152 lines (135 loc) · 4.1 KB
/
PropertyList.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Foundation
/// A enum representing data types for legacy PropertyList type.
/// Note that the `identifier` enum is not strictly necessary,
/// but useful to semantically distinguish the strings that
/// represents object identifiers from those that are just data.
/// see: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
public enum PropertyList {
case identifier(String)
case string(String)
case array([PropertyList])
case dictionary([String: PropertyList])
var string: String? {
if case .string(let string) = self {
return string
}
return nil
}
var array: [PropertyList]? {
if case .array(let array) = self {
return array
}
return nil
}
}
extension PropertyList: CustomStringConvertible {
public var description: String {
String(decoding: serialize(), as: UTF8.self)
}
}
extension PropertyList {
/// Serializes the Plist enum.
public func serialize() -> Data {
var writer = UTF8Writer()
writePlistRepresentation(to: &writer)
return Data(writer.bytes)
}
}
fileprivate extension PropertyList {
struct UTF8Writer {
private(set) var level: Int = 0
private(set) var bytes: [UInt8] = []
init() {
self += "// !$*UTF8*$!\n"
}
mutating func withIndent(body: (inout Self) -> Void) {
level += 1
body(&self)
level -= 1
}
mutating func append(_ byte: UInt8) {
bytes.append(byte)
}
static func += (writer: inout UTF8Writer, str: StaticString) {
str.withUTF8Buffer { utf8 in
writer.bytes += utf8
}
}
@_disfavoredOverload
static func += (writer: inout UTF8Writer, str: String) {
writer.bytes += str.utf8
}
mutating func indent() {
for _ in 0 ..< level {
self += " "
}
}
/// Appends the given string, with instances of quote (") and backward slash
/// (\) characters escaped with a backslash.
mutating func appendEscaped(_ string: String) {
for char in string.utf8 {
if char == UInt8(ascii: "\\") || char == UInt8(ascii: "\"") {
append(UInt8(ascii: "\\"))
}
append(char)
}
}
}
/// Private function to generate OPENSTEP-style plist representation.
func writePlistRepresentation(to writer: inout UTF8Writer) {
// Do the appropriate thing for each type of plist node.
switch self {
case .identifier(let ident):
// FIXME: we should assert that the identifier doesn't need quoting
writer += ident
case .string(let string):
writer += "\""
writer.appendEscaped(string)
writer += "\""
case .array(let array):
writer += "(\n"
writer.withIndent { writer in
for (i, item) in array.enumerated() {
writer.indent()
item.writePlistRepresentation(to: &writer)
writer += (i != array.count - 1) ? ",\n" : "\n"
}
}
writer.indent()
writer += ")"
case .dictionary(let dict):
let dict = dict.sorted(by: {
// Make `isa` sort first (just for readability purposes).
switch ($0.key, $1.key) {
case ("isa", "isa"): return false
case ("isa", _): return true
case (_, "isa"): return false
default: return $0.key < $1.key
}
})
writer += "{\n"
writer.withIndent { writer in
for (key, value) in dict {
writer.indent()
writer += key
writer += " = "
value.writePlistRepresentation(to: &writer)
writer += ";\n"
}
}
writer.indent()
writer += "}"
}
}
}