diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift b/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift index b977b2768..647ff215c 100644 --- a/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift +++ b/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift @@ -7,9 +7,8 @@ import Foundation -public struct OPMLDocument: Sendable { +final class OPMLDocument: OPMLItem, @unchecked Sendable { - public let title: String - public let url: String - public let items: [OPMLItem]? + var title: String? + var url: String? } diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift b/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift index caff99a38..fba59b526 100644 --- a/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift +++ b/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift @@ -8,17 +8,19 @@ import Foundation import os -public struct OPMLItem: Sendable { +class OPMLItem: @unchecked Sendable { public let feedSpecifier: OPMLFeedSpecifier public let attributes: [String: String] public let titleFromAttributes: String? - public let isFolder: Bool - public let items: [OPMLItem]? + public var items: [OPMLItem]? + public var isFolder: Bool { + items.count > 0 + } - init?(attributes: [String : String], items: [OPMLItem]?) { + init?(attributes: [String : String]) { guard let feedURL = attributes.opml_xmlUrl, !feedURL.isEmpty else { return nil @@ -35,8 +37,13 @@ public struct OPMLItem: Sendable { self.feedSpecifier = OPMLFeedSpecifier(title: titleFromAttributes, feedDescription: attributes.opml_description, homePageURL: attributes.opml_htmlUrl, feedURL: feedURL) self.attributes = attributes + } + + func addItem(_ item: OPMLItem) { - self.items = items - self.isFolder = (items?.count ?? 0) > 0 + if items == nil { + items = [OPMLItem]() + } + items?.append(item) } } diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift b/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift new file mode 100644 index 000000000..ad34ff31b --- /dev/null +++ b/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift @@ -0,0 +1,60 @@ +// +// OPMLParser.swift +// +// +// Created by Brent Simmons on 8/18/24. +// + +import Foundation + +public final class OPMLParser { + + let url: String + let data: Data + + private var itemStack = [OPMLItem]() + + enum OPMLParserError: Error { + case notOPML + } + + init(parserData: ParserData) { + + self.url = parserData.url + self.data = parserData.data + } + + func parse() throws -> OPMLDocument? { + + guard canParseData() else { + throw OPMLParserError.notOPML + } + + let parser = SAXParser(delegate: self, data: data) + + + } +} + +extension OPMLParser: SAXParserDelegate { + + func saxParser(_: SAXParser, xmlStartElement: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafeMutablePointer?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafeMutablePointer?) { + + } + + func saxParser(_: SAXParser, xmlEndElement: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) { + + } + + func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int) { + + } + + func saxParser(_: SAXParser, internedStringForName: XMLPointer, prefix: XMLPointer?) -> String? { + + } + + func saxParser(_: SAXParser, internedStringForValue: XMLPointer, count: Int) -> String? { + + } +} diff --git a/Modules/Parser/Sources/Parser/OPML/ParsedOPMLDocument.swift b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLDocument.swift new file mode 100644 index 000000000..ee90cd99d --- /dev/null +++ b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLDocument.swift @@ -0,0 +1,25 @@ +// +// ParsedOPMLDocument.swift +// +// +// Created by Brent Simmons on 8/18/24. +// + +import Foundation + +public final class ParsedOPMLDocument: Sendable { + + public let title: String? + public let url: String? + public let items: [ParsedOPMLItem]? + + init(opmlDocument: OPMLDocument) { + + self.title = opmlDocument.title + self.url = opmlDocument.url + + self.items = opmlDocument.items.map { opmlItem in + ParsedOPMLItem(opmlItem: opmlItem) + } + } +} diff --git a/Modules/Parser/Sources/Parser/OPML/ParsedOPMLFeedSpecifier.swift b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLFeedSpecifier.swift new file mode 100644 index 000000000..0d7e574f2 --- /dev/null +++ b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLFeedSpecifier.swift @@ -0,0 +1,24 @@ +// +// ParsedOPMLFeedSpecifier.swift +// +// +// Created by Brent Simmons on 8/18/24. +// + +import Foundation + +public struct ParsedOPMLFeedSpecifier: Sendable { + + public let title: String? + public let feedDescription: String? + public let homePageURL: String? + public let feedURL: String + + init(_ opmlFeedSpecifier: OPMLFeedSpecifier) { + + self.title = opmlFeedSpecifier.title + self.feedDescription = opmlFeedSpecifier.feedDescription + self.homePageURL = opmlFeedSpecifier.homePageURL + self.feedURL = opmlFeedSpecifier.feedURL + } +} diff --git a/Modules/Parser/Sources/Parser/OPML/ParsedOPMLItem.swift b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLItem.swift new file mode 100644 index 000000000..517a1681f --- /dev/null +++ b/Modules/Parser/Sources/Parser/OPML/ParsedOPMLItem.swift @@ -0,0 +1,31 @@ +// +// File.swift +// +// +// Created by Brent Simmons on 8/18/24. +// + +import Foundation + +public struct ParsedOPMLItem: Sendable { + + public let feedSpecifier: ParsedOPMLFeedSpecifier? + + public let attributes: [String: String]? + public let title: String? + + public var items: [ParsedOPMLItem]? + public var isFolder: Bool + + init(opmlItem: OPMLItem) { + + self.feedSpecifier = ParsedOPMLFeedSpecifier(opmlItem.feedSpecifier) + self.attributes = opmlItem.attributes + self.title = opmlItem.title + + self.items = opmlItem.items.map { opmlItem in + ParsedOPMLItem(opmlItem: opmlItem) + } + self.isFolder = (self.items?.count ?? 0) > 0 + } +}