mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 10:30:42 +00:00
275 lines
9.1 KiB
Swift
275 lines
9.1 KiB
Swift
import Foundation
|
|
|
|
public final class PresentationThemeEncoder {
|
|
public init() {
|
|
}
|
|
|
|
public func encode<T: Encodable>(_ value: T) throws -> String {
|
|
let encoding = PresentationThemeEncoding()
|
|
try value.encode(to: encoding)
|
|
return encoding.data.formatted
|
|
}
|
|
|
|
private func stringsFormat(from strings: [(String, String)]) -> String {
|
|
let strings = strings.map { "\($0.0): \($0.1)" }
|
|
return strings.joined(separator: "\n")
|
|
}
|
|
}
|
|
|
|
private func renderNodes(string: inout String, nodes: [PresentationThemeEncoding.Node], level: Int = 0) {
|
|
for node in nodes {
|
|
if level > 1 {
|
|
string.append(String(repeating: " ", count: level - 1))
|
|
}
|
|
switch node.value {
|
|
case let .string(value):
|
|
if let key = node.key {
|
|
string.append("\(key): \(value)\n")
|
|
}
|
|
case let .subnode(nodes):
|
|
if let key = node.key {
|
|
string.append("\(key):\n")
|
|
}
|
|
renderNodes(string: &string, nodes: nodes, level: level + 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
fileprivate class PresentationThemeEncoding: Encoder {
|
|
fileprivate enum NodeValue {
|
|
case string(String)
|
|
case subnode([Node])
|
|
}
|
|
|
|
fileprivate final class Node {
|
|
var key: String?
|
|
var value: NodeValue
|
|
|
|
init(key: String? = nil, value: NodeValue) {
|
|
self.key = key
|
|
self.value = value
|
|
}
|
|
}
|
|
|
|
fileprivate final class Data {
|
|
private(set) var rootNode = Node(value: .subnode([]))
|
|
|
|
func encode(key codingKey: [CodingKey], value: String) {
|
|
var currentNode: Node = self.rootNode
|
|
for i in 0 ..< codingKey.count {
|
|
let key = codingKey[i].stringValue
|
|
var found = false
|
|
switch currentNode.value {
|
|
case var .subnode(nodes):
|
|
for node in nodes {
|
|
if node.key == key {
|
|
currentNode = node
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
let newNode: Node
|
|
if i == codingKey.count - 1 {
|
|
newNode = Node(key: key, value: .string(value))
|
|
} else {
|
|
newNode = Node(key: key, value: .subnode([]))
|
|
}
|
|
nodes.append(newNode)
|
|
currentNode.value = .subnode(nodes)
|
|
currentNode = newNode
|
|
}
|
|
case .string:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
var formatted: String {
|
|
var result = ""
|
|
renderNodes(string: &result, nodes: [self.rootNode])
|
|
return result
|
|
}
|
|
}
|
|
|
|
fileprivate var data: Data
|
|
var codingPath: [CodingKey] = []
|
|
let userInfo: [CodingUserInfoKey : Any] = [:]
|
|
|
|
init(to encodedData: Data = Data()) {
|
|
self.data = encodedData
|
|
}
|
|
|
|
func container<Key: CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
|
|
var container = StringsKeyedEncoding<Key>(to: data)
|
|
container.codingPath = self.codingPath
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
func unkeyedContainer() -> UnkeyedEncodingContainer {
|
|
var container = StringsUnkeyedEncoding(to: data)
|
|
container.codingPath = self.codingPath
|
|
return container
|
|
}
|
|
|
|
func singleValueContainer() -> SingleValueEncodingContainer {
|
|
var container = StringsSingleValueEncoding(to: data)
|
|
container.codingPath = self.codingPath
|
|
return container
|
|
}
|
|
}
|
|
|
|
fileprivate struct StringsKeyedEncoding<Key: CodingKey>: KeyedEncodingContainerProtocol {
|
|
private let data: PresentationThemeEncoding.Data
|
|
var codingPath: [CodingKey] = []
|
|
|
|
init(to data: PresentationThemeEncoding.Data) {
|
|
self.data = data
|
|
}
|
|
|
|
mutating func encodeNil(forKey key: Key) throws {
|
|
}
|
|
|
|
mutating func encode(_ value: Bool, forKey key: Key) throws {
|
|
self.data.encode(key: codingPath + [key], value: value.description)
|
|
}
|
|
|
|
mutating func encode(_ value: String, forKey key: Key) throws {
|
|
self.data.encode(key: codingPath + [key], value: value)
|
|
}
|
|
|
|
mutating func encode(_ value: Int32, forKey key: Key) throws {
|
|
self.data.encode(key: codingPath + [key], value: value.description)
|
|
}
|
|
|
|
mutating func encode<T: Encodable>(_ value: T, forKey key: Key) throws {
|
|
let stringsEncoding = PresentationThemeEncoding(to: self.data)
|
|
stringsEncoding.codingPath = self.codingPath + [key]
|
|
try value.encode(to: stringsEncoding)
|
|
}
|
|
|
|
mutating func nestedContainer<NestedKey: CodingKey>(
|
|
keyedBy keyType: NestedKey.Type,
|
|
forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
|
|
var container = StringsKeyedEncoding<NestedKey>(to: self.data)
|
|
container.codingPath = self.codingPath + [key]
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
|
var container = StringsUnkeyedEncoding(to: data)
|
|
container.codingPath = self.codingPath + [key]
|
|
return container
|
|
}
|
|
|
|
mutating func superEncoder() -> Encoder {
|
|
let superKey = Key(stringValue: "super")!
|
|
return superEncoder(forKey: superKey)
|
|
}
|
|
|
|
mutating func superEncoder(forKey key: Key) -> Encoder {
|
|
let stringsEncoding = PresentationThemeEncoding(to: self.data)
|
|
stringsEncoding.codingPath = self.codingPath + [key]
|
|
return stringsEncoding
|
|
}
|
|
}
|
|
|
|
fileprivate struct StringsUnkeyedEncoding: UnkeyedEncodingContainer {
|
|
private let data: PresentationThemeEncoding.Data
|
|
var codingPath: [CodingKey] = []
|
|
private(set) var count: Int = 0
|
|
|
|
init(to data: PresentationThemeEncoding.Data) {
|
|
self.data = data
|
|
}
|
|
|
|
private mutating func nextIndexedKey() -> CodingKey {
|
|
let nextCodingKey = IndexedCodingKey(intValue: count)!
|
|
self.count += 1
|
|
return nextCodingKey
|
|
}
|
|
|
|
private struct IndexedCodingKey: CodingKey {
|
|
let intValue: Int?
|
|
let stringValue: String
|
|
|
|
init?(intValue: Int) {
|
|
self.intValue = intValue
|
|
self.stringValue = intValue.description
|
|
}
|
|
|
|
init?(stringValue: String) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
mutating func encodeNil() throws {
|
|
}
|
|
|
|
mutating func encode(_ value: Bool) throws {
|
|
self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description)
|
|
}
|
|
|
|
mutating func encode(_ value: String) throws {
|
|
self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value)
|
|
}
|
|
|
|
mutating func encode(_ value: Int32) throws {
|
|
self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description)
|
|
}
|
|
|
|
mutating func encode<T: Encodable>(_ value: T) throws {
|
|
let stringsEncoding = PresentationThemeEncoding(to: self.data)
|
|
stringsEncoding.codingPath = self.codingPath + [self.nextIndexedKey()]
|
|
try value.encode(to: stringsEncoding)
|
|
}
|
|
|
|
mutating func nestedContainer<NestedKey: CodingKey>(
|
|
keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
|
|
var container = StringsKeyedEncoding<NestedKey>(to: self.data)
|
|
container.codingPath = self.codingPath + [self.nextIndexedKey()]
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
|
var container = StringsUnkeyedEncoding(to: self.data)
|
|
container.codingPath = self.codingPath + [self.nextIndexedKey()]
|
|
return container
|
|
}
|
|
|
|
mutating func superEncoder() -> Encoder {
|
|
let stringsEncoding = PresentationThemeEncoding(to: self.data)
|
|
stringsEncoding.codingPath = self.codingPath
|
|
return stringsEncoding
|
|
}
|
|
}
|
|
|
|
fileprivate struct StringsSingleValueEncoding: SingleValueEncodingContainer {
|
|
private let data: PresentationThemeEncoding.Data
|
|
var codingPath: [CodingKey] = []
|
|
|
|
init(to data: PresentationThemeEncoding.Data) {
|
|
self.data = data
|
|
}
|
|
|
|
mutating func encodeNil() throws {
|
|
}
|
|
|
|
mutating func encode(_ value: Bool) throws {
|
|
self.data.encode(key: self.codingPath, value: value.description)
|
|
}
|
|
|
|
mutating func encode(_ value: String) throws {
|
|
self.data.encode(key: self.codingPath, value: value)
|
|
}
|
|
|
|
mutating func encode(_ value: Int32) throws {
|
|
self.data.encode(key: self.codingPath, value: value.description)
|
|
}
|
|
|
|
mutating func encode<T: Encodable>(_ value: T) throws {
|
|
let stringsEncoding = PresentationThemeEncoding(to: self.data)
|
|
stringsEncoding.codingPath = self.codingPath
|
|
try value.encode(to: stringsEncoding)
|
|
}
|
|
}
|