mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This was originally a separate library since it was copied directly from the legacy SwiftPM library. However it's since been changed a bunch, and ought to be able leverage SwiftXcodeGen utilities such as RelativePath. Fold it into SwiftXcodeGen. I considered splitting out the utility code from SwiftXcodeGen into a new library, but unfortunately that currently regresses performance (even with max CMO). The module organization doesn't really currently matter since we don't expose any products.
153 lines
4.1 KiB
Swift
153 lines
4.1 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 += "}"
|
|
}
|
|
}
|
|
}
|