Skip to content

Commit 465d163

Browse files
committed
Start implementing f-droid catalog; add parsing of atom releases feed
1 parent 81f2a21 commit 465d163

7 files changed

Lines changed: 652 additions & 193 deletions

File tree

Sources/FairExpo/AtomFeed.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import Swift
2+
import FairCore
3+
import Universal
4+
import struct Foundation.Data
5+
6+
/// An Atom web feed
7+
public struct AtomFeed {
8+
public var id: String?
9+
public var title: String?
10+
public var updated: String?
11+
public var links: [Link]
12+
public var entries: [Entry]
13+
14+
/// Attempts to initialze the AtomFeed with the given atom data, which must be valid XML.
15+
public init(xmlData data: Data) throws {
16+
try self.init(node: XMLNode.parse(data: data, options: [.reportNamespacePrefixes]))
17+
}
18+
19+
private init(node: XMLNode) throws {
20+
guard let root = node.elementChildren.first,
21+
root.elementName == "feed" else {
22+
throw Errors.noAtomRoot
23+
}
24+
25+
let entries = root.elementChildren
26+
27+
let elements = entries.dictionary(keyedBy: \.elementName)
28+
self.id = elements["id"]?.childContentTrimmed
29+
self.title = elements["title"]?.childContentTrimmed
30+
self.updated = elements["updated"]?.childContentTrimmed
31+
32+
self.entries = try entries.filter({ $0.elementName == "entry" }).map(Entry.init)
33+
self.links = try entries.filter({ $0.elementName == "link" }).map(Link.init)
34+
}
35+
36+
public struct Link {
37+
public var type: String?
38+
public var rel: String?
39+
public var href: String?
40+
41+
public init(node: XMLNode) throws {
42+
let dict = node.elementDictionary(attributes: true, childNodes: false)
43+
self.type = dict["type"]
44+
self.rel = dict["rel"]
45+
self.href = dict["href"]
46+
}
47+
}
48+
49+
public struct Author {
50+
public var name: String?
51+
52+
public init(node: XMLNode) throws {
53+
let entries = node.elementChildren
54+
self.name = entries.filter({ $0.elementName == "name" }).first?.stringContent
55+
}
56+
}
57+
58+
public struct Content {
59+
public var type: String?
60+
public var contents: String?
61+
62+
public init(node: XMLNode) throws {
63+
let dict = node.elementDictionary(attributes: true, childNodes: false)
64+
self.type = dict["type"]
65+
self.contents = node.stringContent
66+
}
67+
}
68+
69+
public struct Entry {
70+
public var id: String?
71+
public var title: String?
72+
public var updated: String?
73+
public var links: [Link]?
74+
public var authors: [Author]?
75+
public var contents: [Content]?
76+
77+
public init(node: XMLNode) throws {
78+
let elements = node.elementChildren.dictionary(keyedBy: \.elementName)
79+
80+
self.id = elements["id"]?.childContentTrimmed
81+
self.title = elements["title"]?.childContentTrimmed
82+
self.updated = elements["updated"]?.childContentTrimmed
83+
84+
self.links = try node.elementChildren.filter({ $0.elementName == "link" }).map(Link.init)
85+
self.authors = try node.elementChildren.filter({ $0.elementName == "author" }).map(Author.init)
86+
self.contents = try node.elementChildren.filter({ $0.elementName == "content" }).map(Content.init)
87+
}
88+
}
89+
90+
public enum Errors : Error {
91+
case noAtomRoot
92+
case rootEntriesInvalid
93+
case noChannelTitle
94+
case noChannelLink
95+
}
96+
}

0 commit comments

Comments
 (0)