Create public, Sendable ParsedOPML* structs to match what we do with feed parsing.

This commit is contained in:
Brent Simmons 2024-08-18 22:07:17 -07:00
parent 0127fd4d0b
commit 2d3c34f96a
6 changed files with 156 additions and 10 deletions

View File

@ -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?
}

View File

@ -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)
}
}

View File

@ -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<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafeMutablePointer<XMLPointer?>?) {
}
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? {
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}