// // NNW3Document.swift // NetNewsWire // // Created by Maurice Parker on 10/14/19. // Copyright © 2019 Ranchero Software. All rights reserved. // import Foundation import RSCore struct NNW3Document { private let children: [OPMLRepresentable]? private init(plist: [[String: AnyObject]]) { self.children = NNW3Folder.itemsWithPlist(plist: plist) } init?(subscriptionsPlistURL url: URL) { guard let data = try? Data(contentsOf: url) else { return nil } guard let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [[String: AnyObject]] else { return nil } self.init(plist: plist) } } // MARK: OPMLRepresentable extension NNW3Document: OPMLRepresentable { func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String { var s = """ NetNewsWire 3 Subscriptions """ if let children = children { for child in children { s += child.OPMLString(indentLevel: indentLevel + 1) } } s += """ """ return s } } // MARK: - NNW3Folder private struct NNW3Folder { private let title: String? private let children: [OPMLRepresentable]? init(plist: [String: Any]) { self.title = plist["name"] as? String guard let childrenArray = plist["childrenArray"] as? [[String: Any]] else { self.children = nil return } self.children = NNW3Folder.itemsWithPlist(plist: childrenArray) } static func itemsWithPlist(plist: [[String: Any]]) -> [OPMLRepresentable]? { // Also used by NNW3Document. var items = [OPMLRepresentable]() for child in plist { if child["isContainer"] as? Bool ?? false { items.append(NNW3Folder(plist: child)) } else { items.append(NNW3Feed(plist: child)) } } return items.isEmpty ? nil : items } } // MARK: OPMLRepresentable extension NNW3Folder: OPMLRepresentable { func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String { let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? "" guard let children = children else { // Empty folder. return "\n".rs_string(byPrependingNumberOfTabs: indentLevel) } var s = "\n".rs_string(byPrependingNumberOfTabs: indentLevel) for child in children { s += child.OPMLString(indentLevel: indentLevel + 1) } s += "\n".rs_string(byPrependingNumberOfTabs: indentLevel) return s } } // MARK: - NNW3Feed private struct NNW3Feed { private let title: String? private let homePageURL: String? private let feedURL: String? init(plist: [String: Any]) { self.title = plist["name"] as? String self.homePageURL = plist["home"] as? String self.feedURL = plist["rss"] as? String } } // MARK: OPMLRepresentable extension NNW3Feed: OPMLRepresentable { func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String { let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? "" let p = homePageURL?.rs_stringByEscapingSpecialXMLCharacters() ?? "" let f = feedURL?.rs_stringByEscapingSpecialXMLCharacters() ?? "" var s = "\n" s = s.rs_string(byPrependingNumberOfTabs: indentLevel) return s } }