Skip to content

Commit e77aab7

Browse files
authored
Enable filtering of podcast items using a predicate (#90)
This change enables a Predicate to be passed to the generatePodcastFeed publishing step, which lets the user conditionally decide which items to include in the feed. This feature was previously added to standard RSS feeds, but now podcast feeds also have the same capability.
1 parent 5406df9 commit e77aab7

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

Sources/Publish/API/PublishingStep.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,17 +400,21 @@ public extension PublishingStep where Site.ItemMetadata: PodcastCompatibleWebsit
400400
/// Note that all of the items within the given section must define `podcast`
401401
/// and `audio` metadata, or an error will be thrown.
402402
/// - parameter section: The section to generate a podcast feed for.
403+
/// - parameter itemPredicate: A predicate used to determine whether to
404+
/// include a given item within the generated feed (default: include all).
403405
/// - parameter config: The configuration to use when generating the feed.
404406
/// - parameter date: The date that should act as the build and publishing
405407
/// date for the generated feed (default: the current date).
406408
static func generatePodcastFeed(
407409
for section: Site.SectionID,
410+
itemPredicate: Predicate<Item<Site>>? = nil,
408411
config: PodcastFeedConfiguration<Site>,
409412
date: Date = Date()
410413
) -> Self {
411414
step(named: "Generate podcast feed") { context in
412415
let generator = PodcastFeedGenerator(
413416
sectionID: section,
417+
itemPredicate: itemPredicate,
414418
config: config,
415419
context: context,
416420
date: date

Sources/Publish/Internal/PodcastFeedGenerator.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Plot
99

1010
internal struct PodcastFeedGenerator<Site: Website> where Site.ItemMetadata: PodcastCompatibleWebsiteItemMetadata {
1111
let sectionID: Site.SectionID
12+
let itemPredicate: Predicate<Item<Site>>?
1213
let config: PodcastFeedConfiguration<Site>
1314
let context: PublishingContext<Site>
1415
let date: Date
@@ -18,7 +19,11 @@ internal struct PodcastFeedGenerator<Site: Website> where Site.ItemMetadata: Pod
1819
let cacheFile = try context.cacheFile(named: "feed")
1920
let oldCache = try? cacheFile.read().decoded() as Cache
2021
let section = context.sections[sectionID]
21-
let items = section.items.sorted(by: { $0.date > $1.date })
22+
var items = section.items.sorted(by: { $0.date > $1.date })
23+
24+
if let predicate = itemPredicate?.inverse() {
25+
items.removeAll(where: predicate.matches)
26+
}
2227

2328
if let date = context.lastGenerationDate, let cache = oldCache {
2429
if cache.config == config, cache.itemCount == items.count {

Tests/PublishTests/Tests/PodcastFeedGenerationTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ final class PodcastFeedGenerationTests: PublishTestCase {
2525
XCTAssertFalse(feed.contains("Not included"))
2626
}
2727

28+
func testOnlyIncludingItemsMatchingPredicate() throws {
29+
let folder = try Folder.createTemporary()
30+
31+
try generateFeed(
32+
in: folder,
33+
itemPredicate: \.path == "one/a",
34+
content: [
35+
"one/a.md": """
36+
\(makeStubbedAudioMetadata())
37+
# Included
38+
""",
39+
"one/b.md": "# Not included"
40+
]
41+
)
42+
43+
let feed = try folder.file(at: "Output/feed.rss").readAsString()
44+
XCTAssertTrue(feed.contains("Included"))
45+
XCTAssertFalse(feed.contains("Not included"))
46+
}
47+
2848
func testConvertingRelativeLinksToAbsolute() throws {
2949
let folder = try Folder.createTemporary()
3050

@@ -148,6 +168,7 @@ extension PodcastFeedGenerationTests {
148168
static var allTests: Linux.TestList<PodcastFeedGenerationTests> {
149169
[
150170
("testOnlyIncludingSpecifiedSection", testOnlyIncludingSpecifiedSection),
171+
("testOnlyIncludingItemsMatchingPredicate", testOnlyIncludingItemsMatchingPredicate),
151172
("testConvertingRelativeLinksToAbsolute", testConvertingRelativeLinksToAbsolute),
152173
("testItemPrefixAndSuffix", testItemPrefixAndSuffix),
153174
("testReusingPreviousFeedIfNoItemsWereModified", testReusingPreviousFeedIfNoItemsWereModified),
@@ -190,6 +211,7 @@ private extension PodcastFeedGenerationTests {
190211
func generateFeed(
191212
in folder: Folder,
192213
config: Configuration? = nil,
214+
itemPredicate: Predicate<Item<Site>>? = nil,
193215
generationSteps: [PublishingStep<Site>] = [
194216
.addMarkdownFiles()
195217
],
@@ -200,6 +222,7 @@ private extension PodcastFeedGenerationTests {
200222
.group(generationSteps),
201223
.generatePodcastFeed(
202224
for: .one,
225+
itemPredicate: itemPredicate,
203226
config: config ?? makeConfigStub(),
204227
date: date
205228
)

0 commit comments

Comments
 (0)