From 239a551807e405258ae41a6ddf049d6890696c48 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 21 Dec 2023 13:02:53 -0500 Subject: [PATCH 01/18] Support Option Link for RSS (#65) --- Data/XML/wait-wait-dont-tell-me.xml | 4410 +++++++++++++++++ Sources/SyndiKit/Common/Entryable.swift | 2 +- .../Formats/Feeds/Atom/AtomEntry.swift | 6 +- .../Formats/Feeds/JSONFeed/JSONItem.swift | 2 +- .../SyndiKit/Formats/Feeds/RSS/RSSItem.swift | 6 +- .../Media/Wordpress/WordPressPost.swift | 4 +- Tests/SyndiKitTests/RSSCodedTests.swift | 21 + 7 files changed, 4441 insertions(+), 10 deletions(-) create mode 100644 Data/XML/wait-wait-dont-tell-me.xml diff --git a/Data/XML/wait-wait-dont-tell-me.xml b/Data/XML/wait-wait-dont-tell-me.xml new file mode 100644 index 0000000..291e544 --- /dev/null +++ b/Data/XML/wait-wait-dont-tell-me.xml @@ -0,0 +1,4410 @@ + + + + Wait Wait... Don't Tell Me! + https://www.npr.org/podcasts/344098539/wait-wait-don-t-tell-me +

Hate free content? Try a subscription to Wait Wait... Don't Tell Me!+. Your subscription supports public radio and unlocks fun bonus episodes along with sponsor-free listening. Learn more at https://plus.npr.org/waitwait]]>
+ Copyright 2014-2021 NPR - For Personal Use Only + NPR Feed Publish Service v1.15.7 + en + NPR + no + + podcasts@npr.org + NPR + + + + + + + + episodic + + https://media.npr.org/assets/img/2022/09/23/waitwait-don-t-tell-me_tile_npr-network-01_sq-d51413832c7ccf5301741d7f1ee2e1853fed9597.jpg?s=1400&c=66&f=jpg + Wait Wait... Don't Tell Me! + https://www.npr.org/podcasts/344098539/wait-wait-don-t-tell-me + + Tue, 19 Dec 2023 16:55:11 +0000 + es fi fr gb nl no se us dk at ch be au nz ca de it ie lu li ad mc pl pt is mx ee lv lt my gr ar hu cz mt bg sk cy br cl co uy sv py hn pa ni pe ec do gt cr bo id jp th ro il za + + Wait Wait Wayback Machine: December 2003 + Wait Wait... Don't Tell Me listener with questions about news events from our actual show — but the catch is they're shows that aired 20 years ago!

The Wait Wait Wayback Machine is normally a bonus episode that only Wait Wait... Don't Tell Me+ supporters can hear and play. Today, we're making it available for everyone.

If you want a chance to play the Wait Wait Wayback Machine, sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org!]]>
+ Tue, 19 Dec 2023 00:08:23 +0000 + 28333718-ffb4-40ba-b94a-047f73be1b9f + https://www.npr.org/2026/01/01/1198920316/zz-wwdtm-bonusdraft + no + Wait Wait Wayback Machine: December 2003 + + 858 + no + bonus + Wait Wait... Don't Tell Me listener with questions about news events from our actual show — but the catch is they're shows that aired 20 years ago!

The Wait Wait Wayback Machine is normally a bonus episode that only Wait Wait... Don't Tell Me+ supporters can hear and play. Today, we're making it available for everyone.

If you want a chance to play the Wait Wait Wayback Machine, sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org!]]>
+ +
+ + WWDTM: Bethenny Frankel + + Sat, 16 Dec 2023 14:48:46 +0000 + 9256964c-9a74-4f96-9d1f-0c03d1e9a106 + https://www.npr.org/2023/12/16/1198908307/bethenny-frankel-talks-feuds-throwing-drinks-and-becoming-an-accidental-influenc + no + WWDTM: Bethenny Frankel + 1375 + + 2933 + no + full + + + + + Extended cut: Dakota Johnson on her new doc, childhood, and curating a sex museum + Wait Wait... Don't Tell Me+ supporters! It's an extended cut of our interview with actor, producer, and activist Dakota Johnson, whose new documentary is The Disappearance of Shere Hite.

That's right — an extended cut. Our celebrity interviews sometimes last much longer than what we're able to include in our regular show. Wait Wait... Don't Tell Me+ supporters can hear extended cuts in regular bonus episodes. They also get a chance to play a special news quiz game over zoom with Peter Sagal and Wait Wait staffer! To find out more, and to hear our regular show without sponsor messages, sign up for Wait Wait... Don't Tell Me+ at plus.npr.org]]>
+ Mon, 11 Dec 2023 21:19:00 +0000 + 7f0b2f90-db95-4db2-bfcd-85d7a989b90e + https://www.npr.org/2026/01/01/1198920314/zz-wwdtm-bonusdraft + no + Extended cut: Dakota Johnson on her new doc, childhood, and curating a sex museum + + 1432 + no + bonus + Wait Wait... Don't Tell Me+ supporters! It's an extended cut of our interview with actor, producer, and activist Dakota Johnson, whose new documentary is The Disappearance of Shere Hite.

That's right — an extended cut. Our celebrity interviews sometimes last much longer than what we're able to include in our regular show. Wait Wait... Don't Tell Me+ supporters can hear extended cuts in regular bonus episodes. They also get a chance to play a special news quiz game over zoom with Peter Sagal and Wait Wait staffer! To find out more, and to hear our regular show without sponsor messages, sign up for Wait Wait... Don't Tell Me+ at plus.npr.org]]>
+ +
+ + WWDTM: Fred Schneider + + Sat, 09 Dec 2023 10:37:57 +0000 + 4a45e191-6fc1-4d1c-9a0e-f9eb0f0c4036 + https://www.npr.org/2023/12/09/1198908286/wait-wait-dont-tell-me-draft-12-09-2023 + no + WWDTM: Fred Schneider + 1374 + + 2938 + no + full + + + + + WWDTM: Dakota Johnson + The Disappearance of Shere Hite, joins panelists Adam Felber, Joyelle Nicole Johnson, and Alonzo Bodden to talk famous families, Fifty Shades of Grey, and more.]]> + Sat, 02 Dec 2023 14:13:46 +0000 + d015fdf2-15f8-46ba-b3b9-45944aed302c + https://www.npr.org/2023/12/02/1198908271/wait-wait-dont-tell-me-draft-12-02-2023 + no + WWDTM: Dakota Johnson + 1373 + + 2943 + no + full + The Disappearance of Shere Hite, joins panelists Adam Felber, Joyelle Nicole Johnson, and Alonzo Bodden to talk famous families, Fifty Shades of Grey, and more.]]> + + + + WWDTM: 25th Year Spectacular Part VIII! + + Sat, 25 Nov 2023 08:00:59 +0000 + 29282f8a-797f-4a39-8116-419cd0e96a56 + https://www.npr.org/2023/11/25/1198908242/happy-thanksgiving-with-adam-savage-jane-curtin-and-more + no + WWDTM: 25th Year Spectacular Part VIII! + 1372 + + 2852 + no + full + + + + + WWDTM: Stephen Smith + + Sat, 18 Nov 2023 17:00:29 +0000 + c0410a5a-a27a-4ba5-9d75-ac4c651860bf + https://www.npr.org/2023/11/18/1198908222/l-l-bean-ceo-stephen-smith-answers-questions-about-jelly-beans + no + WWDTM: Stephen Smith + 1371 + + 2877 + no + full + + + + + WWDTM: John Stamos + + Sat, 11 Nov 2023 08:00:59 +0000 + 796361ef-e59f-4fa2-ae75-bd4930bf07ee + https://www.npr.org/2023/11/11/1198908209/john-stamos-talks-being-ridiculously-handsome + no + WWDTM: John Stamos + 1370 + + 2879 + no + full + + + + + WWDTM: Dr. Rae Wynn-Grant + Mutual of Omaha's Wild Kingdom. She joins panelists Adam Burke, Maeve Higgins, and Tom Papa to talk bear attacks, gummy bears, and the strange joy of being squeezed by a boa constrictor.]]> + Sat, 04 Nov 2023 12:47:27 +0000 + 20fad301-24ff-41af-a94f-32ba87522f87 + https://www.npr.org/2023/11/04/1198908185/dr-rae-wynn-grant-talks-bear-attacks-and-gummy-bears + no + WWDTM: Dr. Rae Wynn-Grant + 1369 + + 2868 + no + full + Mutual of Omaha's Wild Kingdom. She joins panelists Adam Burke, Maeve Higgins, and Tom Papa to talk bear attacks, gummy bears, and the strange joy of being squeezed by a boa constrictor.]]> + + + + WWDTM: Bernie Taupin + Scattershot]]> + Sat, 28 Oct 2023 12:31:22 +0000 + ca95f8b5-bd49-4477-84f1-d4df1b913522 + https://www.npr.org/2023/10/28/1198908160/bernie-taupin-talks-his-new-memoir + no + WWDTM: Bernie Taupin + 1368 + + 2869 + no + full + Scattershot]]> + + + + WWDTM: James Patterson + + Sat, 21 Oct 2023 16:23:13 +0000 + 52062e52-f137-44d3-b325-a9be0e17d099 + https://www.npr.org/2023/10/21/1198908118/wait-wait-dont-tell-me-draft-10-21-2023 + no + WWDTM: James Patterson + 1367 + + 2872 + no + full + + + + + WWDTM: 25th Year Spectacular Part VII! + + Sat, 14 Oct 2023 12:02:50 +0000 + 6b527c16-6b90-4345-9215-dd55d607cec5 + https://www.npr.org/2023/10/14/1198908103/our-25th-anniversary-spectacular-continues + no + WWDTM: 25th Year Spectacular Part VII! + 1366 + + 2927 + no + full + + + + + WWDTM: Solicitor General Elizabeth Prelogar + + Sat, 07 Oct 2023 14:45:14 +0000 + e65f99d7-5065-4bec-b4c5-d4ba374e18d0 + https://www.npr.org/2023/10/07/1198908040/solicitor-general-elizabeth-prelogar-on-the-supreme-court-and-being-miss-idaho + no + WWDTM: Solicitor General Elizabeth Prelogar + 1365 + + 2845 + no + full + + + + + WWDTM: Bob and Erin Odenkirk + + Sat, 30 Sep 2023 16:03:57 +0000 + ecc899f0-4b76-41a1-be61-5dc476176866 + https://www.npr.org/2023/09/30/1198908024/bob-and-erin-odenkirk-debate-on-the-funniest-member-of-the-family + no + WWDTM: Bob and Erin Odenkirk + 1364 + + 2879 + no + full + + + + + WWDTM: John Wilson + How To With John Wilson, and joins us to talk about being paid in Wite-Out and the best place in New York to meet a referee.]]> + Sat, 23 Sep 2023 16:00:59 +0000 + 5bf1768e-b954-4c99-ae2c-5add8fa360f3 + https://www.npr.org/2023/09/23/1198907957/wait-wait-dont-tell-me-draft-09-23-2023 + no + WWDTM: John Wilson + 1363 + + 2842 + no + full + How To With John Wilson, and joins us to talk about being paid in Wite-Out and the best place in New York to meet a referee.]]> + + + + WWDTM: Secretary Clinton + + Sat, 16 Sep 2023 16:00:18 +0000 + 5985578b-8213-41ae-8c9b-dee1b6602d96 + https://www.npr.org/2023/09/16/1198748508/wait-wait-dont-tell-me-draft-09-16-2023 + no + WWDTM: Secretary Clinton + 1362 + + 2889 + no + full + + + + + WWDTM: Martinus Evans + + Sat, 09 Sep 2023 15:50:47 +0000 + c909f608-0bbf-41f2-b3b1-dafe6a8fbf9b + https://www.npr.org/2023/09/08/1198502011/slow-af-run-clubs-martinus-evans-talks-falling-off-a-treadmill-running-for-reven + no + WWDTM: Martinus Evans + 1361 + + 2876 + no + full + + + + + WWDTM: Bob Seger + + Sat, 02 Sep 2023 16:00:37 +0000 + bdcd43b6-e5a1-471e-b4c3-d1c39d1ed4c0 + https://www.npr.org/2023/08/29/1196586872/bob-seger-talks-about-partying-in-a-farmers-field-and-gives-us-hair-care-tips + no + WWDTM: Bob Seger + 1360 + 2875 + no + full + + + + + WWDTM: Mark Ronson + Barbie. He joins guest host Negin Farsad and panelists Shantira Jackson, Alzo Slade, and Luke Burbank.]]> + Sat, 26 Aug 2023 16:00:53 +0000 + 731366d4-3c67-44e2-8267-45294808d647 + https://www.npr.org/2023/08/25/1196087175/mark-ronson-on-how-rupaul-inspired-his-business-cards + no + WWDTM: Mark Ronson + 1359 + + 2840 + no + full + Barbie. He joins guest host Negin Farsad and panelists Shantira Jackson, Alzo Slade, and Luke Burbank.]]> + + + + WWDTM: 25th Year Spectacular Part VI! + + Sat, 19 Aug 2023 16:00:00 +0000 + d5b1f31c-9b50-4a39-9208-e0ff8d3c3830 + https://www.npr.org/2023/07/26/1190348223/wwdtm-25th-year-spectacular-part-vi + no + WWDTM: 25th Year Spectacular Part VI! + 1358 + + 2856 + no + full + + + + + WWDTM: 25th Year Spectacular Part V! + + Sat, 12 Aug 2023 16:00:46 +0000 + 18483f76-a201-4d35-9941-338fa2a99906 + https://www.npr.org/2023/07/26/1190348006/wwdtm-25th-year-spectacular-part-v + no + WWDTM: 25th Year Spectacular Part V! + 1357 + + 2850 + no + full + + + + + WWDTM: Maggie Smith + + Sat, 05 Aug 2023 16:00:11 +0000 + 38fe8ba2-afad-415e-a46d-9472c38df7d9 + https://www.npr.org/2023/08/01/1191395134/poet-maggie-smith-talks-going-viral-and-being-confused-with-that-other-maggie-sm + no + WWDTM: Maggie Smith + 1356 + + 2895 + no + full + + + + + WWDTM: Randall Park + Shortcomings. He joins guest host Karen Chee and panelists Zainab Johnson, Tom Bodett and Josh Gondelman.]]> + Sat, 29 Jul 2023 16:00:56 +0000 + f919be6a-cb9e-49c8-9c4f-6a7f26883e7a + https://www.npr.org/2023/07/26/1190348657/randall-park-the-person-gets-quizzed-on-randall-park-the-mall + no + WWDTM: Randall Park + 1355 + + 2836 + no + full + Shortcomings. He joins guest host Karen Chee and panelists Zainab Johnson, Tom Bodett and Josh Gondelman.]]> + + + + WWDTM: Damian Lillard + + Sat, 22 Jul 2023 16:00:11 +0000 + b13a2f35-3201-43fa-a03a-01895af2b3b5 + https://www.npr.org/2023/07/21/1189181410/damian-lillard-talks-rap-battling-shaq + no + WWDTM: Damian Lillard + 1354 + + 2845 + no + full + + + + + WWDTM: Patti LuPone + + Sat, 15 Jul 2023 16:00:04 +0000 + 722cfde2-e755-4df5-a878-01445a27670f + https://www.npr.org/2023/07/14/1187794395/patti-lupone-talks-quitting-broadway + no + WWDTM: Patti LuPone + 1353 + + 2822 + no + full + + + + + WWDTM: 25th Year Spectacular Part IV! + Barbie director Greta Gerwig!]]> + Sat, 08 Jul 2023 16:00:00 +0000 + 67043290-caeb-423b-8012-0fb1e6d73a5e + https://www.npr.org/2023/06/28/1184863768/25th-anniversary-spectacular-part-iv + no + WWDTM: 25th Year Spectacular Part IV! + 1352 + + 2873 + no + full + Barbie director Greta Gerwig!]]> + + + + WWDTM: Aleeza Ben Shalom + Jewish Matchmaking, and she joins panelists Adam Burke, Brian Babylon, and Roxanne Roberts to talk about how matchmaking is just like a live-action game of Where's Waldo]]> + Sat, 01 Jul 2023 16:00:55 +0000 + 1195cafe-b944-4073-8825-addcf804fd40 + https://www.npr.org/2023/06/28/1184863489/aleeza-ben-shalom-on-matchmaking + no + WWDTM: Aleeza Ben Shalom + 1351 + 2808 + no + full + Jewish Matchmaking, and she joins panelists Adam Burke, Brian Babylon, and Roxanne Roberts to talk about how matchmaking is just like a live-action game of Where's Waldo]]> + + + + WWDTM: Karen Allen + Indiana Jones movies. She joins Karen Chee, Roy Blount, Jr. and Negin Farsad to talk spitting in auditions, Tom Selleck, and working with snakes]]> + Sat, 24 Jun 2023 16:00:02 +0000 + d2656fdc-a44f-47d8-bb8d-67987114842c + https://www.npr.org/2023/06/23/1183949214/indiana-jones-karen-allen-on-working-with-6-000-snakes + no + WWDTM: Karen Allen + 1350 + + 2873 + no + full + Indiana Jones movies. She joins Karen Chee, Roy Blount, Jr. and Negin Farsad to talk spitting in auditions, Tom Selleck, and working with snakes]]> + + + + WWDTM: James Marsden + Jury Duty, playing yourself, and what it's like to be the Baxter in a romcom.]]> + Sat, 17 Jun 2023 16:00:32 +0000 + e7586bab-ca46-4610-82dc-addaf6a9584d + https://www.npr.org/2023/06/16/1182827705/james-marsden-on-little-white-lies-and-being-the-other-guy + no + WWDTM: James Marsden + 1349 + + 2884 + no + full + Jury Duty, playing yourself, and what it's like to be the Baxter in a romcom.]]> + + + + WWDTM: Radhika Jones + + Sat, 10 Jun 2023 16:00:45 +0000 + 3f8ae279-9e44-4c67-b4c6-81cb2f0fffdf + https://www.npr.org/2023/06/09/1181349799/vanity-fairs-radhika-jones-talks-little-house-on-the-prairie + no + WWDTM: Radhika Jones + 1348 + + 2825 + no + full + + + + + WWDTM: The First Quarter Century, pt. III +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 03 Jun 2023 16:00:07 +0000 + c08ab0e3-ec55-4f45-95c2-50a6f16ac26e + https://www.npr.org/2023/05/23/1177691126/25th-anniversary-spectacular-part-three + no + WWDTM: The First Quarter Century, pt. III + 1348 + + 2839 + no + full +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: John Goodman + The Big Lebowski, and being the voice of the St. Louis airport.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 27 May 2023 16:00:04 +0000 + cc485059-f896-40cc-8fad-eb69093fac76 + https://www.npr.org/2023/05/23/1177690696/john-goodman-on-the-dark-secret-behind-all-his-lovable-characters + no + WWDTM: John Goodman + 1347 + + 2904 + no + full + The Big Lebowski, and being the voice of the St. Louis airport.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: Golda Rosheuvel + Bridgerton's Queen Charlotte that Netflix made a show all about her. She joins Luke Burbank, Negin Farsad, and Adam Burke to talk wigs, neck braces, and bodice ripping]]> + Sat, 20 May 2023 16:00:08 +0000 + 69c4dc60-143d-4915-a439-4e49cdd18ef6 + https://www.npr.org/2023/05/19/1177100450/bridgertons-golda-rosheuvel-talks-giant-wigs-and-neck-braces + no + WWDTM: Golda Rosheuvel + 1346 + + 2832 + no + full + Bridgerton's Queen Charlotte that Netflix made a show all about her. She joins Luke Burbank, Negin Farsad, and Adam Burke to talk wigs, neck braces, and bodice ripping]]> + + + + WWDTM: Gabrielle Dennis + The Big Door Prize's Gabrielle Dennis joins panelists Paula Poundstone, Alonzo Bodden, and Adam Felber to talk about living to your full potential and the job at Six Flags that everyone else is jealous of.]]> + Sat, 13 May 2023 16:00:57 +0000 + a397029b-59f4-47ec-9e39-b8d2a4461c77 + https://www.npr.org/2023/05/10/1175307952/gabrielle-dennis-on-working-at-six-flags-and-giving-audiences-existential-crises + no + WWDTM: Gabrielle Dennis + 1345 + + 2900 + no + full + The Big Door Prize's Gabrielle Dennis joins panelists Paula Poundstone, Alonzo Bodden, and Adam Felber to talk about living to your full potential and the job at Six Flags that everyone else is jealous of.]]> + + + + WWDTM: Ray Romano +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 06 May 2023 15:55:33 +0000 + a2d65027-6f4c-447a-b7f4-d300bbfeead1 + https://www.npr.org/2023/05/05/1174279082/ray-romano-on-the-real-secret-to-a-35-year-marriage + no + WWDTM: Ray Romano + 1344 + 2883 + no + full +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: Brad Paisley + + Sat, 29 Apr 2023 16:04:28 +0000 + 90a30d89-0284-4702-b7a1-9be7e6acf065 + https://www.npr.org/2023/04/28/1172852428/brad-paisley-on-what-to-avoid-when-writing-songs-about-your-wife + no + WWDTM: Brad Paisley + 1343 + + 2864 + no + full + + + + + WWDTM: Weird Al Yankovic + + Sat, 22 Apr 2023 16:00:09 +0000 + 2e957727-c248-43c4-b346-825c36c551b9 + https://www.npr.org/2023/04/19/1170960666/weird-al-on-getting-turned-down-by-prince + no + WWDTM: Weird Al Yankovic + 1342 + + 2835 + no + full + + + + + WWDTM: Kaila Mullady +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 15 Apr 2023 16:00:26 +0000 + 4f126ad8-260d-45f4-bbce-fb7e32fd32d9 + https://www.npr.org/2023/04/11/1169359677/beatbox-champion-kaila-mullady-on-boots-and-cats + no + WWDTM: Kaila Mullady + 1341 + + 2873 + no + full +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: Everyone & Goodbyes + + Wed, 12 Apr 2023 14:00:16 +0000 + c6b8817b-d983-40a4-8efd-816baef4379b + https://www.npr.org/2023/04/11/1169326367/everyone-goodbyes + no + WWDTM: Everyone & Goodbyes + 1340 + + 357 + no + full + + + + + WWDTM: The First Quarter Century + + Sat, 08 Apr 2023 15:55:22 +0000 + 9f72fb3e-86f3-45a6-9360-e35f651decf1 + https://www.npr.org/2023/04/03/1167848677/the-wait-wait-25th-anniversary-spectacular-pt-ii + no + WWDTM: The First Quarter Century + 1359 + 2883 + no + full + + + + + WWDTM: Michelle Rodriguez + The Fast & The Furious, Dungeons & Dragons, and her personal state slogan for New Jersey.


Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 01 Apr 2023 16:01:14 +0000 + b9108ee9-9e5c-490e-922f-d084dcfe0357 + https://www.npr.org/2023/03/29/1166938226/michelle-rodriguez-on-fast-cars-and-fiery-dragons + no + WWDTM: Michelle Rodriguez + 1358 + + 2900 + no + full + The Fast & The Furious, Dungeons & Dragons, and her personal state slogan for New Jersey.


Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: David Axelrod +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 25 Mar 2023 15:58:56 +0000 + 6cf547c3-a91f-4439-a4b6-c456a3aaed4a + https://www.npr.org/2023/03/24/1165881738/david-axelrod-on-president-paula-poundstone + no + WWDTM: David Axelrod + 1357 + + 2825 + no + full +
Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM: Sam Waterston + Law & Order, how to look good in a stovepipe hat, and where to see raccoons doing Shakespeare.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 18 Mar 2023 16:00:26 +0000 + ec37b61d-4b54-4abc-ac68-3a8dcbed8f60 + https://www.npr.org/2023/03/17/1164375148/sam-waterston-on-being-the-most-recognizable-pretend-lawyer-in-new-york + no + WWDTM: Sam Waterston + 1356 + + 2899 + no + full + Law & Order, how to look good in a stovepipe hat, and where to see raccoons doing Shakespeare.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Nick Kroll + Big Mouth and History of the World, Part II. We Rick Roll Nick Kroll, that is, ask him questions about roles played by actors named Rick.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 11 Mar 2023 16:55:06 +0000 + f15a2e22-5bc6-43ae-a1f5-01f541faa8ac + https://www.npr.org/2023/03/06/1161353805/nick-kroll-on-rejected-characters-and-getting-mel-brooks-to-laugh + no + Nick Kroll + 1355 + + 2832 + no + full + Big Mouth and History of the World, Part II. We Rick Roll Nick Kroll, that is, ask him questions about roles played by actors named Rick.

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + The Wait Wait Anthology: Reality TV Edition + The Wait Wait Anthology, we bring the drama and dive deep into the world of reality TV. From The Bachelor to The Kardashians, we leave no stone unturned and no suite un-fantasied.]]> + Wed, 08 Mar 2023 22:35:58 +0000 + 59c2e813-930d-4d34-b939-ca66abceed72 + https://www.npr.org/2023/03/01/1160422578/the-wait-wait-anthology-reality-tv-edition + no + The Wait Wait Anthology: Reality TV Edition + 1354 + 1002 + no + full + The Wait Wait Anthology, we bring the drama and dive deep into the world of reality TV. From The Bachelor to The Kardashians, we leave no stone unturned and no suite un-fantasied.]]> + + + + Malala Yousafzai on winning the Nobel Peace Prize while in chemistry class +
A message to listeners: a recent error with Apple Podcasts meant you might not have been able to hear our regular weekend show without signing up for Wait Wait... Don't Tell Me+. We have worked with Apple to correct the error. Regular episodes have not changed and will remain free and available. We've shared some information about managing your subscription via Apple on our Facebook page: https://bit.ly/3mamqm2

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 04 Mar 2023 17:00:40 +0000 + 5711c982-ea8d-4c66-9366-d54bc74b6730 + https://www.npr.org/2023/03/01/1160422137/malala-yousafzai-won-the-nobel-peace-prize-while-in-chemistry + no + Malala Yousafzai on winning the Nobel Peace Prize while in chemistry class + 1353 + + 2907 + no + full +
A message to listeners: a recent error with Apple Podcasts meant you might not have been able to hear our regular weekend show without signing up for Wait Wait... Don't Tell Me+. We have worked with Apple to correct the error. Regular episodes have not changed and will remain free and available. We've shared some information about managing your subscription via Apple on our Facebook page: https://bit.ly/3mamqm2

Support NPR by signing up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Best of the First 25 Years + + Sat, 25 Feb 2023 17:02:14 +0000 + 06cc54f1-d5af-4754-8739-1d6b2a83bd5f + https://www.npr.org/2023/02/22/1158897769/best-of-the-first-25-years + no + Best of the First 25 Years + 1352 + 2844 + no + full + + + + + Everyone & Spotify Stalking + + Wed, 22 Feb 2023 15:52:11 +0000 + 5a35c9d0-e5fb-4531-839b-c5c0f13d615f + https://www.npr.org/2023/02/15/1157244889/everyone-spotify-stalking + no + Everyone & Spotify Stalking + 1351 + + 913 + no + full + + + + + Rosie Perez + Do The Right Thing to White Men Can't Jump to her new role on Showtime's Your Honor. She's the best part of any project she's in, but can she answer our questions about advice columns?]]> + Sat, 18 Feb 2023 17:00:05 +0000 + dec38ff6-7281-47f7-9631-c589c2463164 + https://www.npr.org/2023/02/15/1157244228/rosie-perez-on-her-first-meeting-with-spike-lee + no + Rosie Perez + 1350 + + 2819 + no + full + Do The Right Thing to White Men Can't Jump to her new role on Showtime's Your Honor. She's the best part of any project she's in, but can she answer our questions about advice columns?]]> + + + + Geena Davis + Mission Unstoppable. But, can she answer our three questions about blue jeans?

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 11 Feb 2023 17:00:06 +0000 + c3817e1b-8a5e-465f-a833-c173fe30d2e7 + https://www.npr.org/2023/02/08/1155478251/geena-davis-on-her-early-gig-as-a-mannequin + no + Geena Davis + 1348 + + 2859 + no + full + Mission Unstoppable. But, can she answer our three questions about blue jeans?

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + The Wait Wait Anthology: Cats Edition + The Wait Wait Anthology, a deep dive into the Wait Wait archives hosted by Bill Kurtis]]> + Wed, 08 Feb 2023 20:33:14 +0000 + 6aeb0f22-f17b-476f-95aa-038de3270916 + https://www.npr.org/2023/02/08/1155477408/the-wait-wait-anthology-cats-edition + no + The Wait Wait Anthology: Cats Edition + 1347 + 746 + no + full + The Wait Wait Anthology, a deep dive into the Wait Wait archives hosted by Bill Kurtis]]> + + + + Billy Porter + 80 For Brady, but what does he know about the Brady Bunch?

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 04 Feb 2023 17:00:22 +0000 + 6412028e-f52c-41f6-9350-8637d2d075b3 + https://www.npr.org/2023/01/30/1152554955/billy-porter-on-the-thin-line-between-fashion-and-pain + no + Billy Porter + 1346 + + 2819 + no + full + 80 For Brady, but what does he know about the Brady Bunch?

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Natasha Lyonne + Poker Face, so we ask her three questions about getting botox.

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 28 Jan 2023 17:00:13 +0000 + 0a24549e-cfee-4d7b-8341-968d2ecfa0b5 + https://www.npr.org/2023/01/27/1152121831/natasha-lyonne-gets-kicked-out-of-boarding-school + no + Natasha Lyonne + 1345 + + 2846 + no + full + Poker Face, so we ask her three questions about getting botox.

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Everyone & Mistaken Identity + + Wed, 25 Jan 2023 20:47:02 +0000 + 2146f28a-685b-4207-8815-4bf17d36cd69 + https://www.npr.org/2023/01/10/1148231915/everyone-mistaken-identity + no + Everyone & Mistaken Identity + 1344 + + 663 + no + full + + + + + Secretary of State Antony Blinken +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 21 Jan 2023 17:07:28 +0000 + bb6527ee-51e2-431f-87ab-a09eeb3f4c30 + https://www.npr.org/2023/01/18/1149896025/secretary-of-state-antony-blinken-musical-alter-ego + no + Secretary of State Antony Blinken + 1343 + + 2846 + no + full +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + George Saunders +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 14 Jan 2023 16:50:21 +0000 + c979588b-d46c-4618-b61f-b1a368a1586a + https://www.npr.org/2023/01/10/1148230592/george-saunders-on-slaughterhouses-and-obscene-poetry + no + George Saunders + 1342 + + 2862 + no + full +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Mariska Hargitay + Law and Order, SVU, answers three questions about Sweet Valley High, the location depicted in the teen book series. We also revisit our moments with Sean Hayes, Myles Stubblefield, and Mo Amer.]]> + Sat, 07 Jan 2023 16:59:10 +0000 + fbcda8b3-5e43-4505-9f47-8b3c2027d592 + https://www.npr.org/2022/12/21/1144810950/mariska-hargitay + no + Mariska Hargitay + 1341 + 2853 + no + full + Law and Order, SVU, answers three questions about Sweet Valley High, the location depicted in the teen book series. We also revisit our moments with Sean Hayes, Myles Stubblefield, and Mo Amer.]]> + + + + The Man Bill-hind the Voice! + + Wed, 04 Jan 2023 16:23:47 +0000 + 7744f58c-247c-4798-a163-b28f1ee10a8c + https://www.npr.org/2023/01/03/1146780258/the-man-bill-hind-the-voice + no + The Man Bill-hind the Voice! + 1340 + 850 + no + full + + + + + Best of Not My Job December 2022 + + Sat, 31 Dec 2022 17:00:40 +0000 + 647aba78-bbea-415f-b244-a3de697295ed + https://www.npr.org/2022/12/21/1144810585/best-of-not-my-job-december-2022 + no + Best of Not My Job December 2022 + 1339 + 2833 + no + full + + + + + Sarah Polley + + Sat, 24 Dec 2022 17:00:40 +0000 + 722c1847-8d83-4605-9cae-0b2cdf1bc4b5 + https://www.npr.org/2022/12/20/1144385047/sarah-polley-women-talking-book-club + no + Sarah Polley + 1338 + + 2802 + no + full + + + + + Everyone & Improv Nerds + + Wed, 21 Dec 2022 17:40:20 +0000 + 439de59d-7c49-4f34-8d90-ee2178b8bd2c + https://www.npr.org/2022/12/20/1144385003/peter-sagal-running-central-park + no + Everyone & Improv Nerds + 1337 + + 956 + no + full + + + + + Andrew Bird +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 17 Dec 2022 16:59:40 +0000 + 54652806-ba2e-43a7-ba9e-1e1b4101894e + https://www.npr.org/2022/12/14/1142825416/andrew-bird-inside-problems-mariah-carey + no + Andrew Bird + 1336 + + 2898 + no + full +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Gayle King + CBS Mornings, Gayle King plays our game called, "Gayle King, meet the real Gale Kings" three questions about meteorologists. She is joined by panelists Negin Farsad, Peter Grosz, and Faith Salie.]]> + Sat, 10 Dec 2022 17:03:19 +0000 + 04ff4bb2-8342-4ce6-9c66-58226f51d5b4 + https://www.npr.org/2022/12/06/1141003550/gayle-king-cbs-mornings + no + Gayle King + 1335 + + 2822 + no + full + CBS Mornings, Gayle King plays our game called, "Gayle King, meet the real Gale Kings" three questions about meteorologists. She is joined by panelists Negin Farsad, Peter Grosz, and Faith Salie.]]> + + + + How to Become a Listener Contestant with Peter, Miles, and Sofie + + Wed, 07 Dec 2022 15:01:21 +0000 + fe2176b1-2cd5-4fab-a14c-95fb6fa133dd + https://www.npr.org/2022/12/06/1141003318/wait-wait-dont-tell-me-caller-contestant + no + How to Become a Listener Contestant with Peter, Miles, and Sofie + 1335 + 751 + no + full + + + + + Dana Carvey +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 03 Dec 2022 17:51:18 +0000 + 942fe26c-424d-4bc5-8385-94e93142c59f + https://www.npr.org/2022/11/15/1136884298/dana-carvey-snl-impressions + no + Dana Carvey + 1334 + + 2907 + no + full +
Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + WWDTM Best of NMJ Thanksgiving +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 26 Nov 2022 17:04:10 +0000 + 5abadc5c-f489-4ee3-b434-e2acdbe56308 + https://www.npr.org/2022/11/15/1136884107/wwdtm-best-of-nmj-thanksgiving + no + WWDTM Best of NMJ Thanksgiving + 1333 + 2842 + no + full +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Everyone & Giant Puzzles + + Wed, 23 Nov 2022 14:59:00 +0000 + 281c1165-57af-43b1-8e94-5a8f53a387ef + https://www.npr.org/2022/11/22/1138692930/worlds-largest-puzzle-dowdle-costco + no + Everyone & Giant Puzzles + 1332 + + 811 + no + full + + + + + Freddie Johnson +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ Sat, 19 Nov 2022 17:03:50 +0000 + fb02593b-fe26-48f3-aa84-9c95144fb169 + https://www.npr.org/2022/11/15/1136883659/freddie-johnson + no + Freddie Johnson + 1331 + 2865 + no + full +

Sign up for Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org.]]>
+ +
+ + Craig Robinson + Office and host of Peacock's Harlem Globetrotters: Play It Forward, plays our game about Craigslist. Joining him are panelists Tom Papa; Negin Farsad and Brian Babylon.]]> + Sat, 12 Nov 2022 17:15:45 +0000 + a59c695c-ce17-449b-9ffd-d5ac7df9a238 + https://www.npr.org/2022/11/09/1135596958/craig-robison-the-office-darryl-music-chicago + no + Craig Robinson + 1330 + + 2891 + no + full + Office and host of Peacock's Harlem Globetrotters: Play It Forward, plays our game about Craigslist. Joining him are panelists Tom Papa; Negin Farsad and Brian Babylon.]]> + + + + Behind the scenes with Peter and Lillian + + Wed, 09 Nov 2022 23:26:08 +0000 + bb735a89-8081-47ab-afa0-1f6d0bbfaec3 + https://www.npr.org/2022/11/09/1135596710/behind-the-scenes-with-peter-and-lillian + no + Behind the scenes with Peter and Lillian + 1329 + 838 + no + full + + + + + Amber Ruffin + The Amber Ruffin Show on Peacock and the purple M&M, plays our game called "Roughin' It" Three questions about camping. Joining in are panelists Karen Chee, Hari Kondabolu and Maz Jobrani.
Subscribe to Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org/waitwait.]]>
+ Sat, 05 Nov 2022 16:26:33 +0000 + 0ae327be-38ee-490f-afe6-b6539332762b + https://www.npr.org/2022/11/04/1134254076/amber-ruffin-peacock-purple-m-and-m + no + Amber Ruffin + 1328 + + 2914 + no + full + The Amber Ruffin Show on Peacock and the purple M&M, plays our game called "Roughin' It" Three questions about camping. Joining in are panelists Karen Chee, Hari Kondabolu and Maz Jobrani.
Subscribe to Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org/waitwait.]]>
+ +
+ + Hasan Minhaj + Patriot Act, plays our game about former Patriot Rob Gronkowski. Joining him are panelists Helen Hong, Mo Rocca, and Shane O'Neill.]]> + Sat, 29 Oct 2022 15:47:07 +0000 + f400235b-847a-4853-9db2-51df2591231c + https://www.npr.org/2022/10/25/1131475743/hasan-minhaj-netflix-kings-jester + no + Hasan Minhaj + 1327 + + 2878 + no + full + Patriot Act, plays our game about former Patriot Rob Gronkowski. Joining him are panelists Helen Hong, Mo Rocca, and Shane O'Neill.]]> + + + + Everyone & The Supernatural + + Wed, 26 Oct 2022 16:37:52 +0000 + a0a6ee55-032f-4cbc-9522-5e51100c9bb1 + https://www.npr.org/2022/10/25/1131475540/ghost-hunting-lighthouse-keepers + no + Everyone & The Supernatural + 1326 + + 1057 + no + full + + + + + Dr. Ibram X. Kendi, a real genius + + Sat, 22 Oct 2022 16:00:08 +0000 + 9f99e4eb-4f69-47fb-b973-95be5416bf55 + https://www.npr.org/2022/10/03/1126619472/dr-ibram-x-kendi-a-real-genius + no + Dr. Ibram X. Kendi, a real genius + 1325 + + 2953 + no + full + + + + + Vivek Murthy + + Sat, 15 Oct 2022 15:52:46 +0000 + 0f140b77-18c8-4c6f-b8de-595bbbe5bd75 + https://www.npr.org/2022/10/03/1126619233/vivek-murthy + no + Vivek Murthy + 1324 + 2879 + no + full + + + + + Behind the scenes with Peter Sagal and Emma Choi + + Wed, 12 Oct 2022 14:13:06 +0000 + 82c22bd1-5117-479b-9473-e89a76bf34e5 + https://www.npr.org/2022/10/07/1127600905/wait-wait-dont-tell-me-peter-sagal-emma-choi + no + Behind the scenes with Peter Sagal and Emma Choi + 1323 + + 1222 + no + full + + + + + Ralph Macchio + + Sat, 08 Oct 2022 16:22:39 +0000 + 337cbbce-49ad-475c-b666-68291130f434 + https://www.npr.org/2022/10/03/1126618998/ralph-macchio-cobra-kai-netflix + no + Ralph Macchio + 1322 + + 2905 + no + full + + + + + Julio Torres + Los Espookys, plays our game about The Addams Family called, "Los Ookys." Joining him are panelists Tom Papa, Dulcé Sloan, and Hari Kondabolu.]]> + Sat, 01 Oct 2022 16:07:48 +0000 + 96b346b9-4fc4-4117-9768-619bdb55172e + https://www.npr.org/2022/09/27/1125421675/julio-torres-los-espookys-hbo + no + Julio Torres + 1321 + + 2831 + no + full + Los Espookys, plays our game about The Addams Family called, "Los Ookys." Joining him are panelists Tom Papa, Dulcé Sloan, and Hari Kondabolu.]]> + + + + Everyone & Spoons + + Wed, 28 Sep 2022 14:00:15 +0000 + 1baa6fb7-ac8c-4c77-9531-b88fe04b3302 + https://www.npr.org/2022/09/27/1125421344/how-lalese-stamps-became-a-ceramics-celebrity + no + Everyone & Spoons + 1320 + + 786 + no + full + + + + + Michael Strahan + Good Morning America host, Michael plays our game called "Strahan? Meet Stray Hams" Three questions about wild hogs. He is joined by panelists Karen Chee, Negin Farsad, and Shane O'Neill.]]> + Sat, 24 Sep 2022 16:01:14 +0000 + 212a3095-86d8-448f-b50a-3a22d6df2425 + https://www.npr.org/2022/09/20/1124070886/michael-strahan-skincare-new-york-giants + no + Michael Strahan + 1319 + + 2894 + no + full + Good Morning America host, Michael plays our game called "Strahan? Meet Stray Hams" Three questions about wild hogs. He is joined by panelists Karen Chee, Negin Farsad, and Shane O'Neill.]]> + + + + Everyone & A Giant Worm + + Wed, 21 Sep 2022 14:00:14 +0000 + b351e290-80f0-41a5-920b-707c7eefbfde + https://www.npr.org/2022/09/20/1124073310/boy-found-big-worm-new-zealand + no + Everyone & A Giant Worm + 1318 + + 858 + no + full + + + + + Mo Amer + + Sat, 17 Sep 2022 16:00:10 +0000 + 0988865c-508b-424b-b3b0-9816c24ccc58 + https://www.npr.org/2022/09/13/1122767713/wait-wait-for-220917 + no + Mo Amer + 1317 + + 2859 + no + full + + + + + Everyone & Time Capsules + + Wed, 14 Sep 2022 17:31:56 +0000 + 9e6c328e-4ec6-4759-9340-d34dfe33950b + https://www.npr.org/2022/09/13/1122767012/andy-warhol-museum-pittsburgh-time-capsule + no + Everyone & Time Capsules + 1316 + + 946 + no + full + + + + + Abbi Jacobson +




Subscribe to Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org/waitwait.]]>
+ Sat, 10 Sep 2022 16:00:34 +0000 + fe3c7b5f-1d3c-46e9-b702-045b74c985bd + https://www.npr.org/2022/09/06/1121344648/abbi-jacobson-a-league-of-their-own + no + Abbi Jacobson + 1315 + + 2881 + no + full +




Subscribe to Wait Wait... Don't Tell Me+ via Apple Podcasts or at plus.npr.org/waitwait.]]>
+ +
+ + Everyone & Ostrich Escape + + Wed, 07 Sep 2022 13:59:17 +0000 + d33d9817-b1ca-4b36-9887-3478dffc00a5 + https://www.npr.org/2022/09/06/1121344199/comedian-josh-gondelman-joins-emma-to-outsmart-a-runaway-ostrich + no + Everyone & Ostrich Escape + 1314 + + 915 + no + full + + + + + Chris Estrada + This Fool, plays our game called, "This Fool, Meet April Fools!" He is joined by panelists Skyler Higley, Maeve Higgins and Mo Rocca.]]> + Sat, 03 Sep 2022 15:55:55 +0000 + 6b278567-8bbc-4de3-bc11-d4aa30afa2ac + https://www.npr.org/2022/08/02/1115198450/this-fool-hulu-chris-estrada-seinfeld + no + Chris Estrada + 1313 + + 2914 + no + full + This Fool, plays our game called, "This Fool, Meet April Fools!" He is joined by panelists Skyler Higley, Maeve Higgins and Mo Rocca.]]> + + + + Everyone & Names + + Wed, 31 Aug 2022 14:00:47 +0000 + ea51a3b2-f5bb-41a0-8240-b7f92e72f6db + https://www.npr.org/2022/08/02/1115199700/tattoo-design-tips-girl-knew-york + no + Everyone & Names + 1312 + + 907 + no + full + + + + + Congresswoman Eleanor Holmes Norton + + Sat, 27 Aug 2022 16:00:36 +0000 + 2fa15a53-bd77-47bd-be99-728b1755cf81 + https://www.npr.org/2022/08/02/1115198108/eleanor-holmes-norton-washington-dc-congress + no + Congresswoman Eleanor Holmes Norton + 1311 + + 2923 + no + full + + + + + Everyone & School + + Wed, 24 Aug 2022 14:00:39 +0000 + ed223e32-7223-4132-8924-255da8db6a7a + https://www.npr.org/2022/08/02/1115199545/best-back-to-school-tips-everyone-and-their-mom + no + Everyone & School + 1310 + + 1443 + no + full + + + + + More Best of Not My Job + + Sat, 20 Aug 2022 16:02:33 +0000 + 83d677d8-c808-4846-8e34-79812be6e981 + https://www.npr.org/2022/08/02/1115197883/wwdtm-220820 + no + More Best of Not My Job + 1309 + 2889 + no + full + + + + + It's an 'Everyone & Their Mom' family reunion! + + Wed, 17 Aug 2022 18:17:01 +0000 + 7adb4eb3-4a68-41e0-9013-d33af65aa82e + https://www.npr.org/2022/08/02/1115199406/everyone-and-their-mom-family-reunion + no + It's an 'Everyone & Their Mom' family reunion! + 1308 + + 949 + no + full + + + + + Best of Not My Job + + Sat, 13 Aug 2022 16:01:33 +0000 + d7ee3b59-4331-4728-80d4-f1ae3207b5e2 + https://www.npr.org/2022/08/02/1115197684/best-of-not-my-job + no + Best of Not My Job + 1307 + 2915 + no + full + + + + + Everyone & Asparagus + + Wed, 10 Aug 2022 14:00:58 +0000 + 25f57172-2731-4cd0-9974-2334bb93d3ef + https://www.npr.org/2022/08/02/1115199295/predict-the-future-asparagus-mystic-veg + no + Everyone & Asparagus + 1306 + + 943 + no + full + + + + + Robin Thede + A Black Lady Sketch Show, plays our game about Bob Ross, "A White Dude Painting Show," along with guest host Negin Farsad and panelists Adam Burke, Amy Dickinson and Hari Kondabolu.]]> + Sat, 06 Aug 2022 15:56:21 +0000 + 727943d3-8ad5-48aa-b515-020d3afbf26f + https://www.npr.org/2022/08/02/1115197498/robin-thede-a-black-lady-sketch-show + no + Robin Thede + 1305 + + 2835 + no + full + A Black Lady Sketch Show, plays our game about Bob Ross, "A White Dude Painting Show," along with guest host Negin Farsad and panelists Adam Burke, Amy Dickinson and Hari Kondabolu.]]> + + + + Everyone & Socks + + Wed, 03 Aug 2022 13:59:15 +0000 + e8f3076b-ccec-4400-b9eb-b1c8ed078f88 + https://www.npr.org/2022/08/02/1115198654/lifetime-guaratee-judge-marilyn-milian + no + Everyone & Socks + 1304 + + 716 + no + full + + + + + Jeremy Allen White + The Bear plays our game called "Please Look After This Bear" three questions about Paddington Bear. He is joined by guest host Tom Papa and panelists Helen Hong, Bobcat Goldthwait, and Faith Salie.]]> + Sat, 30 Jul 2022 17:23:02 +0000 + e6413e75-f6f5-499a-ad8d-b971e0d61b9e + https://www.npr.org/2022/07/25/1113450332/jeremy-allen-white-the-bear-hulu-chefs + no + Jeremy Allen White + 1303 + + 2859 + no + full + The Bear plays our game called "Please Look After This Bear" three questions about Paddington Bear. He is joined by guest host Tom Papa and panelists Helen Hong, Bobcat Goldthwait, and Faith Salie.]]> + + + + Everyone & Van Gogh + + Wed, 27 Jul 2022 14:00:41 +0000 + e6fb3b88-07bd-484d-a35e-f58da2929109 + https://www.npr.org/2022/07/25/1113449607/van-gogh-hidden-painting-tiktok-devin-halbal + no + Everyone & Van Gogh + 1302 + + 662 + no + full + + + + + Nathan Lane + + Sat, 23 Jul 2022 16:01:44 +0000 + 3af54edd-15c7-4f70-9500-d3aee8355409 + https://www.npr.org/2022/07/19/1112317705/nathan-lane-most-nominated-guest-actor-emmys + no + Nathan Lane + 1301 + + 2860 + no + full + + + + + Everyone & Minions + + Wed, 20 Jul 2022 14:02:50 +0000 + f518e734-8b2b-4616-bade-db65004cc1b7 + https://www.npr.org/2022/07/19/1112317555/jenny-slate-marcel-the-shell-minions + no + Everyone & Minions + 1300 + + 934 + no + full + + + + + Puja Patel + + Sat, 16 Jul 2022 15:53:57 +0000 + e15d0254-6ba5-42ad-a38f-6e59852ad4d1 + https://www.npr.org/2022/07/12/1111057119/puja-patel + no + Puja Patel + 1299 + + 2838 + no + full + + + + + Everyone & Games + + Wed, 13 Jul 2022 14:01:11 +0000 + 1f206077-b7b5-4ede-a62b-6e91b2341414 + https://www.npr.org/2022/07/12/1111056734/everyone-and-their-mom-wait-wait-secret-game-show + no + Everyone & Games + 1298 + + 673 + no + full + + + + + Del the Funky Homosapien + + Sat, 09 Jul 2022 16:01:26 +0000 + 94bb066d-5474-42ac-93ce-fe434454e4e7 + https://www.npr.org/2022/06/28/1108398040/del-the-funky-homosapien + no + Del the Funky Homosapien + 1297 + 2878 + no + full + + + + + Everyone & Cowboys + + Wed, 06 Jul 2022 14:00:48 +0000 + 09c005bf-8883-49fc-84a3-737ff0a985d2 + https://www.npr.org/2022/06/28/1108398301/cowboy-culture-orville-peck-the-compton-cowboys-randy-savvy + no + Everyone & Cowboys + 1296 + + 1609 + no + full + + + + + Darryl "Cornbread" McCray + + Sat, 02 Jul 2022 16:54:50 +0000 + 39e4d68a-14a7-4ed7-8e28-36ba16e29f70 + https://www.npr.org/2022/06/27/1107843977/darryl-cornbread-mccray + no + Darryl "Cornbread" McCray + 1295 + 2841 + no + full + + + + + Everyone & Lost and Found + + Wed, 29 Jun 2022 14:00:07 +0000 + f752a879-1643-47e5-9039-d0d40f3e4a80 + https://www.npr.org/2022/06/27/1107844771/what-we-lost-ubers-what-we-found + no + Everyone & Lost and Found + 1294 + + 1149 + no + full + + + + + Dan Perrault and Tony Yacenda + Players, on Paramount+ play our game about a C-sport: croquet. Joining them are panelists Peter Grosz, Adam Burke and Helen Hong.]]> + Sat, 25 Jun 2022 15:58:45 +0000 + 9b93bd02-dc08-438e-ae55-54281191bc34 + https://www.npr.org/2022/06/21/1106386916/dan-perrault-and-tony-yacenda + no + Dan Perrault and Tony Yacenda + 1293 + 2845 + no + full + Players, on Paramount+ play our game about a C-sport: croquet. Joining them are panelists Peter Grosz, Adam Burke and Helen Hong.]]> + + + + Everyone & Elvis + + Wed, 22 Jun 2022 14:00:23 +0000 + f0550ebb-3ca3-4b6e-ba9c-b20e912f7e09 + https://www.npr.org/2022/06/21/1106386782/no-more-elvis-presley-impersonator-wedding-las-vegas + no + Everyone & Elvis + 1292 + + 921 + no + full + + + + + Sean Hayes + + Sat, 18 Jun 2022 16:00:17 +0000 + a9d7d663-2cac-437e-8686-dbd3edfd1291 + https://www.npr.org/2022/06/14/1104971554/sean-hayes + no + Sean Hayes + 1291 + + 2909 + no + full + + + + + Everyone & Dolphins + + Wed, 15 Jun 2022 14:01:26 +0000 + e6b94802-fa41-43a1-9863-b8e318b44d96 + https://www.npr.org/2022/06/14/1104971354/dolphins-recognize-each-other-urine-study-stephen-f-austin + no + Everyone & Dolphins + 1290 + + 1000 + no + full + + + + + Kenan Thompson + + Sat, 11 Jun 2022 15:57:13 +0000 + aa94213b-6971-42c3-bc6d-8b1d39f6024d + https://www.npr.org/2022/06/07/1103508516/kenan-thompson + no + Kenan Thompson + 1289 + + 2895 + no + full + + + + + Everyone & Peter Sagal + Wait Wait... Don't Tell Me! host Peter Sagal pops by to share his favorites from the podcast so far. Turns out Peter loves Mountain Dew, Emma's Grandma's Kimchi, and unsolved ham mysteries.]]> + Wed, 08 Jun 2022 14:03:19 +0000 + 36f6d13c-57d4-4612-90b1-875b0193b436 + https://www.npr.org/2022/06/07/1103506745/best-of-peter-sagal-domee-shi-samin-nosrat + no + Everyone & Peter Sagal + 1288 + + 1266 + no + full + Wait Wait... Don't Tell Me! host Peter Sagal pops by to share his favorites from the podcast so far. Turns out Peter loves Mountain Dew, Emma's Grandma's Kimchi, and unsolved ham mysteries.]]> + + + + Best of NMJ June 2022 + + Sat, 04 Jun 2022 16:00:50 +0000 + a251c26f-3375-4ddb-8eff-e9c81b75f0c8 + https://www.npr.org/2022/05/31/1102174166/best-of-nmj-june-2022 + no + Best of NMJ June 2022 + 1287 + + 2825 + no + full + + + + + Everyone & Graduation + + Wed, 01 Jun 2022 14:11:26 +0000 + ec6275c1-6998-4a2b-b4e6-eebb9cfae5c0 + https://www.npr.org/2022/05/31/1102174465/2022-graduation-president-barack-obama-speechwriter + no + Everyone & Graduation + 1286 + + 1000 + no + full + + + + + Earlonne Woods and Nigel Poor + + Sat, 28 May 2022 15:50:57 +0000 + 84c8eb9c-e557-4909-b5ec-90613cc3bd34 + https://www.npr.org/2022/05/20/1100524480/earlonne-woods-and-nigel-poor + no + Earlonne Woods and Nigel Poor + 1285 + 2885 + no + full + + + + + Everyone & Cruise Ship Love + + Wed, 25 May 2022 13:12:22 +0000 + 6e92bc50-5ac0-4658-9bf0-8f22a24fa2f5 + https://www.npr.org/2022/05/20/1100524449/love-cruise-ships-guinness-world-record + no + Everyone & Cruise Ship Love + 1284 + + 914 + no + full + + + + + Mandy Moore + This Is Us, plays our game called This is Utz, three questions about the snack brand Utz. She is joined by panelists Alonzo Bodden, Maeve Higgins, and Tom Papa.]]> + Sat, 21 May 2022 15:58:10 +0000 + ccc9fa83-4c14-42dd-98e7-bca92949f95b + https://www.npr.org/2022/05/17/1099627383/mandy-moore + no + Mandy Moore + 1283 + + 2849 + no + full + This Is Us, plays our game called This is Utz, three questions about the snack brand Utz. She is joined by panelists Alonzo Bodden, Maeve Higgins, and Tom Papa.]]> + + + + Everyone & Skeletons + + Wed, 18 May 2022 12:00:32 +0000 + 75651d51-12bd-44df-bd15-0ed99fee929e + https://www.npr.org/2022/05/17/1099627268/medieval-times-knight-vegetarian-skeleton + no + Everyone & Skeletons + 1282 + + 965 + no + full + + + + + Hannah Einbinder + Hacks, Hannah Einbinder plays our game about life hacks. She is joined by panelists Peter Grosz, Faith Salie and Laci Mosley.]]> + Sat, 14 May 2022 16:00:41 +0000 + aa91fd70-cabf-469c-b46a-e94de9dd6b97 + https://www.npr.org/2022/05/10/1097962234/hannah-einbinder + no + Hannah Einbinder + 1281 + + 2871 + no + full + Hacks, Hannah Einbinder plays our game about life hacks. She is joined by panelists Peter Grosz, Faith Salie and Laci Mosley.]]> + + + + Everyone & Pay Phones + + Wed, 11 May 2022 14:31:51 +0000 + 8b6525fb-9d7c-4879-9b47-4b3949ab9c58 + https://www.npr.org/2022/05/10/1097965385/minnesota-pay-phone-samin-nosrat-salt-fat-acid-heat + no + Everyone & Pay Phones + 1280 + + 1008 + no + full + + + + + Adam Scott + Severance, plays our game about weddings and marriage proposals. He is joined by panelists Maz Jobrani, Emmy Blotnick and Paula Poundstone.]]> + Sat, 07 May 2022 16:01:43 +0000 + 5bb3320f-2c89-4875-86fc-983ca8df104f + https://www.npr.org/2022/05/03/1096128956/adam-scott + no + Adam Scott + 1279 + + 2820 + no + full + Severance, plays our game about weddings and marriage proposals. He is joined by panelists Maz Jobrani, Emmy Blotnick and Paula Poundstone.]]> + + + + Everyone & Corpse Flowers + + Wed, 04 May 2022 14:00:53 +0000 + 6fb020e0-411b-4d27-899e-dc678cfd6bfb + https://www.npr.org/2022/05/03/1096128413/corpse-flower-dating-smell-test + no + Everyone & Corpse Flowers + 1278 + + 710 + no + full + + + + + Myles Stubblefield + + Sat, 30 Apr 2022 16:00:41 +0000 + 39add13a-209c-4e16-bcae-4ae1e6efaa2f + https://www.npr.org/2022/04/26/1094895528/myles-stubblefield + no + Myles Stubblefield + 1277 + + 2827 + no + full + + + + + Everyone & Mice + + Wed, 27 Apr 2022 18:41:13 +0000 + 4e9a5fc8-5b1e-42f9-92c4-02ba2f2cf79b + https://www.npr.org/2022/04/26/1094895665/fda-mice-rat-kings-atsuko-okatsuka + no + Everyone & Mice + 1276 + + 1151 + no + full + + + + + Stephen Merchant + + Sat, 23 Apr 2022 15:56:06 +0000 + 81812e9d-f3ef-4031-ab6f-68f464b01568 + https://www.npr.org/2022/04/19/1093670892/stephen-merchant + no + Stephen Merchant + 1275 + + 2846 + no + full + + + + + Everyone & Secret Messages + + Wed, 20 Apr 2022 14:00:08 +0000 + 4563f817-9cbb-43ad-938e-233d2db1a277 + https://www.npr.org/2022/04/19/1093670338/darwin-lost-notebooks-domee-shi-turning-red + no + Everyone & Secret Messages + 1274 + + 1269 + no + full + + + + + Best of NMJ April 2022 + + Sat, 16 Apr 2022 16:00:25 +0000 + 13c1404e-295c-42a8-9ec3-718c2c9f3a1a + https://www.npr.org/2022/04/05/1091134655/best-of-nmj-april-2022 + no + Best of NMJ April 2022 + 1273 + + 2829 + no + full + + + + + Everyone & Robot Dogs + + Wed, 13 Apr 2022 14:09:54 +0000 + 57bca443-bd14-4a70-9af8-495d553008cd + https://www.npr.org/2022/04/11/1092162972/boston-dynamics-robot-dogs-pompeii + no + Everyone & Robot Dogs + 1272 + + 783 + no + full + + + + + Matt Walsh + + Sat, 09 Apr 2022 16:13:10 +0000 + 20cfe3ed-761b-4fae-a02e-981007d1302d + https://www.npr.org/2022/04/05/1091081502/matt-walsh + no + Matt Walsh + 1271 + + 2814 + no + full + + + + + Everyone & Boring People + + Wed, 06 Apr 2022 14:00:17 +0000 + 8a9f70ee-dfe2-493a-9548-b7b3153475eb + https://www.npr.org/2022/04/05/1091081488/boring-people + no + Everyone & Boring People + 1270 + + 1015 + no + full + + + + + Slash + sashes: beauty pageants. He is joined by panelists Tom Papa, Cristela Alonzo and Maeve Higgins.]]> + Sat, 02 Apr 2022 14:47:47 +0000 + 639261af-9509-4b97-9fbc-08a4d60b8dd5 + https://www.npr.org/2022/04/01/1090415984/slash + no + Slash + 1269 + + 2818 + no + full + sashes: beauty pageants. He is joined by panelists Tom Papa, Cristela Alonzo and Maeve Higgins.]]> + + + + Everyone & Fancy Ham + + Wed, 30 Mar 2022 14:14:35 +0000 + 490a4f55-1a2d-466c-9e10-b7e6b0b3864e + https://www.npr.org/2022/03/29/1089533105/everyone-fancy-ham + no + Everyone & Fancy Ham + 1268 + + 1193 + no + full + + + + + Dan Snow + + Sat, 26 Mar 2022 16:02:10 +0000 + e30f7336-50da-4434-aa9f-3ccc0b5d93a0 + https://www.npr.org/2022/03/25/1088901175/dan-snow + no + Dan Snow + 1267 + + 2830 + no + full + + + + + Everyone & Bears + + Wed, 23 Mar 2022 14:00:17 +0000 + f1ab00ac-ab14-4cb1-89a6-2b0a38da6926 + https://www.npr.org/2022/03/22/1088128286/bears-love-is-blind + no + Everyone & Bears + 1266 + + 1360 + no + full + + + + + Zazie Beetz + Atlanta, Zazie Beetz, plays our game called "Zazie Beetz, meet Sassy Beats!" Three stories about Rolling Stones drummer Charlie Watts. She is joined by panelists Roy Blount Jr, Helen Hong and Mo Rocca.]]> + Sat, 19 Mar 2022 16:21:42 +0000 + bc3ad9ba-47f5-49af-85c0-2216c0105a6f + https://www.npr.org/2022/03/18/1087659907/zazie-beetz + no + Zazie Beetz + 1265 + + 2862 + no + full + Atlanta, Zazie Beetz, plays our game called "Zazie Beetz, meet Sassy Beats!" Three stories about Rolling Stones drummer Charlie Watts. She is joined by panelists Roy Blount Jr, Helen Hong and Mo Rocca.]]> + + + + Everyone & Dinosaurs + + Wed, 16 Mar 2022 14:00:14 +0000 + dc6ed3de-db37-416a-8303-33b009baac1b + https://www.npr.org/2022/03/15/1086794313/everyone-dinosaurs + no + Everyone & Dinosaurs + 1264 + + 1212 + no + full + + + + + Elana Meyers Taylor + + Sat, 12 Mar 2022 17:27:16 +0000 + 76431c8b-c706-44ef-8616-f808957ce270 + https://www.npr.org/2022/03/11/1086215442/elana-meyers-taylor + no + Elana Meyers Taylor + 1263 + + 2818 + no + full + + + + + Everyone & The Dew + + Wed, 09 Mar 2022 15:00:19 +0000 + a0d85914-d53b-48d1-bf3b-d962fc4fb706 + https://www.npr.org/2022/03/07/1085016220/everyone-the-dew + no + Everyone & The Dew + 1262 + + 873 + no + full + + + + + Porsha Williams + + Sat, 05 Mar 2022 16:59:12 +0000 + bd0c19b0-9ece-4adf-92e7-4f00a5bce8b7 + https://www.npr.org/2022/03/04/1084712766/porsha-williams + no + Porsha Williams + 1261 + + 2869 + no + full + + + + + Everyone & Roy Choi + + Wed, 02 Mar 2022 15:00:14 +0000 + ab558ce0-8b5a-4c52-b0a4-94a295832074 + https://www.npr.org/2022/02/25/1083154152/kimchi-tips-roy-choi + no + Everyone & Roy Choi + 1260 + + 1069 + no + full + + + + + Best of Not My Job February 2022 + + Sat, 26 Feb 2022 17:17:16 +0000 + 95d8a253-9447-4346-aa52-61db928fe96a + https://www.npr.org/2022/02/25/1083125867/best-of-not-my-job-february-2022 + no + Best of Not My Job February 2022 + 1259 + + 2815 + no + full + + + + + Everyone & Monkey Business + + Wed, 23 Feb 2022 12:58:15 +0000 + b972db08-8523-4dfa-aa3f-3cc1d1d61047 + https://www.npr.org/2022/02/22/1082398220/everyone-and-monkey-business + no + Everyone & Monkey Business + 1258 + + 875 + no + full + + + + + Everyone & Their Mom + Everyone & Their Mom from Wait Wait...Don't Tell Me! Every week, there's an odd or funny story that everyone & their mom seems to be talking about. On this new show from your friends at Wait Wait... Don't Tell Me!, host Emma Choi takes that story and uses it as an excuse to talk to culture makers, Wait Wait panelists, and hilarious new comedians. New episodes every Wednesday, starting February 23rd. Tell your mom.]]> + Mon, 21 Feb 2022 13:00:08 +0000 + 9b94c40e-da04-414f-9990-bdb9025639f1 + https://www.npr.org/2022/02/18/1081659546/everyone-their-mom + no + Everyone & Their Mom + 1257 + + 99 + no + trailer + Everyone & Their Mom from Wait Wait...Don't Tell Me! Every week, there's an odd or funny story that everyone & their mom seems to be talking about. On this new show from your friends at Wait Wait... Don't Tell Me!, host Emma Choi takes that story and uses it as an excuse to talk to culture makers, Wait Wait panelists, and hilarious new comedians. New episodes every Wednesday, starting February 23rd. Tell your mom.]]> + + + + Marlon James + + Sat, 19 Feb 2022 17:18:32 +0000 + d8864c3f-b095-4801-837e-cf1bd4c3f669 + https://www.npr.org/2022/02/18/1081860469/marlon-james + no + Marlon James + 1256 + + 2857 + no + full + + + + + Patti Smith + + Sat, 12 Feb 2022 17:04:58 +0000 + 98e7ee36-56d0-41fd-9d83-e65de729c980 + https://www.npr.org/2022/02/11/1080303953/patti-smith + no + Patti Smith + 1255 + + 2850 + no + full + + + + + Shermann 'Dilla' Thomas + + Sat, 05 Feb 2022 17:05:50 +0000 + ba4cc4d1-b070-4790-b1f5-a7c1bf0b5fab + https://www.npr.org/2022/02/04/1078428743/shermann-dilla-thomas + no + Shermann 'Dilla' Thomas + 1254 + 2895 + no + full + + + + + Jeremy O. Harris + + Sat, 29 Jan 2022 17:00:36 +0000 + 3e25d9d6-7d88-4208-918e-ba81a171d002 + https://www.npr.org/2022/01/28/1076626735/jeremy-o-harris + no + Jeremy O. Harris + 1253 + 2907 + no + full + + + + + Brian Cox + Succession, answers three questions about suck sessions, or vacuum cleaning. He is joined by panelists Mo Rocca, Paula Poundstone and Cristela Alonzo.]]> + Sat, 22 Jan 2022 17:01:11 +0000 + 865cf43e-8288-48df-8302-54a01814a6e4 + https://www.npr.org/2022/01/21/1074998514/brian-cox + no + Brian Cox + 1252 + 2922 + no + full + Succession, answers three questions about suck sessions, or vacuum cleaning. He is joined by panelists Mo Rocca, Paula Poundstone and Cristela Alonzo.]]> + + + + Kacey Musgraves + + Sat, 15 Jan 2022 17:06:46 +0000 + 7f9fc568-6de2-4009-8384-e41ad96ae352 + https://www.npr.org/2022/01/14/1073293337/kacey-musgraves + no + Kacey Musgraves + 1251 + + 2880 + no + full + + + + + Woody Hoburg + + Sat, 08 Jan 2022 16:59:01 +0000 + 2fc55b5c-b430-4531-845e-56039388a5d1 + https://www.npr.org/2022/01/07/1071512040/woody-hoburg + no + Woody Hoburg + 1250 + 2944 + no + full + + + + + WWDTM Best of 2021 January + + Sat, 01 Jan 2022 05:10:45 +0000 + 2896ec62-a868-44d0-9486-3ac8f42fbde3 + https://www.npr.org/2021/12/29/1069000678/wwdtm-best-of-2021-january + no + WWDTM Best of 2021 January + 1249 + 2855 + no + full + + + + + WWDTM Best of 2021 Bonus Podcast + + Wed, 29 Dec 2021 17:30:55 +0000 + b4996ed8-97e8-4928-8d69-292b96f11cff + https://www.npr.org/2021/12/27/1068333165/wwdtm-best-of-2021-bonus-podcast + no + WWDTM Best of 2021 Bonus Podcast + 1248 + 1753 + no + full + + + + + Best of WWDTM December 2021 + + Sat, 25 Dec 2021 05:15:58 +0000 + 0db198b6-c671-4576-8394-d03ef7c1da5e + https://www.npr.org/2021/12/22/1067073106/best-of-wwdtm-december-2021 + no + Best of WWDTM December 2021 + 1247 + 2899 + no + full + + + + + Keke Palmer + Southern Belle Insults, plays our game about palm readers. She is joined by panelists Faith Salie, Mo Rocca, and Tom Bodett.]]> + Sat, 18 Dec 2021 17:27:27 +0000 + 72de1fa8-c7a1-4215-8b87-bd1d41c0bd9f + https://www.npr.org/2021/12/17/1065424189/keke-palmer + no + Keke Palmer + 1246 + 2810 + no + full + Southern Belle Insults, plays our game about palm readers. She is joined by panelists Faith Salie, Mo Rocca, and Tom Bodett.]]> + + + + Bashir and Sultan Salahuddin + South Side, play our game called "Welcome to the Real South Side!" Three questions about Antarctica. They are joined by panelists Cristela Alonzo, Luke Burbank and Maeve Higgins.]]> + Sat, 11 Dec 2021 17:00:54 +0000 + fd5d4097-87a8-48d9-a8f6-f2f6ac1f75ba + https://www.npr.org/2021/12/10/1063274801/bashir-and-sultan-salahuddin + no + Bashir and Sultan Salahuddin + 1245 + 2831 + no + full + South Side, play our game called "Welcome to the Real South Side!" Three questions about Antarctica. They are joined by panelists Cristela Alonzo, Luke Burbank and Maeve Higgins.]]> + + + + Audra McDonald + + Sat, 04 Dec 2021 17:00:56 +0000 + 7e4b3dd5-af3f-4062-8acf-0756720b09fa + https://www.npr.org/2021/12/03/1061428122/audra-mcdonald + no + Audra McDonald + 1244 + 2793 + no + full + + + + + Best of Thanksgiving 2021 + + Sat, 27 Nov 2021 17:00:42 +0000 + 5c0193f2-a40b-4b5b-884d-c722f01746bd + https://www.npr.org/2021/11/23/1058529654/best-of-thanksgiving-2021 + no + Best of Thanksgiving 2021 + 1243 + 2798 + no + full + + + + + Brook and Robin Lopez + + Sat, 20 Nov 2021 17:00:35 +0000 + 2028a76c-ee80-4757-951c-86e0de14fee9 + https://www.npr.org/2021/11/19/1057576360/brook-and-robin-lopez + no + Brook and Robin Lopez + 1242 + 2789 + no + full + + + + + Ed Begley Jr. + + Sat, 13 Nov 2021 17:00:46 +0000 + e73a837b-af31-4582-bac6-f4733b2b38b1 + https://www.npr.org/2021/11/12/1055465480/ed-begley-jr + no + Ed Begley Jr. + 1241 + 2863 + no + full + + + + + Chance The Rapper + + Sat, 06 Nov 2021 16:00:39 +0000 + fbdbdc87-4d56-4fe9-8067-ecf043ee62e8 + https://www.npr.org/2021/11/05/1053116356/chance-the-rapper + no + Chance The Rapper + 1240 + 2807 + no + full + + + + + P.K. Subban + + Sat, 30 Oct 2021 16:00:49 +0000 + 67b1a49f-a92e-4d0e-a09e-cf4ff9374daa + https://www.npr.org/2021/10/29/1050726499/p-k-subban + no + P.K. Subban + 1239 + 2828 + no + full + + + + + Ron and Clint Howard + The Boys, play our game called, "Mayberry, Maybe Not Berry." They are joined by panelists Maz Jobrani, Alonzo Bodden, and Karen Chee.]]> + Sat, 23 Oct 2021 16:00:16 +0000 + 155e83e9-467d-490b-9280-d65d95042e4a + https://www.npr.org/2021/10/23/1048615121/ron-and-clint-howard + no + Ron and Clint Howard + 1238 + 2943 + no + full + The Boys, play our game called, "Mayberry, Maybe Not Berry." They are joined by panelists Maz Jobrani, Alonzo Bodden, and Karen Chee.]]> + + + + Best of WWDTM October 2021 + + Sat, 16 Oct 2021 16:00:50 +0000 + 807c2d67-2d77-4706-8485-89a93a72ab6d + https://www.npr.org/2021/10/06/1043875977/best-of-wwdtm-october-2021 + no + Best of WWDTM October 2021 + 1237 + 2868 + no + full + + + + + Ilana Glazer + Broad City, plays our game about different kinds of glazers: donuts. She is joined by panelists Adam Burke, Helen Hong and Roxanne Roberts.]]> + Sat, 09 Oct 2021 16:00:53 +0000 + 08654a9b-1e3b-4a0e-b21e-a156d7a2bc12 + https://www.npr.org/2021/10/08/1044669800/ilana-glazer + no + Ilana Glazer + 1236 + 2868 + no + full + Broad City, plays our game about different kinds of glazers: donuts. She is joined by panelists Adam Burke, Helen Hong and Roxanne Roberts.]]> + + + + RZA + + Sat, 02 Oct 2021 16:00:58 +0000 + 890fd430-ca31-447f-9206-56ae952ef022 + https://www.npr.org/2021/10/01/1042621990/rza + no + RZA + 1235 + 2935 + no + full + + + + + Bowen Yang + Saturday Night Live cast member, plays our game called "Monday Through Friday Night Live" about local TV news. He is joined by panelists Hari Kondabolu, Roy Blount Jr and Faith Salie.]]> + Sat, 25 Sep 2021 16:00:52 +0000 + bbe2df6a-7390-4039-ad1c-1cb9a78072a2 + https://www.npr.org/2021/09/24/1040644216/bowen-yang + no + Bowen Yang + 2896 + no + full + Saturday Night Live cast member, plays our game called "Monday Through Friday Night Live" about local TV news. He is joined by panelists Hari Kondabolu, Roy Blount Jr and Faith Salie.]]> + + + + Yamiche Alcindor + + Sat, 18 Sep 2021 16:00:33 +0000 + bafd4c1b-96ef-4790-a7f5-061377556dc5 + https://www.npr.org/2021/09/17/1038488144/yamiche-alcindor + no + Yamiche Alcindor + 2919 + no + full + + + + + Antoni Porowski + + Sat, 11 Sep 2021 01:44:26 +0000 + b72817d6-0e95-453e-ac5d-1e894a816f75 + https://www.npr.org/2021/09/10/1036173872/antoni-porowski + no + Antoni Porowski + 2891 + no + full + + + + + Martin Short + Only Murders in the Building, plays our game about murders of crows. He is joined by panelists Peter Grosz, Helen Hong and Emmy Blotnick.]]> + Sat, 04 Sep 2021 16:00:18 +0000 + 9b50956b-2318-49bf-aac5-51fe80c2aac6 + https://www.npr.org/2021/09/03/1034248006/martin-short + no + Martin Short + 2946 + no + full + Only Murders in the Building, plays our game about murders of crows. He is joined by panelists Peter Grosz, Helen Hong and Emmy Blotnick.]]> + + + + Jane Kaczmarek + Malcolm In The Middle, plays our game called "Malcolm in the Middle, meet Finger in the Middle." She is joined by panelists Paula Poundstone, Josh Gondelman, and Maz Jobrani.]]> + Sat, 28 Aug 2021 16:00:24 +0000 + cab5e676-efe0-42e8-aa7c-5c685aa7f0f2 + https://www.npr.org/2021/08/27/1031926866/jane-kaczmarek + no + Jane Kaczmarek + 2853 + no + full + Malcolm In The Middle, plays our game called "Malcolm in the Middle, meet Finger in the Middle." She is joined by panelists Paula Poundstone, Josh Gondelman, and Maz Jobrani.]]> + + + + Best of WWDTM Summertime 2 + + Sat, 21 Aug 2021 16:00:47 +0000 + e60beb91-01fb-4b4a-92d5-06ae2eedf084 + https://www.npr.org/2021/08/20/1029908712/best-of-wwdtm-summertime-2 + no + Best of WWDTM Summertime 2 + 2860 + no + full + + + + + Best of WWDTM Summertime 1 + + Sat, 14 Aug 2021 16:00:46 +0000 + 3c5517d2-8b9d-44d9-ac6e-cbb3d8d0c694 + https://www.npr.org/2021/08/10/1026511507/best-of-wwdtm-summertime-1 + no + Best of WWDTM Summertime 1 + 2861 + no + full + + + + + Larry Krasner + + Sat, 07 Aug 2021 16:00:15 +0000 + 1485e407-7336-47f5-be39-fc30484ab8ea + https://www.npr.org/2021/08/06/1025708092/larry-krasner + no + Larry Krasner + 2861 + no + full + + + + + Stephen Fry + + Sat, 31 Jul 2021 16:00:27 +0000 + 16537372-310e-4664-8062-eef9f1cec688 + https://www.npr.org/2021/07/31/1023057288/stephen-fry + no + Stephen Fry + 2980 + no + full + + + + + Dr. Ellen Stofan + + Sat, 24 Jul 2021 17:16:18 +0000 + 589cdbe9-ea43-4f99-b1ec-5442e58a6d64 + https://www.npr.org/2021/07/23/1020024122/dr-ellen-stofan + no + Dr. Ellen Stofan + 2880 + no + full + + + + + Phillipa Soo + Hamilton, plays our game about a ton of ham. She is joined by panelists Gina Brillon, Helen Hong, and Mo Rocca.]]> + Sat, 17 Jul 2021 16:00:15 +0000 + ea35470e-5992-4570-922c-696295a97429 + https://www.npr.org/2021/07/16/1017177376/phillipa-soo + no + Phillipa Soo + 2899 + no + full + Hamilton, plays our game about a ton of ham. She is joined by panelists Gina Brillon, Helen Hong, and Mo Rocca.]]> + + + + Best of WWDTM July 2021 + + Sat, 10 Jul 2021 16:00:01 +0000 + 8b6b5f63-13a8-4f5b-b640-419bb847f17a + https://www.npr.org/2021/07/09/1014858059/best-of-wwdtm-july-2021 + no + Best of WWDTM July 2021 + 2882 + no + full + + + + + Roger Bennett + + Sat, 03 Jul 2021 16:00:41 +0000 + 4c268f3d-091a-47c3-9a96-f5543d893ef2 + https://www.npr.org/2021/07/02/1012804147/roger-bennett + no + Roger Bennett + 2836 + no + full + + + + + T-Pain + + Sat, 26 Jun 2021 16:00:54 +0000 + 4d184e09-a418-44f2-a4cd-47c2794f6e2b + https://www.npr.org/2021/06/25/1010490950/t-pain + no + T-Pain + 2865 + no + full + + + + + Anna Konkle + PEN15, plays our game about a drink that will give you a junior high: Red Bull. She is joined by panelists Dulcé Sloan, Faith Salie, and Luke Burbank.]]> + Sat, 19 Jun 2021 16:00:06 +0000 + b385ff3b-28fb-477a-b3c3-8b80aec5194b + https://www.npr.org/2021/06/18/1008253058/anna-konkle + no + Anna Konkle + 2970 + no + full + PEN15, plays our game about a drink that will give you a junior high: Red Bull. She is joined by panelists Dulcé Sloan, Faith Salie, and Luke Burbank.]]> + + + + Chris Bosh + + Sat, 12 Jun 2021 16:00:06 +0000 + df7dc04a-4172-40e8-bb65-0bcab162b143 + https://www.npr.org/2021/06/11/1005781698/chris-bosh + no + Chris Bosh + 2793 + no + full + + + + + WWDTM Best Of: Summer Fun Edition + + Sat, 05 Jun 2021 15:07:00 +0000 + 293cb68b-b0b8-4ee7-8fbe-0e05b5aa1d90 + https://www.npr.org/2021/06/03/1003120516/wwdtm-best-of-summer-fun-edition + no + WWDTM Best Of: Summer Fun Edition + 2926 + no + full + + + + + Joel McHale + Community, plays our game about community theater. He is joined by panelists Cristela Alonzo, Jessi Klein and Helen Hong.]]> + Sat, 29 May 2021 16:00:28 +0000 + affb45a6-20c7-4c9c-9285-4c79b6f4d59e + https://www.npr.org/2021/05/28/1001474796/joel-mchale + no + Joel McHale + 2880 + no + full + Community, plays our game about community theater. He is joined by panelists Cristela Alonzo, Jessi Klein and Helen Hong.]]> + + + + Jennifer Finney Boylan + + Sat, 22 May 2021 16:00:34 +0000 + 6d878f6d-9aa8-43f6-9111-c7fc2fded036 + https://www.npr.org/2021/05/21/999352237/jennifer-finney-boylan + no + Jennifer Finney Boylan + 2854 + no + full + + + + + Senator Elizabeth Warren + Persist and plays our game about "War and Peace." She is joined by panelists Karen Chee, Hari Kondabolu and Peter Grosz]]> + Sat, 15 May 2021 16:00:08 +0000 + b77c7d41-5474-48f6-a2db-5180fe7f63ad + https://www.npr.org/2021/05/14/997050412/senator-elizabeth-warren + no + Senator Elizabeth Warren + 2866 + no + full + Persist and plays our game about "War and Peace." She is joined by panelists Karen Chee, Hari Kondabolu and Peter Grosz]]> + + + + Symone + + Sat, 08 May 2021 16:00:20 +0000 + 005167c3-b401-48f4-ba12-7ac599f736ec + https://www.npr.org/2021/05/03/993164641/symone + no + Symone + 2855 + no + full + + + + + Tariq Trotter AKA Black Thought from The Roots + + Sat, 01 May 2021 16:00:28 +0000 + f6392b2b-009f-4977-829b-bd8822a4bb48 + https://www.npr.org/2021/04/30/992618948/tariq-trotter-aka-black-thought-from-the-roots + no + Tariq Trotter AKA Black Thought from The Roots + 2886 + no + full + + + + + André De Shields + + Sat, 24 Apr 2021 16:00:33 +0000 + d990e491-fd5e-4598-8d86-0a6f9da6955a + https://www.npr.org/2021/04/23/990406071/andre-de-shields + no + André De Shields + 2895 + no + full + + + + + Michelle Zauner + + Sat, 17 Apr 2021 16:00:21 +0000 + 57ca2fd2-58bf-4015-8828-520553c53eef + https://www.npr.org/2021/04/16/988279734/michelle-zauner + no + Michelle Zauner + 2877 + no + full + + + + + Ally Love + + Sat, 10 Apr 2021 16:00:23 +0000 + 125bd163-3d2b-42f5-9d19-0efa47dc2eb7 + https://www.npr.org/2021/04/09/985996542/ally-love + no + Ally Love + 2833 + no + full + + + + + Best of WWDTM April 2021 + + Sat, 03 Apr 2021 16:00:45 +0000 + 53995069-9687-4737-97dc-4781c1925a8b + https://www.npr.org/2021/04/02/984037792/best-of-wwdtm-april-2021 + no + Best of WWDTM April 2021 + 2863 + no + full + + + + + Kemp Powers + + Sat, 27 Mar 2021 16:00:38 +0000 + 4132b4d0-c8a7-45c3-9a36-cbecafc949d5 + https://www.npr.org/2021/03/26/981830888/kemp-powers + no + Kemp Powers + 2898 + no + full + + + + + Sam Sifton + + Sat, 20 Mar 2021 16:00:50 +0000 + f102f7c7-0e99-49cc-a3a3-4217e4c79ff5 + https://www.npr.org/2021/03/19/979444825/sam-sifton + no + Sam Sifton + 2838 + no + full + + + + + Desus Nice and The Kid Mero + Desus and Mero, play our Not My Job game. They join panelists Maz Jobrani, Karen Chee and Josh Gondelman, as well as Host Peter Sagal and Official Judge and Scorekeeper Bill Kurtis.]]> + Sat, 13 Mar 2021 17:00:23 +0000 + d01a1a1c-cb06-4cf3-9ed0-74fb0d016bff + https://www.npr.org/2021/03/12/976715877/desus-nice-and-the-kid-mero + no + Desus Nice and The Kid Mero + 2919 + no + full + Desus and Mero, play our Not My Job game. They join panelists Maz Jobrani, Karen Chee and Josh Gondelman, as well as Host Peter Sagal and Official Judge and Scorekeeper Bill Kurtis.]]> + + + + Jordan Jonas + + Sat, 06 Mar 2021 17:00:00 +0000 + 69f0d42a-010f-4b8d-9b81-689618238e72 + no + Jordan Jonas + 2816 + no + full + + + + + Dr. Swati Mohan + + Sat, 27 Feb 2021 17:00:00 +0000 + 426bcaf3-6b9f-4841-860a-0a7d2b0ee8d4 + no + Dr. Swati Mohan + 2905 + no + full + + + + + Best of WWDTM February 2020 + + Sat, 20 Feb 2021 17:00:00 +0000 + ddbfeb24-31b3-4549-a823-12a586c1d775 + no + Best of WWDTM February 2020 + 2906 + no + full + + + + + Abby Phillip + + Sat, 13 Feb 2021 17:00:00 +0000 + 9c867f9e-f3fe-467d-81a5-9017fc4b739c + no + Abby Phillip + 2938 + no + full + + + + + Owen Wilson + + Sat, 06 Feb 2021 17:00:00 +0000 + 414fbec1-f860-4e92-880a-d44fe0f1cf02 + no + Owen Wilson + 2911 + no + full + + + + + Jen Psaki + + Sat, 30 Jan 2021 17:00:00 +0000 + f1e9d12f-779c-4ca6-8235-0b0bbe403280 + no + Jen Psaki + 2952 + no + full + + + + + Mandy Patinkin and Kathryn Grody + + Sat, 23 Jan 2021 17:00:00 +0000 + 24a0a1af-cf0b-4b50-ba6c-04ad13dd8df1 + no + Mandy Patinkin and Kathryn Grody + 2952 + no + full + + + + + Phoebe Bridgers + + Sat, 16 Jan 2021 17:00:00 +0000 + a5fa7a2b-4704-4258-b274-fb29ea0fd76b + no + Phoebe Bridgers + 3003 + no + full + + + + + Jane Krakowski + + Sat, 09 Jan 2021 17:00:00 +0000 + 14bf9a12-9d63-40db-b0cc-d3419ab931c3 + no + Jane Krakowski + 2947 + no + full + + + + + WWDTM New Year 2021 + + Sat, 02 Jan 2021 17:00:00 +0000 + 30e5de7e-9a8c-4870-963c-3be2e26eb94a + no + WWDTM New Year 2021 + 2860 + no + full + + + + + WWDTM Christmas 2020 + + Sat, 26 Dec 2020 17:00:00 +0000 + 5956d260-83fc-4ba3-856e-40426235f3b5 + no + WWDTM Christmas 2020 + 2939 + no + full + + + + + Este and Alana Haim + + Sat, 19 Dec 2020 17:00:00 +0000 + a292086d-2e8c-4635-8b32-c902455a9fbd + no + Este and Alana Haim + 3010 + no + full + + + + + Wait Wait's Letter from the Editors VI + + Wed, 16 Dec 2020 18:57:00 +0000 + 74071ecd-a6c8-4b54-b8e0-6a6ec5450c54 + no + Wait Wait's Letter from the Editors VI + 398 + no + full + + + + + Robert Reich + + Sat, 12 Dec 2020 17:00:00 +0000 + e780106d-1759-458a-909f-5e675d1c47f1 + no + Robert Reich + 2998 + no + full + + + + + Wait Wait's Letter from the Editors V + + Wed, 09 Dec 2020 19:16:00 +0000 + 7380c75c-d45a-4a7e-a955-ad92143dead5 + no + Wait Wait's Letter from the Editors V + 474 + yes + full + + + + + Lindsey Vonn + + Sat, 05 Dec 2020 17:00:00 +0000 + d4253d0e-30c7-4e25-a7c3-99f555461bef + no + Lindsey Vonn + 2997 + no + full + + + + + Wait Wait's Letter from the Editors IV + + Wed, 02 Dec 2020 19:32:00 +0000 + 6d10c2e7-fe73-48f7-823d-144a90aca7de + no + Wait Wait's Letter from the Editors IV + 479 + no + full + + + + + WWDTM Thanksgiving 2020 + + Sat, 28 Nov 2020 17:00:00 +0000 + 3d3f161a-7519-4a9b-b90e-76f00811631f + no + WWDTM Thanksgiving 2020 + 3018 + no + full + + + + + Wait Wait's Letter from the Editors III + + Wed, 25 Nov 2020 19:07:00 +0000 + dcbdc5eb-30b4-4514-b3dd-ecc9c5122da9 + no + Wait Wait's Letter from the Editors III + 406 + no + full + + + + + Sarah Paulson + + Sat, 21 Nov 2020 17:00:00 +0000 + c5dc5c2b-75d8-41fd-b0ae-27cbf804dbc6 + no + Sarah Paulson + 2975 + no + full + + + + + Wait Wait's Letter from the Editors II + + Wed, 18 Nov 2020 17:59:00 +0000 + d38cc113-3480-4ea1-a4a8-3c73e562d3a0 + no + Wait Wait's Letter from the Editors II + 323 + no + full + + + + + Chelsea Peretti + + Sat, 14 Nov 2020 17:00:00 +0000 + 6d6d8a3b-00d6-4a30-b61c-8362675933ad + no + Chelsea Peretti + 2934 + no + full + + + + + Wait Wait's Letter from the Editors + + Wed, 11 Nov 2020 16:46:00 +0000 + 25f35f96-002d-4ae9-a526-2ca440eed8f5 + no + Wait Wait's Letter from the Editors + 376 + no + full + + + + + A'ja Wilson + + Sat, 07 Nov 2020 17:00:00 +0000 + a50ecb76-929f-4cb4-8a4a-34ae3c0a58fe + no + A'ja Wilson + 2907 + no + full + + + + + Mike Murphy + + Sat, 31 Oct 2020 16:00:00 +0000 + f4630480-5bce-43e6-ac1f-354a70e5660f + no + Mike Murphy + 2952 + no + full + + + + + Actor Doug Jones + + Sat, 24 Oct 2020 16:00:00 +0000 + 9b339c42-179b-4743-b5e5-b9fb52dafeca + no + Actor Doug Jones + 2982 + no + full + + + + + Best of WWDTM October 2020 + + Sat, 17 Oct 2020 16:00:00 +0000 + 4e5f2434-3dbe-4d62-9b9a-1d894f62a4b4 + no + Best of WWDTM October 2020 + 2982 + no + full + + + + + Jason Ward + + Sat, 10 Oct 2020 16:00:00 +0000 + 283fa607-fac0-42c8-8973-486dcc7b84c4 + no + Jason Ward + 2997 + no + full + + + + + Dame Karen Pierce + + Sat, 03 Oct 2020 16:00:00 +0000 + a59f139b-8240-4321-b5fb-8c8d2921fef3 + no + Dame Karen Pierce + 3004 + no + full + + + + + Jonna Mendez + + Sat, 26 Sep 2020 16:00:00 +0000 + 71081c4c-58da-40a3-a818-909afe18863f + no + Jonna Mendez + 2972 + no + full + + + + + Tituss Burgess + + Sat, 19 Sep 2020 16:00:00 +0000 + d2c93293-1d4c-4b76-92ea-7919728792bd + no + Tituss Burgess + 2789 + no + full + + + + + Tyra Banks + + Sat, 12 Sep 2020 16:00:00 +0000 + a54c8101-be09-474b-9efb-d2cf2e439844 + no + Tyra Banks + 2883 + no + full + + + + + Kellee Edwards + + Sat, 05 Sep 2020 16:00:00 +0000 + 20e75d5d-168d-4dbc-aff8-3a4910bc7165 + no + Kellee Edwards + 2868 + no + full + + + + + Cecily Strong + + Sat, 29 Aug 2020 16:00:00 +0000 + 3501de41-f173-4ff5-bacf-56bb3b87a133 + no + Cecily Strong + 2807 + no + full + + + + + Best of WWDTM August 2020 + + Sat, 22 Aug 2020 16:00:00 +0000 + 1fee804d-79ad-46ec-a9f3-0737eb084e02 + no + Best of WWDTM August 2020 + 2823 + no + full + + + + + Best of WWDTM Mo Rocca Revealed + + Sat, 15 Aug 2020 16:00:00 +0000 + 28b4e8a9-bf74-4f5f-adb3-eb03da638512 + no + Best of WWDTM Mo Rocca Revealed + 2831 + no + full + + + + + Bryan Cranston + + Sat, 08 Aug 2020 16:00:00 +0000 + 623b04b0-439a-4c8d-b338-2f0ee6464e07 + no + Bryan Cranston + 2796 + no + full + + + + + Ramy Youssef + + Sat, 01 Aug 2020 16:00:00 +0000 + 1dab26dc-a02c-4352-8057-0380a2cce384 + no + Ramy Youssef + 2919 + no + full + + + + + Padma Lakshmi + + Sat, 25 Jul 2020 16:00:00 +0000 + bbc5ffed-0d93-4f22-9552-30bee68a5c4d + no + Padma Lakshmi + 2928 + no + full + + + + + Maria Konnikova + + Sat, 18 Jul 2020 16:00:00 +0000 + 578fd11b-0448-406a-a63d-99b536c43318 + no + Maria Konnikova + 2874 + no + full + + + + + Bonus Podcast: Tur-Bill Tax + + Tue, 14 Jul 2020 22:55:00 +0000 + 531223ef-031b-4efb-a020-20e4ecbded1c + no + Bonus Podcast: Tur-Bill Tax + 74 + no + full + + + + + Jameela Jamil + + Sat, 11 Jul 2020 16:00:00 +0000 + 41868a31-df3e-48e6-b572-2ff0b0e4ae29 + no + Jameela Jamil + 2828 + no + full + + + + + WWDTM Quarantine Edition + + Sat, 04 Jul 2020 16:00:00 +0000 + c9b64706-efc5-4a8b-93ee-1f4323c8bfca + no + WWDTM Quarantine Edition + 2839 + no + full + + + + + Don Cheadle + + Sat, 27 Jun 2020 16:00:00 +0000 + 4662e484-dac3-4692-8e7a-e2b5ff176979 + no + Don Cheadle + 2869 + no + full + + + + + Dan Riskin + + Sat, 20 Jun 2020 16:00:00 +0000 + bc6029ab-2bec-4fe5-aa4f-d570a29257b7 + no + Dan Riskin + 2843 + no + full + + + + + Ashima Shiraishi + + Sat, 13 Jun 2020 16:00:00 +0000 + 98aaa454-fd63-4edc-8236-c2499f37a5fc + no + Ashima Shiraishi + 2833 + no + full + + + + + Sarah Cooper + + Sat, 06 Jun 2020 16:00:00 +0000 + db687322-1327-4ad2-a1cb-251a988da5fd + no + Sarah Cooper + 2828 + no + full + + + + + Tony Hawk and Jeff Tweedy + + Sat, 30 May 2020 16:00:00 +0000 + 0e0c5ee0-3733-45bc-979f-20b627a95c09 + no + Tony Hawk and Jeff Tweedy + 2896 + no + full + + + + + Christina Koch + + Sat, 23 May 2020 16:00:00 +0000 + b13d7726-d82a-4e00-b23c-d09e3e0d383e + no + Christina Koch + 2938 + no + full + + + + + Adam Rippon + + Sat, 16 May 2020 16:00:00 +0000 + 709d41f4-e5be-45ed-83fe-543282c2051d + no + Adam Rippon + 2912 + no + full + + + + + Bonus Podcast: Bill Kurtis In the Wild + + Tue, 12 May 2020 22:50:00 +0000 + 3903c8b4-b735-45c6-bf09-2b237b6d5e15 + no + Bonus Podcast: Bill Kurtis In the Wild + 139 + no + full + + + + + Samantha Bee + + Sat, 09 May 2020 16:00:00 +0000 + 29550aa1-e223-4496-b394-bdd685b0c08f + no + Samantha Bee + 2908 + no + full + + + + + Christine Baranski + + Sat, 02 May 2020 16:00:00 +0000 + 1c9b2452-02c9-4897-9f32-01156fc0bf8a + no + Christine Baranski + 2962 + no + full + + + + + Allison Janney + + Sat, 25 Apr 2020 16:00:00 +0000 + 01528da1-fa6f-4177-a19d-9aafb1926a86 + no + Allison Janney + 2956 + no + full + + + + + Tom Hanks At Home + + Sat, 18 Apr 2020 16:00:00 +0000 + 61215c56-640e-4fc0-bef3-d88810aa48b6 + no + Tom Hanks At Home + 2946 + no + full + + + + + Samin Nosrat + + Sat, 11 Apr 2020 16:00:00 +0000 + be3fe3c6-153b-4f9a-8082-56eedf498adc + no + Samin Nosrat + 2896 + no + full + + + + + Kumail Nanjiani and Emily V. Gordon + + Sat, 04 Apr 2020 16:00:00 +0000 + aef8d01b-c353-475f-862a-5568f1d67ca1 + no + Kumail Nanjiani and Emily V. Gordon + 2881 + no + full + + + + + Tim Gunn + + Sat, 28 Mar 2020 16:00:00 +0000 + 44b1eadf-d573-47b1-84bf-ab34aed89925 + no + Tim Gunn + 2822 + no + full + + + + + Bonus: The Viral Load + + Mon, 23 Mar 2020 21:39:00 +0000 + c97e0f29-bd09-46d0-af2d-5ea382483be8 + no + Bonus: The Viral Load + 193 + no + full + + + + + Stephen Colbert At Home + + Sat, 21 Mar 2020 16:00:00 +0000 + cdb93c98-42d2-446c-ac7d-6a4c2ea6e701 + no + Stephen Colbert At Home + 2858 + no + full + + + + + Big Boi + + Sat, 14 Mar 2020 16:00:00 +0000 + f8a58ace-b16a-457c-b2c8-d65adfde2861 + no + Big Boi + 2824 + no + full + + + + + Karamo Brown + + Sat, 07 Mar 2020 17:00:00 +0000 + f7bdbe9e-0d82-47ef-bcbc-79c11910b00d + no + Karamo Brown + 2811 + no + full + + + + + Will Arnett + + Sat, 29 Feb 2020 17:00:00 +0000 + 88661a25-e274-4380-997a-4f37b3c78fa3 + no + Will Arnett + 2917 + no + full + + + + + Best of Not My Job Feb2020 + + Sat, 22 Feb 2020 17:00:00 +0000 + 70197692-35ff-4bec-8561-0e0c67330948 + no + Best of Not My Job Feb2020 + 2950 + no + full + + + + + Barry Sonnenfeld Calling + + Sat, 15 Feb 2020 17:00:00 +0000 + 034852be-c88e-415e-81e1-35cd27f426ce + no + Barry Sonnenfeld Calling + 2856 + no + full + + + + + Tracy Letts + + Sat, 08 Feb 2020 17:00:00 +0000 + a881f6b0-ec34-4269-8d1e-54d5213005e4 + no + Tracy Letts + 2902 + no + full + + + + + Isabella Rossellini + + Sat, 01 Feb 2020 17:00:00 +0000 + 257cba6a-a25f-4763-8489-a6da26295ab5 + no + Isabella Rossellini + 2845 + no + full + + + + + Olivia Nuzzi + + Sat, 25 Jan 2020 17:00:00 +0000 + 65a23237-ff28-40b9-afee-41fd7d7c13ec + no + Olivia Nuzzi + 2836 + no + full + + + + + Alison Roman + + Sat, 18 Jan 2020 17:00:00 +0000 + 9b44ee36-5a2c-4a8b-864a-37d44638a930 + no + Alison Roman + 2848 + no + full + + + + + Ronan Farrow + + Sat, 11 Jan 2020 17:00:00 +0000 + ae901aa4-632c-4adb-aa04-12ba92266535 + no + Ronan Farrow + 2854 + no + full + + + + + WWDTM Best of the Decade + + Sat, 04 Jan 2020 17:00:00 +0000 + 1f350ca9-106a-4c7f-953b-22a69115e4f0 + no + WWDTM Best of the Decade + 2844 + no + full + + + + + Best of Not My Job 2019 + + Sat, 28 Dec 2019 17:00:00 +0000 + 615b5675-50af-4bd8-b939-3f2fdb5bdb2c + no + Best of Not My Job 2019 + 2892 + no + full + + + + + Jennifer Lee + + Sat, 21 Dec 2019 17:00:00 +0000 + 89014046-2b7a-435b-9eea-88877e7428bb + no + Jennifer Lee + 2924 + no + full + + + + + Sean Doolittle + + Sat, 14 Dec 2019 17:00:00 +0000 + d0f13fbc-98e8-435c-b3cc-a143b40d3a3e + no + Sean Doolittle + 2880 + no + full + + + + + Ali Wong + + Sat, 07 Dec 2019 17:00:00 +0000 + 10d1f786-4304-4dcc-9a56-fe256af24c0e + no + Ali Wong + 2919 + no + full + + + + + Alex Boyé + + Sat, 30 Nov 2019 17:00:00 +0000 + 6683cfb4-3f09-4b86-8473-580ff77f826b + no + Alex Boyé + 2937 + no + full + + + + + Elaine Welteroth + + Sat, 23 Nov 2019 17:00:00 +0000 + 08aa61ad-612c-44f7-b0ab-5cca26f2b7c7 + no + Elaine Welteroth + 2840 + no + full + + + + + Bonus Wait Wait: This week's Trump Dump + + Mon, 18 Nov 2019 18:38:00 +0000 + fa737a68-c1cb-4ca5-a4fa-c34412a30f5f + no + Bonus Wait Wait: This week's Trump Dump + 198 + no + full + + + + + Senator Tim Kaine + + Sat, 16 Nov 2019 17:00:00 +0000 + 78de894e-3c32-40e4-b587-1b2e0b818f06 + no + Senator Tim Kaine + 2854 + no + full + + + + + Leslie Odom, Jr. + + Sat, 09 Nov 2019 17:00:00 +0000 + 3864a441-93b7-49a2-9836-01da8188382b + no + Leslie Odom, Jr. + 2859 + no + full + + + + + Gloria Steinem + + Sat, 02 Nov 2019 16:00:00 +0000 + eab0419b-b9d2-40de-9f81-2a98428f9b10 + no + Gloria Steinem + 2888 + no + full + + + + + Nalini Nadkarni + + Sat, 26 Oct 2019 16:00:00 +0000 + 68d4f6fe-ee77-4520-802d-6002d26994ba + no + Nalini Nadkarni + 2869 + no + full + + + + + Renée Fleming + + Sat, 19 Oct 2019 16:00:00 +0000 + 77427bb2-db1d-44e2-9d2f-78a822ce4132 + no + Renée Fleming + 2890 + no + full + + + + + Regina King + + Sat, 12 Oct 2019 16:00:00 +0000 + 2479cafe-ad3e-4ef5-994b-954d7f4bc8c7 + no + Regina King + 2886 + no + full + + + + + Danica Patrick + + Sat, 05 Oct 2019 16:00:00 +0000 + 992595ca-24f9-4f00-921d-247dcb58a4a6 + no + Danica Patrick + 2895 + no + full + + + + + Charlie Day + + Sat, 28 Sep 2019 16:00:00 +0000 + 492c4179-6777-4507-a694-55b1c981509a + no + Charlie Day + 2870 + no + full + + + + + Zach Galifinakis + + Sat, 21 Sep 2019 16:00:00 +0000 + 9b6da58e-3244-4e8a-81ef-9f952c380e00 + no + Zach Galifinakis + 2875 + no + full + + + + + Tina Charles + + Sat, 14 Sep 2019 16:00:00 +0000 + 6796da8f-0dda-4603-804d-dd7c7ef0066b + no + Tina Charles + 2870 + no + full + + + + + Mary Wilson + + Sat, 07 Sep 2019 16:00:00 +0000 + dd139e2d-730c-4721-a932-a678871e48aa + no + Mary Wilson + 2865 + no + full + + + + + José Andrés + + Sat, 31 Aug 2019 16:00:00 +0000 + d4c94427-9bc5-412b-a4ab-ba2afa3b678f + no + José Andrés + 2889 + no + full + + + + + Best of WWDTM + + Sat, 24 Aug 2019 16:00:00 +0000 + f5224623-672d-4030-9de8-ea0dec2362de + no + Best of WWDTM + 2898 + no + full + + + + + Best of WWDTM + + Sat, 17 Aug 2019 16:00:00 +0000 + 52225aa2-8424-4820-a092-8d60f70b10d8 + no + Best of WWDTM + 2852 + no + full + + + + + Henry Winkler + + Sat, 10 Aug 2019 16:00:00 +0000 + 7c60085d-9b52-42da-b145-9bfee1a896a5 + no + Henry Winkler + 2878 + no + full + + + + + Anthony Anderson + + Sat, 03 Aug 2019 16:00:00 +0000 + e9000e11-3a99-4b0d-9d7d-90882263c0ee + no + Anthony Anderson + 2858 + no + full + + + + + Marin Alsop + + Sat, 27 Jul 2019 16:00:00 +0000 + f42c705b-ee54-4718-805d-4738c8c72924 + no + Marin Alsop + 2884 + no + full + + + + + Piper Kerman + + Sat, 20 Jul 2019 16:00:00 +0000 + 5fe8dd92-170d-48e9-b57d-17d73caaa084 + no + Piper Kerman + 2920 + no + full + + + + + Tiera Fletcher + + Sat, 13 Jul 2019 16:00:00 +0000 + 45f0ca0c-6cd6-4b6c-94b9-849f27919f2e + no + Tiera Fletcher + 2932 + no + full + + + + + Best of WWDTM + + Sat, 06 Jul 2019 16:00:00 +0000 + 940b79a5-8d75-443d-bf15-ec8e81dfc3a9 + no + Best of WWDTM + 2877 + no + full + + + + + Jennifer Weiner + + Sat, 29 Jun 2019 16:00:00 +0000 + 2b111598-ee97-428e-a379-af01d1797c49 + no + Jennifer Weiner + 2858 + no + full + + + + + Valerie Jarrett + + Sat, 22 Jun 2019 16:00:00 +0000 + bf9355d7-2da4-44c6-a29e-3d820f8092c7 + no + Valerie Jarrett + 2865 + no + full + + + + + Kristine Lilly + + Sat, 15 Jun 2019 16:00:00 +0000 + 65a0d76d-a1de-4eda-99a8-ebae963fb445 + no + Kristine Lilly + 2886 + no + full + + + + + Olivia Wilde + + Sat, 08 Jun 2019 16:00:00 +0000 + e159cf49-4bb5-4f0f-bb8d-d86f3fbd8ac3 + no + Olivia Wilde + 2883 + no + full + + + + + WWDTM Super Heros + + Sat, 01 Jun 2019 16:00:00 +0000 + 90ae36be-0dce-4d80-8214-7962b0ef53a5 + no + WWDTM Super Heros + 2884 + no + full + + + + + Kate Mulgrew + + Sat, 25 May 2019 16:00:00 +0000 + ef762e61-c8c6-4eb5-9a58-6b42e934e21c + no + Kate Mulgrew + 2852 + no + full + + + + + Lance Reddick + + Sat, 18 May 2019 16:00:00 +0000 + 1cb758c9-ee8b-4bc5-bc9c-b2dc7cd51506 + no + Lance Reddick + 2898 + no + full + + + + + Ozzie Smith + + Sat, 11 May 2019 16:00:00 +0000 + 284728fa-d48d-483f-9aab-872a682a088b + no + Ozzie Smith + 2909 + no + full + + + + + Steve Ballmer + + Sat, 04 May 2019 16:00:00 +0000 + 81733296-cb29-4806-a77d-124e1d9cb521 + no + Steve Ballmer + 2984 + no + full + + + +
+
\ No newline at end of file diff --git a/Sources/SyndiKit/Common/Entryable.swift b/Sources/SyndiKit/Common/Entryable.swift index 164dd02..b381ae7 100644 --- a/Sources/SyndiKit/Common/Entryable.swift +++ b/Sources/SyndiKit/Common/Entryable.swift @@ -5,7 +5,7 @@ public protocol Entryable { /// Unique Identifier of the Item. var id: EntryID { get } /// The URL of the item. - var url: URL { get } + var url: URL? { get } /// The title of the item. var title: String { get } /// HTML content of the item. diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift index 0dc2288..dc982bc 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift @@ -1,8 +1,6 @@ import Foundation public struct AtomEntry: Codable { - public static let defaultURL = URL(string: "/")! - /// A permanent, universally unique identifier for an entry. public let id: EntryID @@ -61,8 +59,8 @@ extension AtomEntry: Entryable { atomCategories } - public var url: URL { - links.first?.href ?? Self.defaultURL + public var url: URL? { + links.first?.href } public var contentHtml: String? { diff --git a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift index dee2e4f..e324310 100644 --- a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift +++ b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift @@ -2,7 +2,7 @@ import Foundation public struct JSONItem: Codable { public let guid: EntryID - public let url: URL + public let url: URL? public let title: String public let contentHtml: String? public let summary: String? diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift index 647bebb..bb31542 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift @@ -3,7 +3,7 @@ import XMLCoder public struct RSSItem: Codable { public let title: String - public let link: URL + public let link: URL? public let description: CData? public let guid: EntryID public let pubDate: Date? @@ -135,7 +135,7 @@ public struct RSSItem: Codable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) - link = try container.decode(URL.self, forKey: .link) + link = try container.decodeIfPresent(URL.self, forKey: .link) description = try container.decodeIfPresent(CData.self, forKey: .description) guid = try container.decode(EntryID.self, forKey: .guid) pubDate = try container.decodeDateIfPresentAndValid(forKey: .pubDate) @@ -297,7 +297,7 @@ extension RSSItem: Entryable { categoryTerms } - public var url: URL { + public var url: URL? { link } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 10daded..e7effb5 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -149,9 +149,11 @@ public extension WordPressPost { guard let modifiedDate = item.wpModifiedDate else { throw WordPressError.missingField(.modifiedDate) } + guard let link = item.link else { + throw WordPressError.missingField(.link) + } let title = item.title - let link = item.link let categoryTerms = item.categoryTerms let meta = item.wpPostMeta let pubDate = item.pubDate diff --git a/Tests/SyndiKitTests/RSSCodedTests.swift b/Tests/SyndiKitTests/RSSCodedTests.swift index 56f9dcc..384a3e5 100644 --- a/Tests/SyndiKitTests/RSSCodedTests.swift +++ b/Tests/SyndiKitTests/RSSCodedTests.swift @@ -406,6 +406,27 @@ public final class SyndiKitTests: XCTestCase { try assertInvalidGeoData(from: invalidCoords) } + func testPodcastMissingLink() throws { + guard let feed = try? Content.xmlFeeds["wait-wait-dont-tell-me"]?.get() else { + XCTFail("Missing Podcast \(name)") + return + } + + guard let rss = feed as? RSSFeed else { + XCTFail("Wrong Type \(name)") + return + } + + guard rss.channel.items.count > 193 else { + XCTFail("Missing Item \(name)") + return + } + + let item = rss.channel.items[193] + + XCTAssertNil(item.link) + } + private func assertInvalidGeoData(from xmlStr: String) throws { guard let data = xmlStr.data(using: .utf8) else { XCTFail("Expected data out of \(xmlStr)") From 49cf7fd734d7c6d5ba5c33a3c11d37ece1d97389 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 21 Dec 2023 13:08:28 -0500 Subject: [PATCH 02/18] updating workflow --- .github/workflows/syndikit.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/syndikit.yml b/.github/workflows/syndikit.yml index 3f5fcec..21ccbc2 100644 --- a/.github/workflows/syndikit.yml +++ b/.github/workflows/syndikit.yml @@ -115,11 +115,17 @@ jobs: watchName: "Apple Watch Ultra (49mm)" iPhoneName: "iPhone 14 Pro Max" - runs-on: macos-13 - xcode: "/Applications/Xcode_15.0.app" - iOSVersion: "17.0" + xcode: "/Applications/Xcode_15.0.1.app" + iOSVersion: "17.0.1" watchOSVersion: "10.0" + watchName: "Apple Watch Series 9 (41mm)" + iPhoneName: "iPhone 15 Pro" + - runs-on: macos-13 + xcode: "/Applications/Xcode_15.1.app" + iOSVersion: "17.2" + watchOSVersion: "10.2" watchName: "Apple Watch Ultra (49mm)" - iPhoneName: "iPhone 14 Pro Max" + iPhoneName: "iPhone 15 Pro Max" steps: - uses: actions/checkout@v3 - name: Cache swift package modules From 9dee5f0c96c12a6872eedb98098e3ac31f12ed9f Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 21 Dec 2023 13:12:12 -0500 Subject: [PATCH 03/18] adding more workflows --- .github/workflows/codeql.yml | 81 ++++++++++++++++++++++++++++++++ .github/workflows/dependabot.yml | 11 +++++ 2 files changed, 92 insertions(+) create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependabot.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ad06939 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,81 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '16 9 * * 1' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'swift' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..ac6621f --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From e16673fdeb74372c1b7c85e21ddabc9f6439bc0a Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 21 Dec 2023 13:13:21 -0500 Subject: [PATCH 04/18] fixup! adding more workflows --- .github/{workflows => }/dependabot.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{workflows => }/dependabot.yml (100%) diff --git a/.github/workflows/dependabot.yml b/.github/dependabot.yml similarity index 100% rename from .github/workflows/dependabot.yml rename to .github/dependabot.yml From 768370174d63944b24ab1ca37eec2d87f0eda8e6 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 21 Dec 2023 15:24:06 -0500 Subject: [PATCH 05/18] Update dependabot.yml --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ac6621f..ae22d88 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,7 @@ version: 2 updates: - - package-ecosystem: "" # See documentation for possible values + - package-ecosystem: "swift" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" From ca03f5fe4acfd79a282229811b7e4ff4de079fa3 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Wed, 3 Jan 2024 16:55:55 -0500 Subject: [PATCH 06/18] working on linting --- .swiftformat | 2 +- Package.swift | 2 +- Sources/SyndiKit/Character.swift | 2 +- Sources/SyndiKit/Collection.swift | 2 +- Sources/SyndiKit/Common/Author.swift | 12 +- .../SyndiKit/Common/Primitives/CData.swift | 11 +- .../Common/Primitives/XMLStringInt.swift | 8 +- Sources/SyndiKit/Decoding/AnyDecoding.swift | 2 +- .../Decoding/CustomDecoderSetup.swift | 2 +- .../Decoding/DateFormatterDecoder.swift | 20 +-- Sources/SyndiKit/Decoding/DecodableFeed.swift | 6 +- Sources/SyndiKit/Decoding/DecoderSetup.swift | 2 +- Sources/SyndiKit/Decoding/DecoderSource.swift | 4 +- Sources/SyndiKit/Decoding/Decoding.swift | 18 +-- Sources/SyndiKit/Decoding/DecodingError.swift | 6 +- Sources/SyndiKit/Decoding/SynDecoder.swift | 20 +-- Sources/SyndiKit/Decoding/TypeDecoder.swift | 2 +- Sources/SyndiKit/Dictionary.swift | 2 +- .../Formats/Blogs/SiteDirectory.swift | 22 +-- .../SyndiKit/Formats/Blogs/SiteLanguage.swift | 8 +- .../Blogs/SiteLanguageCategory.Site.swift | 6 +- .../Formats/Feeds/Atom/AtomEntry.swift | 30 ++-- .../Formats/Feeds/Atom/AtomFeed.swift | 33 ++--- .../Formats/Feeds/Atom/AtomMediaGroup.swift | 12 +- .../Formats/Feeds/JSONFeed/JSONFeed.swift | 6 +- .../Formats/Feeds/RSS/Enclosure.swift | 10 +- .../Formats/Feeds/RSS/RSSChannel.swift | 6 +- .../SyndiKit/Formats/Feeds/RSS/RSSFeed.swift | 6 +- .../SyndiKit/Formats/Feeds/RSS/RSSItem.swift | 94 ++++++------- .../Formats/Feeds/RSS/RSSItemCategory.swift | 12 +- .../Media/Podcast/PodcastChapters.swift | 8 +- .../Media/Podcast/PodcastEpisode.swift | 24 ++-- .../Media/Podcast/PodcastFunding.swift | 8 +- .../Podcast/PodcastLocation+GeoURI.swift | 4 +- .../Podcast/PodcastLocation+OsmQuery.swift | 14 +- .../Media/Podcast/PodcastLocation.swift | 12 +- .../Formats/Media/Podcast/PodcastLocked.swift | 8 +- .../Formats/Media/Podcast/PodcastPerson.swift | 16 +-- .../Formats/Media/Podcast/PodcastSeason.swift | 8 +- .../Media/Podcast/PodcastSoundbite.swift | 13 +- .../Media/Podcast/PodcastTranscript.swift | 14 +- .../Formats/Media/Wordpress/WPCategory.swift | 16 ++- .../Formats/Media/Wordpress/WPPostMeta.swift | 13 +- .../Formats/Media/Wordpress/WPTag.swift | 15 +- .../Media/Wordpress/WordPressPost.swift | 130 +++++++++--------- .../Formats/Media/YouTube/YouTubeID.swift | 31 +++-- .../Formats/Media/iTunes/iTunesDuration.swift | 10 +- .../Formats/Media/iTunes/iTunesOwner.swift | 8 +- Sources/SyndiKit/Formats/OPML/OPML+Body.swift | 11 ++ .../OPML/{OPMLHead.swift => OPML+Head.swift} | 7 +- .../{OPMLOutline.swift => OPML+Outline.swift} | 38 ++--- Sources/SyndiKit/Formats/OPML/OPML.swift | 12 +- Sources/SyndiKit/Formats/OPML/OPMLBody.swift | 11 -- ...PMLOutlineType.swift => OutlineType.swift} | 0 .../SyndicationUpdate/SyndicationUpdate.swift | 2 +- .../KeyedDecodingContainerProtocol.swift | 2 +- Sources/SyndiKit/Substring.SubSequence.swift | 6 +- Sources/SyndiKit/URL.swift | 2 +- Tests/SyndiKitTests/Content.Directories.swift | 4 +- .../Extensions/FileManager.swift | 4 +- Tests/SyndiKitTests/Extensions/JSONFeed.swift | 4 +- Tests/SyndiKitTests/Extensions/Sequence.swift | 10 +- .../Extensions/SiteCollection.swift | 4 +- Tests/SyndiKitTests/Extensions/String.swift | 4 +- Tests/SyndiKitTests/RSSCoded.Durations.swift | 4 +- 65 files changed, 430 insertions(+), 415 deletions(-) create mode 100644 Sources/SyndiKit/Formats/OPML/OPML+Body.swift rename Sources/SyndiKit/Formats/OPML/{OPMLHead.swift => OPML+Head.swift} (84%) rename Sources/SyndiKit/Formats/OPML/{OPMLOutline.swift => OPML+Outline.swift} (87%) delete mode 100644 Sources/SyndiKit/Formats/OPML/OPMLBody.swift rename Sources/SyndiKit/Formats/OPML/{OPMLOutlineType.swift => OutlineType.swift} (100%) diff --git a/.swiftformat b/.swiftformat index 7001c5e..2601bf7 100644 --- a/.swiftformat +++ b/.swiftformat @@ -2,6 +2,6 @@ --header strip --commas inline --disable wrapMultilineStatementBraces ---extensionacl on-extension +--extensionacl on-declarations --decimalgrouping 3,4 --exclude .build, DerivedData diff --git a/Package.swift b/Package.swift index c708bf2..b1facd9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,5 @@ // swift-tools-version:5.5 -// swiftlint:disable explicit_top_level_acl +// swiftlint:disable explicit_top_level_acl explicit_acl import PackageDescription let package = Package( diff --git a/Sources/SyndiKit/Character.swift b/Sources/SyndiKit/Character.swift index 6bbf343..aff55a0 100644 --- a/Sources/SyndiKit/Character.swift +++ b/Sources/SyndiKit/Character.swift @@ -1,7 +1,7 @@ import Foundation extension Character { - func asOsmType() -> PodcastLocation.OsmQuery.OsmType? { + internal func asOsmType() -> PodcastLocation.OsmQuery.OsmType? { .init(rawValue: String(self)) } } diff --git a/Sources/SyndiKit/Collection.swift b/Sources/SyndiKit/Collection.swift index a0beab0..586ff09 100644 --- a/Sources/SyndiKit/Collection.swift +++ b/Sources/SyndiKit/Collection.swift @@ -1,7 +1,7 @@ import Foundation extension Collection { - subscript(safe index: Index) -> Element? { + internal subscript(safe index: Index) -> Element? { indices.contains(index) ? self[index] : nil } } diff --git a/Sources/SyndiKit/Common/Author.swift b/Sources/SyndiKit/Common/Author.swift index 6ecb039..75bce78 100644 --- a/Sources/SyndiKit/Common/Author.swift +++ b/Sources/SyndiKit/Common/Author.swift @@ -2,12 +2,6 @@ import Foundation /// a person, corporation, or similar entity. public struct Author: Codable, Equatable { - init(name: String) { - self.name = name - email = nil - uri = nil - } - /// Conveys a human-readable name for the person. public let name: String @@ -16,4 +10,10 @@ public struct Author: Codable, Equatable { /// Contains a home page for the person. public let uri: URL? + + internal init(name: String) { + self.name = name + email = nil + uri = nil + } } diff --git a/Sources/SyndiKit/Common/Primitives/CData.swift b/Sources/SyndiKit/Common/Primitives/CData.swift index 3d7b0e8..13d4795 100644 --- a/Sources/SyndiKit/Common/Primitives/CData.swift +++ b/Sources/SyndiKit/Common/Primitives/CData.swift @@ -1,9 +1,8 @@ /// #CDATA XML element. +public typealias StringLiteralType = String public struct CData: Codable, ExpressibleByStringLiteral, Equatable { - public typealias StringLiteralType = String - - public init(stringLiteral value: String) { - self.value = value + public enum CodingKeys: String, CodingKey { + case value = "#CDATA" } public var description: String { @@ -13,8 +12,8 @@ public struct CData: Codable, ExpressibleByStringLiteral, Equatable { /// String value of the #CDATA element. public let value: String - enum CodingKeys: String, CodingKey { - case value = "#CDATA" + public init(stringLiteral value: String) { + self.value = value } public init(from decoder: Decoder) throws { diff --git a/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift b/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift index b25c06f..a01fd8b 100644 --- a/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift +++ b/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift @@ -1,14 +1,14 @@ /// XML Element which contains a `String` parsable into a `Integer`. public struct XMLStringInt: Codable, ExpressibleByIntegerLiteral { - public init(integerLiteral value: Int) { - self.value = value - } - public typealias IntegerLiteralType = Int /// The underlying `Int` value. public let value: Int + public init(integerLiteral value: Int) { + self.value = value + } + public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let stringValue = try container.decode(String.self) diff --git a/Sources/SyndiKit/Decoding/AnyDecoding.swift b/Sources/SyndiKit/Decoding/AnyDecoding.swift index cececec..7539f46 100644 --- a/Sources/SyndiKit/Decoding/AnyDecoding.swift +++ b/Sources/SyndiKit/Decoding/AnyDecoding.swift @@ -1,6 +1,6 @@ import Foundation -protocol AnyDecoding { +internal protocol AnyDecoding { static var label: String { get } func decodeFeed(data: Data) throws -> Feedable } diff --git a/Sources/SyndiKit/Decoding/CustomDecoderSetup.swift b/Sources/SyndiKit/Decoding/CustomDecoderSetup.swift index 5520520..fe82cac 100644 --- a/Sources/SyndiKit/Decoding/CustomDecoderSetup.swift +++ b/Sources/SyndiKit/Decoding/CustomDecoderSetup.swift @@ -1,5 +1,5 @@ import Foundation -protocol CustomDecoderSetup { +internal protocol CustomDecoderSetup { func setup(decoder: TypeDecoder) } diff --git a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift index 204b4d4..3845535 100644 --- a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift +++ b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift @@ -1,26 +1,26 @@ import Foundation -struct DateFormatterDecoder { - let formatters: [DateFormatter] - - public enum RSS { - static let dateFormatStrings = [ +internal struct DateFormatterDecoder { + internal enum RSS { + private static let dateFormatStrings = [ "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX", "yyyy-MM-dd'T'HH:mm:ssXXXXX", "E, d MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd HH:mm:ss" ] - public static let decoder = DateFormatterDecoder( + internal static let decoder = DateFormatterDecoder( basedOnFormats: Self.dateFormatStrings ) } + private let formatters: [DateFormatter] + internal init(formatters: [DateFormatter]) { self.formatters = formatters } - static func isoPOSIX(withFormat dateFormat: String) -> DateFormatter { + private static func isoPOSIX(withFormat dateFormat: String) -> DateFormatter { let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) formatter.locale = Locale(identifier: "en_US_POSIX") @@ -29,11 +29,11 @@ struct DateFormatterDecoder { return formatter } - init(basedOnFormats formats: [String]) { + internal init(basedOnFormats formats: [String]) { formatters = formats.map(Self.isoPOSIX(withFormat:)) } - public func decodeString(_ dateStr: String) -> Date? { + internal func decodeString(_ dateStr: String) -> Date? { for formatter in formatters { if let date = formatter.date(from: dateStr) { return date @@ -42,7 +42,7 @@ struct DateFormatterDecoder { return nil } - func decode(from decoder: Decoder) throws -> Date { + internal func decode(from decoder: Decoder) throws -> Date { let container = try decoder.singleValueContainer() let dateStr = try container.decode(String.self) diff --git a/Sources/SyndiKit/Decoding/DecodableFeed.swift b/Sources/SyndiKit/Decoding/DecodableFeed.swift index 3f9ec36..4fdafff 100644 --- a/Sources/SyndiKit/Decoding/DecodableFeed.swift +++ b/Sources/SyndiKit/Decoding/DecodableFeed.swift @@ -1,16 +1,16 @@ import Foundation -protocol DecodableFeed: Decodable, Feedable { +internal protocol DecodableFeed: Decodable, Feedable { static var source: DecoderSetup { get } static var label: String { get } } extension DecodableFeed { - static func decoding(using decoder: TypeDecoder) -> Decoding { + internal static func decoding(using decoder: TypeDecoder) -> Decoding { Decoding(for: Self.self, using: decoder) } - static func anyDecoding(using decoder: TypeDecoder) -> AnyDecoding { + internal static func anyDecoding(using decoder: TypeDecoder) -> AnyDecoding { Self.decoding(using: decoder) } } diff --git a/Sources/SyndiKit/Decoding/DecoderSetup.swift b/Sources/SyndiKit/Decoding/DecoderSetup.swift index 4e5a904..cb4b24a 100644 --- a/Sources/SyndiKit/Decoding/DecoderSetup.swift +++ b/Sources/SyndiKit/Decoding/DecoderSetup.swift @@ -1,5 +1,5 @@ import Foundation -protocol DecoderSetup { +internal protocol DecoderSetup { var source: DecoderSource { get } } diff --git a/Sources/SyndiKit/Decoding/DecoderSource.swift b/Sources/SyndiKit/Decoding/DecoderSource.swift index dc756cf..335d3d4 100644 --- a/Sources/SyndiKit/Decoding/DecoderSource.swift +++ b/Sources/SyndiKit/Decoding/DecoderSource.swift @@ -1,10 +1,10 @@ import Foundation -enum DecoderSource: UInt8, DecoderSetup { +internal enum DecoderSource: UInt8, DecoderSetup { case json = 0x007B case xml = 0x003C - public var source: DecoderSource { + internal var source: DecoderSource { self } } diff --git a/Sources/SyndiKit/Decoding/Decoding.swift b/Sources/SyndiKit/Decoding/Decoding.swift index 16cf444..44f9410 100644 --- a/Sources/SyndiKit/Decoding/Decoding.swift +++ b/Sources/SyndiKit/Decoding/Decoding.swift @@ -1,21 +1,21 @@ import Foundation -struct Decoding: AnyDecoding { - func decodeFeed(data: Data) throws -> Feedable { - try decode(data: data) +internal struct Decoding: AnyDecoding { + internal static var label: String { + DecodingType.label } - let decoder: TypeDecoder + internal let decoder: TypeDecoder - init(for _: DecodingType.Type, using decoder: TypeDecoder) { + internal init(for _: DecodingType.Type, using decoder: TypeDecoder) { self.decoder = decoder } - func decode(data: Data) throws -> DecodingType { - try decoder.decode(DecodingType.self, from: data) + internal func decodeFeed(data: Data) throws -> Feedable { + try decode(data: data) } - static var label: String { - DecodingType.label + internal func decode(data: Data) throws -> DecodingType { + try decoder.decode(DecodingType.self, from: data) } } diff --git a/Sources/SyndiKit/Decoding/DecodingError.swift b/Sources/SyndiKit/Decoding/DecodingError.swift index d98f02d..0e96ebf 100644 --- a/Sources/SyndiKit/Decoding/DecodingError.swift +++ b/Sources/SyndiKit/Decoding/DecodingError.swift @@ -1,7 +1,7 @@ import Foundation extension DecodingError { - struct Dictionary: Error { + private struct Dictionary: Error { internal init?(errors: [String: DecodingError]) { guard errors.count > 1 else { return nil @@ -9,10 +9,10 @@ extension DecodingError { self.errors = errors } - let errors: [String: DecodingError] + private let errors: [String: DecodingError] } - static func failedAttempts(_ errors: [String: DecodingError]) -> Self { + internal static func failedAttempts(_ errors: [String: DecodingError]) -> Self { let context = DecodingError.Context( codingPath: [], debugDescription: "Failed to decode data with several decoders.", diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index 720e6cc..eeced4a 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -12,18 +12,18 @@ import XMLCoder /// /// - ``decode(_:)`` public class SynDecoder { - static func setupJSONDecoder(_ decoder: JSONDecoder) { + internal static func setupJSONDecoder(_ decoder: JSONDecoder) { decoder.keyDecodingStrategy = .convertFromSnakeCase decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) } - static func setupXMLDecoder(_ decoder: XMLDecoder) { + internal static func setupXMLDecoder(_ decoder: XMLDecoder) { decoder.keyDecodingStrategy = .convertFromSnakeCase decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) decoder.trimValueWhitespaces = false } - init( + internal init( types: [DecodableFeed.Type]? = nil, defaultJSONDecoderSetup: ((JSONDecoder) -> Void)? = nil, defaultXMLDecoderSetup: ((XMLDecoder) -> Void)? = nil @@ -38,29 +38,29 @@ public class SynDecoder { self.init(types: nil, defaultJSONDecoderSetup: nil, defaultXMLDecoderSetup: nil) } - let defaultJSONDecoderSetup: (JSONDecoder) -> Void - let defaultXMLDecoderSetup: (XMLDecoder) -> Void - let types: [DecodableFeed.Type] + private let defaultJSONDecoderSetup: (JSONDecoder) -> Void + private let defaultXMLDecoderSetup: (XMLDecoder) -> Void + private let types: [DecodableFeed.Type] - static let defaultTypes: [DecodableFeed.Type] = [ + private static let defaultTypes: [DecodableFeed.Type] = [ RSSFeed.self, AtomFeed.self, JSONFeed.self ] - lazy var defaultXMLDecoder: XMLDecoder = { + private lazy var defaultXMLDecoder: XMLDecoder = { let decoder = XMLDecoder() self.defaultXMLDecoderSetup(decoder) return decoder }() - lazy var defaultJSONDecoder: JSONDecoder = { + private lazy var defaultJSONDecoder: JSONDecoder = { let decoder = JSONDecoder() self.defaultJSONDecoderSetup(decoder) return decoder }() - lazy var decodings: [DecoderSource: [String: AnyDecoding]] = { + private lazy var decodings: [DecoderSource: [String: AnyDecoding]] = { let decodings = types.map { type -> (DecoderSource, AnyDecoding) in let source = type.source let setup = type.source as? CustomDecoderSetup diff --git a/Sources/SyndiKit/Decoding/TypeDecoder.swift b/Sources/SyndiKit/Decoding/TypeDecoder.swift index 63ad550..c58e077 100644 --- a/Sources/SyndiKit/Decoding/TypeDecoder.swift +++ b/Sources/SyndiKit/Decoding/TypeDecoder.swift @@ -1,7 +1,7 @@ import Foundation import XMLCoder -protocol TypeDecoder { +internal protocol TypeDecoder { func decode(_ type: T.Type, from data: Data) throws -> T where T: DecodableFeed } diff --git a/Sources/SyndiKit/Dictionary.swift b/Sources/SyndiKit/Dictionary.swift index 096f91b..db1f51d 100644 --- a/Sources/SyndiKit/Dictionary.swift +++ b/Sources/SyndiKit/Dictionary.swift @@ -1,5 +1,5 @@ extension Dictionary { - mutating func formUnion( + internal mutating func formUnion( _ collection: SequenceType, key: Key ) where Value == Set, SequenceType.Element == ElementType { diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 58bf139..35689c6 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -17,8 +17,8 @@ public protocol SiteDirectory { var categories: CategorySequence { get } } -public extension SiteDirectory { - func sites( +extension SiteDirectory { + public func sites( withLanguage language: SiteLanguageType? = nil, withCategory category: SiteCategoryType? = nil ) -> SiteSequence { @@ -35,7 +35,7 @@ public struct SiteCollectionDirectory: SiteDirectory { public typealias CategorySequence = Dictionary.Values - let instance: Instance + private let instance: Instance public var languages: Dictionary< SiteLanguageType, SiteLanguage @@ -56,16 +56,16 @@ public struct SiteCollectionDirectory: SiteDirectory { instance.sites(withLanguage: language, withCategory: category) } - init(blogs: SiteCollection) { + internal init(blogs: SiteCollection) { instance = .init(blogs: blogs) } - struct Instance { - let allSites: [Site] - let languageDictionary: [SiteLanguageType: SiteLanguage] - let categoryDictionary: [SiteCategoryType: SiteCategory] - let languageIndicies: [SiteLanguageType: Set] - let categoryIndicies: [SiteCategoryType: Set] + private struct Instance { + internal let allSites: [Site] + internal let languageDictionary: [SiteLanguageType: SiteLanguage] + internal let categoryDictionary: [SiteCategoryType: SiteCategory] + internal let languageIndicies: [SiteLanguageType: Set] + internal let categoryIndicies: [SiteCategoryType: Set] public func sites( withLanguage language: SiteLanguageType?, @@ -107,7 +107,7 @@ public struct SiteCollectionDirectory: SiteDirectory { } // swiftlint:disable function_body_length - init(blogs: SiteCollection) { + internal init(blogs: SiteCollection) { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift index da16869..75733bb 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift @@ -1,9 +1,9 @@ public struct SiteLanguage { - init(content: SiteLanguageContent) { + public let type: SiteLanguageType + public let title: String + + internal init(content: SiteLanguageContent) { type = content.language title = content.title } - - public let type: SiteLanguageType - public let title: String } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift index c76bdab..7946977 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift @@ -1,13 +1,13 @@ import Foundation -public extension SiteLanguageCategory { - struct Site: Codable { +extension SiteLanguageCategory { + public struct Site: Codable { public let title: String public let author: String public let siteURL: URL public let feedURL: URL public let twitterURL: URL? - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case title case author case siteURL = "site_url" diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift index dc982bc..fd6d70a 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift @@ -1,6 +1,21 @@ import Foundation public struct AtomEntry: Codable { + public enum CodingKeys: String, CodingKey { + case id + case title + case published + case content + case updated + case links = "link" + case authors = "author" + case atomCategories = "category" + case youtubeVideoID = "yt:videoId" + case youtubeChannelID = "yt:channelId" + case creators = "dc:creator" + case mediaGroup = "media:group" + } + /// A permanent, universally unique identifier for an entry. public let id: EntryID @@ -37,21 +52,6 @@ public struct AtomEntry: Codable { /// Grouping of elements that are effectively the same content, /// yet different representations. public let mediaGroup: AtomMediaGroup? - - enum CodingKeys: String, CodingKey { - case id - case title - case published - case content - case updated - case links = "link" - case authors = "author" - case atomCategories = "category" - case youtubeVideoID = "yt:videoId" - case youtubeChannelID = "yt:channelId" - case creators = "dc:creator" - case mediaGroup = "media:group" - } } extension AtomEntry: Entryable { diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift index 7fbebed..36217b1 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift @@ -5,6 +5,19 @@ import Foundation /// Based on the /// [specifications here](https://datatracker.ietf.org/doc/html/rfc4287#section-4.1.2). public struct AtomFeed { + public enum CodingKeys: String, CodingKey { + case id + case title + case description + case subtitle + case published + case pubDate + case links = "link" + case entries = "entry" + case authors = "author" + case youtubeChannelID = "yt:channelId" + } + /// Identifies the feed using a universally unique and permanent URI. /// If you have a long-term, renewable lease on your Internet domain name, /// then you can feel free to use your website's address. @@ -37,22 +50,13 @@ public struct AtomFeed { /// YouTube channel ID, if from a YouTube channel. public let youtubeChannelID: String? - - enum CodingKeys: String, CodingKey { - case id - case title - case description - case subtitle - case published - case pubDate - case links = "link" - case entries = "entry" - case authors = "author" - case youtubeChannelID = "yt:channelId" - } } extension AtomFeed: DecodableFeed { + internal static let source: DecoderSetup = DecoderSource.xml + + internal static let label: String = "Atom" + public var summary: String? { description ?? subtitle } @@ -80,7 +84,4 @@ extension AtomFeed: DecodableFeed { public var syndication: SyndicationUpdate? { nil } - - static let source: DecoderSetup = DecoderSource.xml - static var label: String = "Atom" } diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift index 34b29fa..ebb59c8 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift @@ -1,15 +1,15 @@ import Foundation public struct AtomMediaGroup: Codable { - public let title: String? - public let contents: [AtomMedia] - public let thumbnails: [AtomMedia] - public let descriptions: [String] - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case title = "media:title" case descriptions = "media:description" case contents = "media:content" case thumbnails = "media:thumbnail" } + + public let title: String? + public let contents: [AtomMedia] + public let thumbnails: [AtomMedia] + public let descriptions: [String] } diff --git a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift index c04796f..86e1191 100644 --- a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift @@ -12,6 +12,9 @@ public struct JSONFeed { } extension JSONFeed: DecodableFeed { + internal static let source: DecoderSetup = DecoderSource.json + internal static let label: String = "JSON" + public var youtubeChannelID: String? { nil } @@ -50,7 +53,4 @@ extension JSONFeed: DecodableFeed { } return [author] } - - static let source: DecoderSetup = DecoderSource.json - static var label: String = "JSON" } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift index 5aa0655..41d2ea3 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift @@ -1,16 +1,16 @@ import Foundation public struct Enclosure: Codable { - public let url: URL - public let type: String - public let length: Int? - - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case url case type case length } + public let url: URL + public let type: String + public let length: Int? + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Self.CodingKeys.self) url = try container.decode(UTF8EncodedURL.self, forKey: .url).value diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift index 26a4327..29afbe2 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift @@ -49,7 +49,7 @@ public struct RSSChannel: Codable { public let podcastFundings: [PodcastFunding] public let podcastPeople: [PodcastPerson] - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case title case link case description @@ -75,8 +75,8 @@ public struct RSSChannel: Codable { } } -public extension RSSChannel { - var syndication: SyndicationUpdate? { +extension RSSChannel { + public var syndication: SyndicationUpdate? { SyndicationUpdate( period: syUpdatePeriod, frequency: syUpdateFrequency?.value diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift index e7f4ac3..250b5db 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift @@ -18,6 +18,9 @@ public struct RSSFeed { } extension RSSFeed: DecodableFeed { + internal static let source: DecoderSetup = DecoderSource.xml + public static let label: String = "RSS" + public var youtubeChannelID: String? { nil } @@ -60,7 +63,4 @@ extension RSSFeed: DecodableFeed { public var syndication: SyndicationUpdate? { channel.syndication } - - static let source: DecoderSetup = DecoderSource.xml - static var label: String = "RSS" } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift index bb31542..744515a 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift @@ -2,6 +2,53 @@ import Foundation import XMLCoder public struct RSSItem: Codable { + public enum CodingKeys: String, CodingKey { + case title + case link + case description + case guid + case pubDate + case categoryTerms = "category" + case enclosure + case contentEncoded = "content:encoded" + case content + case itunesTitle = "itunes:title" + case itunesEpisode = "itunes:episode" + case itunesAuthor = "itunes:author" + case itunesSubtitle = "itunes:subtitle" + case itunesSummary = "itunes:summary" + case itunesExplicit = "itunes:explicit" + case podcastPeople = "podcast:person" + case podcastTranscripts = "podcast:transcript" + case podcastChapters = "podcast:chapters" + case podcastSoundbites = "podcast:soundbite" + case podcastSeason = "podcast:season" + case itunesDuration = "itunes:duration" + case itunesImage = "itunes:image" + case creators = "dc:creator" + + case wpPostID = "wp:postId" + case wpPostDate = "wp:postDate" + case wpPostDateGMT = "wp:postDateGmt" + case wpModifiedDate = "wp:postModified" + case wpModifiedDateGMT = "wp:postModifiedGmt" + case wpPostName = "wp:postName" + case wpPostType = "wp:postType" + case wpPostMeta = "wp:postmeta" + case wpCommentStatus = "wp:commentStatus" + case wpPingStatus = "wp:pingStatus" + case wpAttachmentURL = "wp:attachmentUrl" + + case wpStatus = "wp:status" + case wpPostParent = "wp:postParent" + case wpMenuOrder = "wp:menuOrder" + case wpIsSticky = "wp:isSticky" + case wpPostPassword = "wp:postPassword" + + case mediaContent = "media:content" + case mediaThumbnail = "media:thumbnail" + } + public let title: String public let link: URL? public let description: CData? @@ -243,53 +290,6 @@ public struct RSSItem: Codable { CData.self, forKey: .wpPostPassword ) } - - enum CodingKeys: String, CodingKey { - case title - case link - case description - case guid - case pubDate - case categoryTerms = "category" - case enclosure - case contentEncoded = "content:encoded" - case content - case itunesTitle = "itunes:title" - case itunesEpisode = "itunes:episode" - case itunesAuthor = "itunes:author" - case itunesSubtitle = "itunes:subtitle" - case itunesSummary = "itunes:summary" - case itunesExplicit = "itunes:explicit" - case podcastPeople = "podcast:person" - case podcastTranscripts = "podcast:transcript" - case podcastChapters = "podcast:chapters" - case podcastSoundbites = "podcast:soundbite" - case podcastSeason = "podcast:season" - case itunesDuration = "itunes:duration" - case itunesImage = "itunes:image" - case creators = "dc:creator" - - case wpPostID = "wp:postId" - case wpPostDate = "wp:postDate" - case wpPostDateGMT = "wp:postDateGmt" - case wpModifiedDate = "wp:postModified" - case wpModifiedDateGMT = "wp:postModifiedGmt" - case wpPostName = "wp:postName" - case wpPostType = "wp:postType" - case wpPostMeta = "wp:postmeta" - case wpCommentStatus = "wp:commentStatus" - case wpPingStatus = "wp:pingStatus" - case wpAttachmentURL = "wp:attachmentUrl" - - case wpStatus = "wp:status" - case wpPostParent = "wp:postParent" - case wpMenuOrder = "wp:menuOrder" - case wpIsSticky = "wp:isSticky" - case wpPostPassword = "wp:postPassword" - - case mediaContent = "media:content" - case mediaThumbnail = "media:thumbnail" - } } extension RSSItem: Entryable { diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift index 2f8bb18..048271c 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift @@ -1,4 +1,10 @@ public struct RSSItemCategory: Codable, EntryCategory { + internal enum CodingKeys: String, CodingKey { + case value = "#CDATA" + case domain + case nicename + } + public var term: String { value } @@ -7,12 +13,6 @@ public struct RSSItemCategory: Codable, EntryCategory { public let domain: String? public let nicename: String? - enum CodingKeys: String, CodingKey { - case value = "#CDATA" - case domain - case nicename - } - public init(value: String, domain: String? = nil, nicename: String? = nil) { self.value = value self.domain = domain diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift index 561012e..e843458 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift @@ -1,11 +1,11 @@ import Foundation public struct PodcastChapters: Codable, Equatable { - public let url: URL - public let type: MimeType - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case url case type } + + public let url: URL + public let type: MimeType } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift index d5b3488..77f4937 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift @@ -13,19 +13,19 @@ public protocol PodcastEpisode { var people: [PodcastPerson] { get } } -struct PodcastEpisodeProperties: PodcastEpisode { - public let title: String? - public let episode: Int? - public let author: String? - public let subtitle: String? - public let summary: String? - public let explicit: String? - public let duration: TimeInterval? - public let image: iTunesImage? - public let enclosure: Enclosure - public let people: [PodcastPerson] +internal struct PodcastEpisodeProperties: PodcastEpisode { + internal let title: String? + internal let episode: Int? + internal let author: String? + internal let subtitle: String? + internal let summary: String? + internal let explicit: String? + internal let duration: TimeInterval? + internal let image: iTunesImage? + internal let enclosure: Enclosure + internal let people: [PodcastPerson] - init?(rssItem: RSSItem) { + internal init?(rssItem: RSSItem) { guard let enclosure = rssItem.enclosure else { return nil } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift index 227ccb4..61dd52b 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift @@ -1,11 +1,11 @@ import Foundation public struct PodcastFunding: Codable, Equatable { - public let url: URL - public let description: String? - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case url case description = "" } + + public let url: URL + public let description: String? } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift index a5bbe92..6bc800b 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift @@ -1,7 +1,7 @@ import Foundation -public extension PodcastLocation { - struct GeoURI: Codable, Equatable, LosslessStringConvertible { +extension PodcastLocation { + public struct GeoURI: Codable, Equatable, LosslessStringConvertible { public let latitude: Double public let longitude: Double public let altitude: Double? diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift index 6eaa0de..be8c06d 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift @@ -1,20 +1,20 @@ import Foundation -public extension PodcastLocation { - struct OsmQuery: Codable, Equatable { - enum OsmType: String, Codable, CaseIterable { +extension PodcastLocation { + public struct OsmQuery: Codable, Equatable { + public enum OsmType: String, Codable, CaseIterable { case node = "N" case way = "W" case relation = "R" - static func isValid(_ rawValue: String) -> Bool { + internal static func isValid(_ rawValue: String) -> Bool { OsmType(rawValue: rawValue) != nil } } - let id: Int - let type: OsmType - let revision: Int? + public let id: Int + public let type: OsmType + public let revision: Int? public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift index df8c1c1..4517282 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift @@ -1,15 +1,15 @@ import Foundation public struct PodcastLocation: Codable, Equatable { - public let geo: GeoURI? - public let osmQuery: OsmQuery? - - public let name: String - - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case geo case osmQuery = "osm" case name = "" } + + public let geo: GeoURI? + public let osmQuery: OsmQuery? + + public let name: String } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift index b35c891..c62cdf3 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift @@ -1,14 +1,14 @@ import Foundation public struct PodcastLocked: Codable, Equatable { - public let owner: String? - public let isLocked: Bool - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case owner case isLocked = "" } + public let owner: String? + public let isLocked: Bool + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) owner = try container.decodeIfPresent(String.self, forKey: .owner) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift index 8ee3322..dcdf454 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift @@ -1,14 +1,7 @@ import Foundation public struct PodcastPerson: Codable, Equatable { - public let role: Role? - public let group: String? - public let href: URL? - public let img: URL? - - public let fullname: String - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case role case group case href @@ -16,6 +9,13 @@ public struct PodcastPerson: Codable, Equatable { case fullname = "" } + public let role: Role? + public let group: String? + public let href: URL? + public let img: URL? + + public let fullname: String + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) role = try container.decodeIfPresent(Role.self, forKey: .role) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift index e18ba77..226bb36 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift @@ -1,11 +1,11 @@ import Foundation public struct PodcastSeason: Codable, Equatable { - public let name: String? - public let number: Int - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case name case number = "" } + + public let name: String? + public let number: Int } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift index 3119f6a..4777633 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift @@ -1,15 +1,16 @@ import Foundation public struct PodcastSoundbite: Codable, Equatable { - public let startTime: TimeInterval - public let duration: TimeInterval - - public let title: String? - - enum CodingKeys: String, CodingKey { + public enum CodingKeys: String, CodingKey { case startTime case duration case title = "" } + + public let startTime: TimeInterval + public let duration: TimeInterval + + public let title: String? + } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift index bde41ac..67f5eba 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift @@ -1,6 +1,13 @@ import Foundation public struct PodcastTranscript: Codable, Equatable { + public enum CodingKeys: String, CodingKey { + case url + case type + case language + case rel + } + public enum Relationship: String, Codable { case captions } @@ -9,11 +16,4 @@ public struct PodcastTranscript: Codable, Equatable { public let type: MimeType public let language: String? public let rel: Relationship? - - enum CodingKeys: String, CodingKey { - case url - case type - case language - case rel - } } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift index 3c395a8..ae4bf37 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift @@ -1,19 +1,21 @@ import Foundation -public extension WordPressElements { - struct Category: Codable { - public let termID: Int - public let niceName: CData - public let parent: CData - public let name: String +public typealias WPCategory = WordPressElements.Category - enum CodingKeys: String, CodingKey { +// swiftlint:disable nesting +extension WordPressElements { + public struct Category: Codable { + internal enum CodingKeys: String, CodingKey { case termID = "wp:termId" case niceName = "wp:categoryNicename" case parent = "wp:categoryParent" case name = "wp:catName" } + public let termID: Int + public let niceName: CData + public let parent: CData + public let name: String public init(termID: Int, niceName: CData, parent: CData, name: String) { self.termID = termID self.niceName = niceName diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift index fd56724..f3537b3 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift @@ -1,15 +1,18 @@ import Foundation -public extension WordPressElements { - struct PostMeta: Codable { - public let key: CData - public let value: CData +public typealias WPPostMeta = WordPressElements.Category - enum CodingKeys: String, CodingKey { +// swiftlint:disable nesting +extension WordPressElements { + public struct PostMeta: Codable { + internal enum CodingKeys: String, CodingKey { case key = "wp:metaKey" case value = "wp:metaValue" } + public let key: CData + public let value: CData + public init(key: String, value: String) { self.key = .init(stringLiteral: key) self.value = .init(stringLiteral: value) diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift index 85f574e..1b82837 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift @@ -1,17 +1,20 @@ import Foundation -public extension WordPressElements { - struct Tag: Codable { - public let termID: Int - public let slug: CData - public let name: CData +public typealias WPTag = WordPressElements.Tag - enum CodingKeys: String, CodingKey { +// swiftlint:disable nesting +extension WordPressElements { + public struct Tag: Codable { + internal enum CodingKeys: String, CodingKey { case termID = "wp:termId" case slug = "wp:tagSlug" case name = "wp:tagName" } + public let termID: Int + public let slug: CData + public let name: CData + public init(termID: Int, slug: CData, name: CData) { self.termID = termID self.slug = slug diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index e7effb5..46e3b0b 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -7,7 +7,55 @@ public struct WordPressPost { public typealias CommentStatus = String public typealias PingStatus = String public typealias Status = String - init( + + public enum Field: Equatable { + case name + case title + case type + case link + case pubDate + case creator + case body + case tags + case categories + case meta + case status + case commentStatus + case pingStatus + case parentID + case menuOrder + case id + case isSticky + case postDate + case postDateGMT + case modifiedDate + case modifiedDateGMT + } + + public let name: String + public let title: String + public let type: PostType + public let link: URL + public let pubDate: Date? + public let creator: String + public let body: String + public let tags: [String] + public let categories: [String] + public let meta: [String: String] + public let status: Status + public let commentStatus: CommentStatus + public let pingStatus: PingStatus + public let parentID: Int? + public let menuOrder: Int? + public let id: Int + public let isSticky: Bool + public let postDate: Date + public let postDateGMT: Date? + public let modifiedDate: Date + public let modifiedDateGMT: Date? + public let attachmentURL: URL? + + internal init( name: String, title: String, type: PostType, @@ -23,7 +71,7 @@ public struct WordPressPost { pingStatus: PingStatus, parentID: Int?, menuOrder: Int?, - ID: Int, + id: Int, isSticky: Bool, postDate: Date, postDateGMT: Date?, @@ -46,7 +94,7 @@ public struct WordPressPost { self.pingStatus = pingStatus self.parentID = parentID self.menuOrder = menuOrder - self.ID = ID + self.id = id self.isSticky = isSticky self.postDate = postDate self.postDateGMT = postDateGMT @@ -54,62 +102,15 @@ public struct WordPressPost { self.modifiedDateGMT = modifiedDateGMT self.attachmentURL = attachmentURL } - - public let name: String - public let title: String - public let type: PostType - public let link: URL - public let pubDate: Date? - public let creator: String - public let body: String - public let tags: [String] - public let categories: [String] - public let meta: [String: String] - public let status: Status - public let commentStatus: CommentStatus - public let pingStatus: PingStatus - public let parentID: Int? - public let menuOrder: Int? - public let ID: Int - public let isSticky: Bool - public let postDate: Date - public let postDateGMT: Date? - public let modifiedDate: Date - public let modifiedDateGMT: Date? - public let attachmentURL: URL? - - enum Field: Equatable { - case name - case title - case type - case link - case pubDate - case creator - case body - case tags - case categories - case meta - case status - case commentStatus - case pingStatus - case parentID - case menuOrder - case ID - case isSticky - case postDate - case postDateGMT - case modifiedDate - case modifiedDateGMT - } } -enum WordPressError: Error, Equatable { +public enum WordPressError: Error, Equatable { case missingField(WordPressPost.Field) } -public extension WordPressPost { +extension WordPressPost { // swiftlint:disable:next cyclomatic_complexity function_body_length - init(item: RSSItem) throws { + public init(item: RSSItem) throws { guard let name = item.wpPostName else { throw WordPressError.missingField(.name) } @@ -137,8 +138,8 @@ public extension WordPressPost { guard let menuOrder = item.wpMenuOrder else { throw WordPressError.missingField(.menuOrder) } - guard let ID = item.wpPostID else { - throw WordPressError.missingField(.ID) + guard let id = item.wpPostID else { + throw WordPressError.missingField(.id) } guard let isSticky = item.wpIsSticky else { throw WordPressError.missingField(.isSticky) @@ -158,9 +159,12 @@ public extension WordPressPost { let meta = item.wpPostMeta let pubDate = item.pubDate - let categoryDictionary = Dictionary(grouping: categoryTerms, by: { - $0.domain - }) + let categoryDictionary = Dictionary( + grouping: categoryTerms, + by: { + $0.domain + } + ) modifiedDateGMT = item.wpModifiedDateGMT self.name = name.value @@ -182,7 +186,7 @@ public extension WordPressPost { self.pingStatus = pingStatus.value self.parentID = parentID self.menuOrder = menuOrder - self.ID = ID + self.id = id self.isSticky = (isSticky != 0) self.postDate = postDate postDateGMT = item.wpPostDateGMT @@ -193,16 +197,16 @@ public extension WordPressPost { extension WordPressPost: Hashable { public static func == (lhs: WordPressPost, rhs: WordPressPost) -> Bool { - lhs.ID == rhs.ID + lhs.id == rhs.id } public func hash(into hasher: inout Hasher) { - hasher.combine(ID) + hasher.combine(id) } } -public extension Entryable { - var wpPost: WordPressPost? { +extension Entryable { + public var wpPost: WordPressPost? { guard let rssItem = self as? RSSItem else { return nil } diff --git a/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift b/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift index 59f54a0..b36ec27 100644 --- a/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift +++ b/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift @@ -1,3 +1,19 @@ +internal struct YouTubeIDProperties: YouTubeID { + internal let videoID: String + + internal let channelID: String + + internal init?(entry: AtomEntry) { + guard + let channelID = entry.youtubeChannelID, + let videoID = entry.youtubeVideoID else { + return nil + } + self.channelID = channelID + self.videoID = videoID + } +} + /// Specific type abstracting the id properties a YouTube RSS Feed. /// ```xml /// 3hccNoPE59U @@ -10,18 +26,3 @@ public protocol YouTubeID { /// YouTube channel ID. var channelID: String { get } } - -struct YouTubeIDProperties: YouTubeID { - public let videoID: String - - public let channelID: String - - init?(entry: AtomEntry) { - guard let channelID = entry.youtubeChannelID, - let videoID = entry.youtubeVideoID else { - return nil - } - self.channelID = channelID - self.videoID = videoID - } -} diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift index 2fe71af..e4944d8 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift @@ -1,13 +1,9 @@ import Foundation // swiftlint:disable:next type_name public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { - public init(floatLiteral value: TimeInterval) { - self.value = value - } - public typealias FloatLiteralType = TimeInterval - static func timeInterval(_ timeString: String) -> TimeInterval? { + internal static func timeInterval(_ timeString: String) -> TimeInterval? { let timeStrings = timeString.components(separatedBy: ":").prefix(3) let doubles = timeStrings.compactMap(Double.init) guard doubles.count == timeStrings.count else { @@ -18,6 +14,10 @@ public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { } } + public init(floatLiteral value: TimeInterval) { + self.value = value + } + public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let stringValue = try container.decode(String.self) diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift index fa26208..b5e3383 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift @@ -1,10 +1,10 @@ // swiftlint:disable:next type_name public struct iTunesOwner: Codable { - public let name: String - public let email: String? - - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case name = "itunes:name" case email = "itunes:email" } + + public let name: String + public let email: String? } diff --git a/Sources/SyndiKit/Formats/OPML/OPML+Body.swift b/Sources/SyndiKit/Formats/OPML/OPML+Body.swift new file mode 100644 index 0000000..32eab57 --- /dev/null +++ b/Sources/SyndiKit/Formats/OPML/OPML+Body.swift @@ -0,0 +1,11 @@ +import Foundation + +extension OPML { + public struct Body: Codable, Equatable { + internal enum CodingKeys: String, CodingKey { + case outlines = "outline" + } + + public let outlines: [Outline] + } +} diff --git a/Sources/SyndiKit/Formats/OPML/OPMLHead.swift b/Sources/SyndiKit/Formats/OPML/OPML+Head.swift similarity index 84% rename from Sources/SyndiKit/Formats/OPML/OPMLHead.swift rename to Sources/SyndiKit/Formats/OPML/OPML+Head.swift index 7d62c8f..a2912b5 100644 --- a/Sources/SyndiKit/Formats/OPML/OPMLHead.swift +++ b/Sources/SyndiKit/Formats/OPML/OPML+Head.swift @@ -1,7 +1,7 @@ import Foundation -public extension OPML { - struct Head: Codable, Equatable { +extension OPML { + public struct Head: Codable, Equatable { public let title: String? public let dateCreated: String? public let dateModified: String? @@ -16,7 +16,8 @@ public extension OPML { public let windowBottom: Int? public let windowRight: Int? - enum CodingKeys: String, CodingKey { + // swiftlint:disable:next nesting + internal enum CodingKeys: String, CodingKey { case title case dateCreated case dateModified diff --git a/Sources/SyndiKit/Formats/OPML/OPMLOutline.swift b/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift similarity index 87% rename from Sources/SyndiKit/Formats/OPML/OPMLOutline.swift rename to Sources/SyndiKit/Formats/OPML/OPML+Outline.swift index ec098d5..5327051 100644 --- a/Sources/SyndiKit/Formats/OPML/OPMLOutline.swift +++ b/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift @@ -1,24 +1,8 @@ import Foundation -public extension OPML { - struct Outline: Codable, Equatable { - public let text: String - public let title: String? - public let description: String? - public let type: OutlineType? - public let url: URL? - public let htmlUrl: URL? - public let xmlUrl: URL? - public let language: String? - public let created: String? - public let categories: ListString? - public let isComment: Bool? - public let isBreakpoint: Bool? - public let version: String? - - public let outlines: [Outline]? - - enum CodingKeys: String, CodingKey { +extension OPML { + public struct Outline: Codable, Equatable { + public enum CodingKeys: String, CodingKey { case text case title case description @@ -35,5 +19,21 @@ public extension OPML { case outlines = "outline" } + + public let text: String + public let title: String? + public let description: String? + public let type: OutlineType? + public let url: URL? + public let htmlUrl: URL? + public let xmlUrl: URL? + public let language: String? + public let created: String? + public let categories: ListString? + public let isComment: Bool? + public let isBreakpoint: Bool? + public let version: String? + + public let outlines: [Outline]? } } diff --git a/Sources/SyndiKit/Formats/OPML/OPML.swift b/Sources/SyndiKit/Formats/OPML/OPML.swift index debc827..18403fa 100644 --- a/Sources/SyndiKit/Formats/OPML/OPML.swift +++ b/Sources/SyndiKit/Formats/OPML/OPML.swift @@ -1,14 +1,14 @@ import Foundation public struct OPML: Codable, Equatable { - public let version: String - - public let head: Head - public let body: Body - - enum CodingKeys: String, CodingKey { + internal enum CodingKeys: String, CodingKey { case version case head case body } + + public let version: String + + public let head: Head + public let body: Body } diff --git a/Sources/SyndiKit/Formats/OPML/OPMLBody.swift b/Sources/SyndiKit/Formats/OPML/OPMLBody.swift deleted file mode 100644 index 1e405e0..0000000 --- a/Sources/SyndiKit/Formats/OPML/OPMLBody.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -public extension OPML { - struct Body: Codable, Equatable { - public let outlines: [Outline] - - enum CodingKeys: String, CodingKey { - case outlines = "outline" - } - } -} diff --git a/Sources/SyndiKit/Formats/OPML/OPMLOutlineType.swift b/Sources/SyndiKit/Formats/OPML/OutlineType.swift similarity index 100% rename from Sources/SyndiKit/Formats/OPML/OPMLOutlineType.swift rename to Sources/SyndiKit/Formats/OPML/OutlineType.swift diff --git a/Sources/SyndiKit/Formats/SyndicationUpdate/SyndicationUpdate.swift b/Sources/SyndiKit/Formats/SyndicationUpdate/SyndicationUpdate.swift index 8df788f..6d80f45 100644 --- a/Sources/SyndiKit/Formats/SyndicationUpdate/SyndicationUpdate.swift +++ b/Sources/SyndiKit/Formats/SyndicationUpdate/SyndicationUpdate.swift @@ -26,7 +26,7 @@ public struct SyndicationUpdate: Codable, Equatable { /// to calculate the publishing schedule. public let base: Date? - init?( + internal init?( period: SyndicationUpdatePeriod? = nil, frequency: Int? = nil, base: Date? = nil diff --git a/Sources/SyndiKit/KeyedDecodingContainerProtocol.swift b/Sources/SyndiKit/KeyedDecodingContainerProtocol.swift index 20582b6..afaf1d2 100644 --- a/Sources/SyndiKit/KeyedDecodingContainerProtocol.swift +++ b/Sources/SyndiKit/KeyedDecodingContainerProtocol.swift @@ -1,7 +1,7 @@ import Foundation extension KeyedDecodingContainerProtocol { - func decodeDateIfPresentAndValid(forKey key: Key) throws -> Date? { + internal func decodeDateIfPresentAndValid(forKey key: Key) throws -> Date? { if let pubDateString = try decodeIfPresent(String.self, forKey: key), !pubDateString.isEmpty { diff --git a/Sources/SyndiKit/Substring.SubSequence.swift b/Sources/SyndiKit/Substring.SubSequence.swift index 38eafa7..1ecf7d8 100644 --- a/Sources/SyndiKit/Substring.SubSequence.swift +++ b/Sources/SyndiKit/Substring.SubSequence.swift @@ -1,17 +1,17 @@ import Foundation extension Substring.SubSequence { - func asDouble() -> Double? { + internal func asDouble() -> Double? { Double(self) } - func asInt() -> Int? { + internal func asInt() -> Int? { guard let double = Double(self) else { return nil } return Int(double) } - func asExactInt() -> Int? { + internal func asExactInt() -> Int? { guard let double = Double(self) else { return nil } return Int(exactly: double) diff --git a/Sources/SyndiKit/URL.swift b/Sources/SyndiKit/URL.swift index e19039e..fb278d9 100644 --- a/Sources/SyndiKit/URL.swift +++ b/Sources/SyndiKit/URL.swift @@ -1,7 +1,7 @@ import Foundation extension URL { - init?(strict string: String) { + internal init?(strict string: String) { guard string.starts(with: "http") else { return nil } diff --git a/Tests/SyndiKitTests/Content.Directories.swift b/Tests/SyndiKitTests/Content.Directories.swift index 24fd7eb..f40ea3c 100644 --- a/Tests/SyndiKitTests/Content.Directories.swift +++ b/Tests/SyndiKitTests/Content.Directories.swift @@ -1,8 +1,8 @@ import Foundation @testable import SyndiKit -internal extension Content { - enum Directories { +extension Content { + internal enum Directories { static let data = URL(fileURLWithPath: #file) .deletingLastPathComponent() .deletingLastPathComponent() diff --git a/Tests/SyndiKitTests/Extensions/FileManager.swift b/Tests/SyndiKitTests/Extensions/FileManager.swift index 7e4951a..9e5a351 100644 --- a/Tests/SyndiKitTests/Extensions/FileManager.swift +++ b/Tests/SyndiKitTests/Extensions/FileManager.swift @@ -1,7 +1,7 @@ import Foundation -internal extension FileManager { - func dataFromDirectory(at sourceURL: URL) throws -> [(String, Result)] { +extension FileManager { + internal func dataFromDirectory(at sourceURL: URL) throws -> [(String, Result)] { let urls = try contentsOfDirectory( at: sourceURL, includingPropertiesForKeys: nil, diff --git a/Tests/SyndiKitTests/Extensions/JSONFeed.swift b/Tests/SyndiKitTests/Extensions/JSONFeed.swift index cf44cda..e675421 100644 --- a/Tests/SyndiKitTests/Extensions/JSONFeed.swift +++ b/Tests/SyndiKitTests/Extensions/JSONFeed.swift @@ -1,8 +1,8 @@ import Foundation import SyndiKit -internal extension JSONFeed { - var homePageURLHttp: URL? { +extension JSONFeed { + internal var homePageURLHttp: URL? { var components = URLComponents(url: homePageUrl, resolvingAgainstBaseURL: false) components?.scheme = "http" return components?.url diff --git a/Tests/SyndiKitTests/Extensions/Sequence.swift b/Tests/SyndiKitTests/Extensions/Sequence.swift index 85950ba..c01ea43 100644 --- a/Tests/SyndiKitTests/Extensions/Sequence.swift +++ b/Tests/SyndiKitTests/Extensions/Sequence.swift @@ -1,5 +1,5 @@ -internal extension Sequence { - func mapPairResult( +extension Sequence { + internal func mapPairResult( _ transform: @escaping (Element) throws -> Success ) -> [(Element, Result)] { map { element in @@ -7,7 +7,7 @@ internal extension Sequence { } } - func mapResult( + internal func mapResult( _ transform: @escaping (Element) throws -> Success ) -> [Result] { map { element in @@ -15,7 +15,7 @@ internal extension Sequence { } } - func flatResultMapValue( + internal func flatResultMapValue( _ transform: @escaping (SuccessValue) throws -> NewSuccess ) -> [(SuccessKey, Result)] where Element == (SuccessKey, Result) { @@ -27,7 +27,7 @@ internal extension Sequence { } } - func flatResultMap( + internal func flatResultMap( _ transform: @escaping (Success) throws -> NewSuccess ) -> [Result] where Element == Result { diff --git a/Tests/SyndiKitTests/Extensions/SiteCollection.swift b/Tests/SyndiKitTests/Extensions/SiteCollection.swift index 2fde8e3..dee0918 100644 --- a/Tests/SyndiKitTests/Extensions/SiteCollection.swift +++ b/Tests/SyndiKitTests/Extensions/SiteCollection.swift @@ -1,8 +1,8 @@ import Foundation @testable import SyndiKit -internal extension SiteCollection { - init(contentsOf url: URL, using decoder: JSONDecoder = .init()) throws { +extension SiteCollection { + internal init(contentsOf url: URL, using decoder: JSONDecoder = .init()) throws { let data = try Data(contentsOf: url) self = try decoder.decode(SiteCollection.self, from: data) } diff --git a/Tests/SyndiKitTests/Extensions/String.swift b/Tests/SyndiKitTests/Extensions/String.swift index 6049af4..1391a9c 100644 --- a/Tests/SyndiKitTests/Extensions/String.swift +++ b/Tests/SyndiKitTests/Extensions/String.swift @@ -1,5 +1,5 @@ -internal extension String { - func trimAndNilIfEmpty() -> String? { +extension String { + internal func trimAndNilIfEmpty() -> String? { let text = trimmingCharacters(in: .whitespacesAndNewlines) return text.isEmpty ? nil : text } diff --git a/Tests/SyndiKitTests/RSSCoded.Durations.swift b/Tests/SyndiKitTests/RSSCoded.Durations.swift index 5dd1f0e..b4e99b3 100644 --- a/Tests/SyndiKitTests/RSSCoded.Durations.swift +++ b/Tests/SyndiKitTests/RSSCoded.Durations.swift @@ -1,7 +1,7 @@ import Foundation -internal extension SyndiKitTests { - static let durationSets: [String: [TimeInterval]] = [ +extension SyndiKitTests { + internal static let durationSets: [String: [TimeInterval]] = [ "empowerapps-show": [ 2_746, 3_500, From 0c203003195aba863de1619caef0561ed61d6ad7 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Wed, 3 Jan 2024 22:10:36 -0500 Subject: [PATCH 07/18] [skip ci] fixing linting --- .../Decoding/DateFormatterDecoder.swift | 8 +- Sources/SyndiKit/Decoding/SynDecoder.swift | 60 ++++----- .../Formats/Blogs/CategoryLanguage.swift | 8 +- Sources/SyndiKit/Formats/Blogs/Site.swift | 16 +-- .../SyndiKit/Formats/Blogs/SiteCategory.swift | 6 +- .../Formats/Blogs/SiteDirectory.swift | 116 +++++++++--------- .../Formats/Blogs/SiteDirectoryBuilder.swift | 10 +- ....swift => SiteLanguageCategory+Site.swift} | 0 .../SyndiKit/Formats/Feeds/RSS/EntryID.swift | 36 +++--- .../Formats/Feeds/RSS/RSSChannel.swift | 50 ++++---- .../Media/Podcast/PodcastEpisode.swift | 26 ++-- .../Podcast/PodcastLocation+OsmQuery.swift | 1 + .../Media/Podcast/PodcastSoundbite.swift | 3 +- .../Media/Wordpress/WordPressPost.swift | 36 +++--- .../Formats/Media/iTunes/iTunesDuration.swift | 22 ++-- Sources/SyndiKit/Formats/OPML/OPML+Body.swift | 1 + .../SyndiKit/Formats/OPML/OPML+Outline.swift | 2 + Sources/SyndiKit/Substring.SubSequence.swift | 8 +- 18 files changed, 207 insertions(+), 202 deletions(-) rename Sources/SyndiKit/Formats/Blogs/{SiteLanguageCategory.Site.swift => SiteLanguageCategory+Site.swift} (100%) diff --git a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift index 3845535..2cac0cc 100644 --- a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift +++ b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift @@ -20,6 +20,10 @@ internal struct DateFormatterDecoder { self.formatters = formatters } + internal init(basedOnFormats formats: [String]) { + formatters = formats.map(Self.isoPOSIX(withFormat:)) + } + private static func isoPOSIX(withFormat dateFormat: String) -> DateFormatter { let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) @@ -29,10 +33,6 @@ internal struct DateFormatterDecoder { return formatter } - internal init(basedOnFormats formats: [String]) { - formatters = formats.map(Self.isoPOSIX(withFormat:)) - } - internal func decodeString(_ dateStr: String) -> Date? { for formatter in formatters { if let date = formatter.date(from: dateStr) { diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index eeced4a..b9ce0ce 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -12,42 +12,16 @@ import XMLCoder /// /// - ``decode(_:)`` public class SynDecoder { - internal static func setupJSONDecoder(_ decoder: JSONDecoder) { - decoder.keyDecodingStrategy = .convertFromSnakeCase - decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) - } - - internal static func setupXMLDecoder(_ decoder: XMLDecoder) { - decoder.keyDecodingStrategy = .convertFromSnakeCase - decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) - decoder.trimValueWhitespaces = false - } - - internal init( - types: [DecodableFeed.Type]? = nil, - defaultJSONDecoderSetup: ((JSONDecoder) -> Void)? = nil, - defaultXMLDecoderSetup: ((XMLDecoder) -> Void)? = nil - ) { - self.types = types ?? Self.defaultTypes - self.defaultJSONDecoderSetup = defaultJSONDecoderSetup ?? Self.setupJSONDecoder(_:) - self.defaultXMLDecoderSetup = defaultXMLDecoderSetup ?? Self.setupXMLDecoder(_:) - } - - /// Creates an instance of `RSSDecoder` - public convenience init() { - self.init(types: nil, defaultJSONDecoderSetup: nil, defaultXMLDecoderSetup: nil) - } - - private let defaultJSONDecoderSetup: (JSONDecoder) -> Void - private let defaultXMLDecoderSetup: (XMLDecoder) -> Void - private let types: [DecodableFeed.Type] - private static let defaultTypes: [DecodableFeed.Type] = [ RSSFeed.self, AtomFeed.self, JSONFeed.self ] + private let defaultJSONDecoderSetup: (JSONDecoder) -> Void + private let defaultXMLDecoderSetup: (XMLDecoder) -> Void + private let types: [DecodableFeed.Type] + private lazy var defaultXMLDecoder: XMLDecoder = { let decoder = XMLDecoder() self.defaultXMLDecoderSetup(decoder) @@ -92,6 +66,32 @@ public class SynDecoder { .mapValues(Dictionary.init(uniqueKeysWithValues:)) }() + internal init( + types: [DecodableFeed.Type]? = nil, + defaultJSONDecoderSetup: ((JSONDecoder) -> Void)? = nil, + defaultXMLDecoderSetup: ((XMLDecoder) -> Void)? = nil + ) { + self.types = types ?? Self.defaultTypes + self.defaultJSONDecoderSetup = defaultJSONDecoderSetup ?? Self.setupJSONDecoder(_:) + self.defaultXMLDecoderSetup = defaultXMLDecoderSetup ?? Self.setupXMLDecoder(_:) + } + + /// Creates an instance of `RSSDecoder` + public convenience init() { + self.init(types: nil, defaultJSONDecoderSetup: nil, defaultXMLDecoderSetup: nil) + } + + internal static func setupJSONDecoder(_ decoder: JSONDecoder) { + decoder.keyDecodingStrategy = .convertFromSnakeCase + decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) + } + + internal static func setupXMLDecoder(_ decoder: XMLDecoder) { + decoder.keyDecodingStrategy = .convertFromSnakeCase + decoder.dateDecodingStrategy = .custom(DateFormatterDecoder.RSS.decoder.decode(from:)) + decoder.trimValueWhitespaces = false + } + /// Returns a `Feedable` object of the type you specify, decoded from a JSON object. /// - Parameter data: The JSON or XML object to decode. /// - Returns: A `Feedable` object diff --git a/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift b/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift index 93350b4..ea16201 100644 --- a/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift +++ b/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift @@ -1,4 +1,8 @@ public struct CategoryLanguage { + public let type: SiteCategoryType + public let descriptor: CategoryDescriptor + public let language: SiteLanguageType + internal init(languageCategory: SiteLanguageCategory, language: SiteLanguageType) { type = languageCategory.slug descriptor = CategoryDescriptor( @@ -7,8 +11,4 @@ public struct CategoryLanguage { ) self.language = language } - - public let type: SiteCategoryType - public let descriptor: CategoryDescriptor - public let language: SiteLanguageType } diff --git a/Sources/SyndiKit/Formats/Blogs/Site.swift b/Sources/SyndiKit/Formats/Blogs/Site.swift index 8549fd6..92fde38 100644 --- a/Sources/SyndiKit/Formats/Blogs/Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/Site.swift @@ -1,6 +1,14 @@ import Foundation public struct Site { + public let title: String + public let author: String + public let siteURL: URL + public let feedURL: URL + public let twitterURL: URL? + public let category: SiteCategoryType + public let language: SiteLanguageType + internal init( site: SiteLanguageCategory.Site, categoryType: SiteCategoryType, @@ -14,12 +22,4 @@ public struct Site { category = categoryType language = languageType } - - public let title: String - public let author: String - public let siteURL: URL - public let feedURL: URL - public let twitterURL: URL? - public let category: SiteCategoryType - public let language: SiteLanguageType } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift index 4e31054..d62c468 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift @@ -1,4 +1,7 @@ public struct SiteCategory { + public let type: SiteCategoryType + public let descriptors: [SiteLanguageType: CategoryDescriptor] + internal init?(languages: [CategoryLanguage]) { guard let type = languages.first?.type else { return nil @@ -7,7 +10,4 @@ public struct SiteCategory { descriptors = Dictionary(grouping: languages, by: { $0.language }) .compactMapValues { $0.first?.descriptor } } - - public let type: SiteCategoryType - public let descriptors: [SiteLanguageType: CategoryDescriptor] } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 35689c6..23a27d4 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -1,31 +1,5 @@ import Foundation -public protocol SiteDirectory { - associatedtype SiteSequence: Sequence - where SiteSequence.Element == Site - associatedtype LanguageSequence: Sequence - where LanguageSequence.Element == SiteLanguage - associatedtype CategorySequence: Sequence - where CategorySequence.Element == SiteCategory - - func sites( - withLanguage language: SiteLanguageType?, - withCategory category: SiteCategoryType? - ) -> SiteSequence - - var languages: LanguageSequence { get } - var categories: CategorySequence { get } -} - -extension SiteDirectory { - public func sites( - withLanguage language: SiteLanguageType? = nil, - withCategory category: SiteCategoryType? = nil - ) -> SiteSequence { - sites(withLanguage: language, withCategory: category) - } -} - public struct SiteCollectionDirectory: SiteDirectory { public typealias SiteSequence = [Site] @@ -35,39 +9,14 @@ public struct SiteCollectionDirectory: SiteDirectory { public typealias CategorySequence = Dictionary.Values - private let instance: Instance - - public var languages: Dictionary< - SiteLanguageType, SiteLanguage - >.Values { - instance.languageDictionary.values - } - - public var categories: Dictionary< - SiteCategoryType, SiteCategory - >.Values { - instance.categoryDictionary.values - } - - public func sites( - withLanguage language: SiteLanguageType?, - withCategory category: SiteCategoryType? - ) -> [Site] { - instance.sites(withLanguage: language, withCategory: category) - } - - internal init(blogs: SiteCollection) { - instance = .init(blogs: blogs) - } - private struct Instance { - internal let allSites: [Site] - internal let languageDictionary: [SiteLanguageType: SiteLanguage] - internal let categoryDictionary: [SiteCategoryType: SiteCategory] - internal let languageIndicies: [SiteLanguageType: Set] - internal let categoryIndicies: [SiteCategoryType: Set] + fileprivate let allSites: [Site] + fileprivate let languageDictionary: [SiteLanguageType: SiteLanguage] + fileprivate let categoryDictionary: [SiteCategoryType: SiteCategory] + fileprivate let languageIndicies: [SiteLanguageType: Set] + fileprivate let categoryIndicies: [SiteCategoryType: Set] - public func sites( + fileprivate func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? ) -> [Site] { @@ -107,7 +56,7 @@ public struct SiteCollectionDirectory: SiteDirectory { } // swiftlint:disable function_body_length - internal init(blogs: SiteCollection) { + fileprivate init(blogs: SiteCollection) { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() @@ -153,4 +102,55 @@ public struct SiteCollectionDirectory: SiteDirectory { allSites = sites } } + + private let instance: Instance + + public var languages: Dictionary< + SiteLanguageType, SiteLanguage + >.Values { + instance.languageDictionary.values + } + + public var categories: Dictionary< + SiteCategoryType, SiteCategory + >.Values { + instance.categoryDictionary.values + } + + internal init(blogs: SiteCollection) { + instance = .init(blogs: blogs) + } + + public func sites( + withLanguage language: SiteLanguageType?, + withCategory category: SiteCategoryType? + ) -> [Site] { + instance.sites(withLanguage: language, withCategory: category) + } +} + +public protocol SiteDirectory { + associatedtype SiteSequence: Sequence + where SiteSequence.Element == Site + associatedtype LanguageSequence: Sequence + where LanguageSequence.Element == SiteLanguage + associatedtype CategorySequence: Sequence + where CategorySequence.Element == SiteCategory + + var languages: LanguageSequence { get } + var categories: CategorySequence { get } + + func sites( + withLanguage language: SiteLanguageType?, + withCategory category: SiteCategoryType? + ) -> SiteSequence +} + +extension SiteDirectory { + public func sites( + withLanguage language: SiteLanguageType? = nil, + withCategory category: SiteCategoryType? = nil + ) -> SiteSequence { + sites(withLanguage: language, withCategory: category) + } } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift index 51963eb..6273543 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift @@ -1,13 +1,13 @@ import Foundation -public protocol SiteDirectoryBuilder { - associatedtype SiteDirectoryType: SiteDirectory - func directory(fromCollection blogs: SiteCollection) -> SiteDirectoryType -} - public struct SiteCollectionDirectoryBuilder: SiteDirectoryBuilder { public init() {} public func directory(fromCollection blogs: SiteCollection) -> SiteCollectionDirectory { SiteCollectionDirectory(blogs: blogs) } } + +public protocol SiteDirectoryBuilder { + associatedtype SiteDirectoryType: SiteDirectory + func directory(fromCollection blogs: SiteCollection) -> SiteDirectoryType +} diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift similarity index 100% rename from Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.Site.swift rename to Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift b/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift index 6e710e7..11e0377 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift @@ -38,6 +38,24 @@ public enum EntryID: Codable, Equatable, LosslessStringConvertible { /// Plain un-parsable String. case string(String) + public var description: String { + let string: String + switch self { + case let .url(url): + string = url.absoluteString + + case let .uuid(uuid): + string = uuid.uuidString.lowercased() + + case let .path(components, separatedBy: separator): + string = components.joined(separator: separator) + + case let .string(value): + string = value + } + return string + } + /// Implementation of ``LosslessStringConvertible`` initializer. /// This will never return a nil instance. /// Therefore you should use ``init(string:)``to avoid the `Optional` result. @@ -68,24 +86,6 @@ public enum EntryID: Codable, Equatable, LosslessStringConvertible { } } - public var description: String { - let string: String - switch self { - case let .url(url): - string = url.absoluteString - - case let .uuid(uuid): - string = uuid.uuidString.lowercased() - - case let .path(components, separatedBy: separator): - string = components.joined(separator: separator) - - case let .string(value): - string = value - } - return string - } - public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let string = try container.decode(String.self) diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift index 29afbe2..f7a64b6 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift @@ -2,6 +2,31 @@ import Foundation /// information about the channel (metadata) and its contents. public struct RSSChannel: Codable { + internal enum CodingKeys: String, CodingKey { + case title + case link + case description + case lastBuildDate + case pubDate + case ttl + case syUpdatePeriod = "sy:updatePeriod" + case syUpdateFrequency = "sy:updateFrequency" + case items = "item" + case itunesAuthor = "itunes:author" + case itunesImage = "itunes:image" + case itunesOwner = "itunes:owner" + case copyright + case image + case author + case wpCategories = "wp:category" + case wpTags = "wp:tag" + case wpBaseSiteURL = "wp:baseSiteUrl" + case wpBaseBlogURL = "wp:baseBlogUrl" + case podcastLocked = "podcast:locked" + case podcastFundings = "podcast:funding" + case podcastPeople = "podcast:person" + } + /// The name of the channel. public let title: String @@ -48,31 +73,6 @@ public struct RSSChannel: Codable { public let podcastLocked: PodcastLocked? public let podcastFundings: [PodcastFunding] public let podcastPeople: [PodcastPerson] - - internal enum CodingKeys: String, CodingKey { - case title - case link - case description - case lastBuildDate - case pubDate - case ttl - case syUpdatePeriod = "sy:updatePeriod" - case syUpdateFrequency = "sy:updateFrequency" - case items = "item" - case itunesAuthor = "itunes:author" - case itunesImage = "itunes:image" - case itunesOwner = "itunes:owner" - case copyright - case image - case author - case wpCategories = "wp:category" - case wpTags = "wp:tag" - case wpBaseSiteURL = "wp:baseSiteUrl" - case wpBaseBlogURL = "wp:baseBlogUrl" - case podcastLocked = "podcast:locked" - case podcastFundings = "podcast:funding" - case podcastPeople = "podcast:person" - } } extension RSSChannel { diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift index 77f4937..1624e14 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift @@ -1,18 +1,5 @@ import Foundation -public protocol PodcastEpisode { - var title: String? { get } - var episode: Int? { get } - var author: String? { get } - var subtitle: String? { get } - var summary: String? { get } - var explicit: String? { get } - var duration: TimeInterval? { get } - var image: iTunesImage? { get } - var enclosure: Enclosure { get } - var people: [PodcastPerson] { get } -} - internal struct PodcastEpisodeProperties: PodcastEpisode { internal let title: String? internal let episode: Int? @@ -41,3 +28,16 @@ internal struct PodcastEpisodeProperties: PodcastEpisode { people = rssItem.podcastPeople } } + +public protocol PodcastEpisode { + var title: String? { get } + var episode: Int? { get } + var author: String? { get } + var subtitle: String? { get } + var summary: String? { get } + var explicit: String? { get } + var duration: TimeInterval? { get } + var image: iTunesImage? { get } + var enclosure: Enclosure { get } + var people: [PodcastPerson] { get } +} diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift index be8c06d..2f14900 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift @@ -2,6 +2,7 @@ import Foundation extension PodcastLocation { public struct OsmQuery: Codable, Equatable { + // swiftlint:disable:next nesting public enum OsmType: String, Codable, CaseIterable { case node = "N" case way = "W" diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift index 4777633..02f709f 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift @@ -7,10 +7,9 @@ public struct PodcastSoundbite: Codable, Equatable { case title = "" } - + public let startTime: TimeInterval public let duration: TimeInterval public let title: String? - } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 46e3b0b..5c2ab14 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -2,6 +2,20 @@ import Foundation public enum WordPressElements {} +public enum WordPressError: Error, Equatable { + case missingField(WordPressPost.Field) +} + +extension Entryable { + public var wpPost: WordPressPost? { + guard let rssItem = self as? RSSItem else { + return nil + } + + return try? WordPressPost(item: rssItem) + } +} + public struct WordPressPost { public typealias PostType = String public typealias CommentStatus = String @@ -104,10 +118,6 @@ public struct WordPressPost { } } -public enum WordPressError: Error, Equatable { - case missingField(WordPressPost.Field) -} - extension WordPressPost { // swiftlint:disable:next cyclomatic_complexity function_body_length public init(item: RSSItem) throws { @@ -160,11 +170,9 @@ extension WordPressPost { let pubDate = item.pubDate let categoryDictionary = Dictionary( - grouping: categoryTerms, - by: { - $0.domain - } - ) + grouping: categoryTerms) { + $0.domain + } modifiedDateGMT = item.wpModifiedDateGMT self.name = name.value @@ -204,13 +212,3 @@ extension WordPressPost: Hashable { hasher.combine(id) } } - -extension Entryable { - public var wpPost: WordPressPost? { - guard let rssItem = self as? RSSItem else { - return nil - } - - return try? WordPressPost(item: rssItem) - } -} diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift index e4944d8..98dc513 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift @@ -3,16 +3,7 @@ import Foundation public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { public typealias FloatLiteralType = TimeInterval - internal static func timeInterval(_ timeString: String) -> TimeInterval? { - let timeStrings = timeString.components(separatedBy: ":").prefix(3) - let doubles = timeStrings.compactMap(Double.init) - guard doubles.count == timeStrings.count else { - return nil - } - return doubles.reduce(0) { partialResult, value in - partialResult * 60.0 + value - } - } + public let value: TimeInterval public init(floatLiteral value: TimeInterval) { self.value = value @@ -39,5 +30,14 @@ public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { self.value = value } - public let value: TimeInterval + internal static func timeInterval(_ timeString: String) -> TimeInterval? { + let timeStrings = timeString.components(separatedBy: ":").prefix(3) + let doubles = timeStrings.compactMap(Double.init) + guard doubles.count == timeStrings.count else { + return nil + } + return doubles.reduce(0) { partialResult, value in + partialResult * 60.0 + value + } + } } diff --git a/Sources/SyndiKit/Formats/OPML/OPML+Body.swift b/Sources/SyndiKit/Formats/OPML/OPML+Body.swift index 32eab57..830d6d8 100644 --- a/Sources/SyndiKit/Formats/OPML/OPML+Body.swift +++ b/Sources/SyndiKit/Formats/OPML/OPML+Body.swift @@ -2,6 +2,7 @@ import Foundation extension OPML { public struct Body: Codable, Equatable { + // swiftlint:disable:next nesting internal enum CodingKeys: String, CodingKey { case outlines = "outline" } diff --git a/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift b/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift index 5327051..33f5434 100644 --- a/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift +++ b/Sources/SyndiKit/Formats/OPML/OPML+Outline.swift @@ -1,5 +1,7 @@ import Foundation +// swiftlint:disable nesting discouraged_optional_boolean + extension OPML { public struct Outline: Codable, Equatable { public enum CodingKeys: String, CodingKey { diff --git a/Sources/SyndiKit/Substring.SubSequence.swift b/Sources/SyndiKit/Substring.SubSequence.swift index 1ecf7d8..dec93ef 100644 --- a/Sources/SyndiKit/Substring.SubSequence.swift +++ b/Sources/SyndiKit/Substring.SubSequence.swift @@ -6,13 +6,17 @@ extension Substring.SubSequence { } internal func asInt() -> Int? { - guard let double = Double(self) else { return nil } + guard let double = Double(self) else { + return nil + } return Int(double) } internal func asExactInt() -> Int? { - guard let double = Double(self) else { return nil } + guard let double = Double(self) else { + return nil + } return Int(exactly: double) } From 256d645361aa53787cafed7696da900ae453bce4 Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Sun, 21 Jan 2024 17:49:17 +0200 Subject: [PATCH 08/18] fix: errors with tests --- Sources/SyndiKit/Decoding/DecodingError.swift | 4 ++-- Tests/SyndiKitTests/WordpressTests.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/SyndiKit/Decoding/DecodingError.swift b/Sources/SyndiKit/Decoding/DecodingError.swift index 0e96ebf..af481d0 100644 --- a/Sources/SyndiKit/Decoding/DecodingError.swift +++ b/Sources/SyndiKit/Decoding/DecodingError.swift @@ -1,7 +1,7 @@ import Foundation extension DecodingError { - private struct Dictionary: Error { + struct Dictionary: Error { internal init?(errors: [String: DecodingError]) { guard errors.count > 1 else { return nil @@ -9,7 +9,7 @@ extension DecodingError { self.errors = errors } - private let errors: [String: DecodingError] + let errors: [String: DecodingError] } internal static func failedAttempts(_ errors: [String: DecodingError]) -> Self { diff --git a/Tests/SyndiKitTests/WordpressTests.swift b/Tests/SyndiKitTests/WordpressTests.swift index 3682da0..7fe364f 100644 --- a/Tests/SyndiKitTests/WordpressTests.swift +++ b/Tests/SyndiKitTests/WordpressTests.swift @@ -143,7 +143,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -229,7 +229,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -312,7 +312,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) From ea5e82bb9f274b41cabb11998ba59222f1712d71 Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Sun, 21 Jan 2024 22:28:48 +0200 Subject: [PATCH 09/18] fix: possible lint issues --- Sources/SyndiKit/Decoding/DecodingError.swift | 4 +- Sources/SyndiKit/Decoding/SynDecoder.swift | 2 +- .../SyndiKit/Formats/Blogs/SiteCategory.swift | 2 +- .../Formats/Blogs/SiteDirectory.swift | 20 +++---- .../Blogs/SiteLanguageCategory+Site.swift | 2 + .../Podcast/PodcastChapters+MimeType.swift | 10 +++- .../Media/Podcast/PodcastPerson+Role.swift | 58 ++++++++++++++----- .../Podcast/PodcastTranscript+MimeType.swift | 50 +++++++++++----- .../Media/Wordpress/WordPressPost.swift | 27 ++++----- 9 files changed, 114 insertions(+), 61 deletions(-) diff --git a/Sources/SyndiKit/Decoding/DecodingError.swift b/Sources/SyndiKit/Decoding/DecodingError.swift index af481d0..3cc52f0 100644 --- a/Sources/SyndiKit/Decoding/DecodingError.swift +++ b/Sources/SyndiKit/Decoding/DecodingError.swift @@ -1,7 +1,7 @@ import Foundation extension DecodingError { - struct Dictionary: Error { + internal struct Dictionary: Error { internal init?(errors: [String: DecodingError]) { guard errors.count > 1 else { return nil @@ -9,7 +9,7 @@ extension DecodingError { self.errors = errors } - let errors: [String: DecodingError] + internal let errors: [String: DecodingError] } internal static func failedAttempts(_ errors: [String: DecodingError]) -> Self { diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index b9ce0ce..ee7cb3a 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -58,7 +58,7 @@ public class SynDecoder { return (source.source, type.anyDecoding(using: decoder)) } - return Dictionary(grouping: decodings, by: { $0.0 }) + return Dictionary(grouping: decodings) { $0.0 } .mapValues { $0 .map { $0.1 } .map { (type(of: $0).label, $0) } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift index d62c468..71282e5 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift @@ -7,7 +7,7 @@ public struct SiteCategory { return nil } self.type = type - descriptors = Dictionary(grouping: languages, by: { $0.language }) + descriptors = Dictionary(grouping: languages) { $0.language } .compactMapValues { $0.first?.descriptor } } } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 23a27d4..29ca470 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -10,13 +10,13 @@ public struct SiteCollectionDirectory: SiteDirectory { Dictionary.Values private struct Instance { - fileprivate let allSites: [Site] - fileprivate let languageDictionary: [SiteLanguageType: SiteLanguage] - fileprivate let categoryDictionary: [SiteCategoryType: SiteCategory] - fileprivate let languageIndicies: [SiteLanguageType: Set] - fileprivate let categoryIndicies: [SiteCategoryType: Set] + private let allSites: [Site] + private let languageDictionary: [SiteLanguageType: SiteLanguage] + private let categoryDictionary: [SiteCategoryType: SiteCategory] + private let languageIndicies: [SiteLanguageType: Set] + private let categoryIndicies: [SiteCategoryType: Set] - fileprivate func sites( + private func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? ) -> [Site] { @@ -56,7 +56,7 @@ public struct SiteCollectionDirectory: SiteDirectory { } // swiftlint:disable function_body_length - fileprivate init(blogs: SiteCollection) { + private init(blogs: SiteCollection) { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() @@ -90,10 +90,8 @@ public struct SiteCollectionDirectory: SiteDirectory { languages.append(language) } - categoryDictionary = Dictionary( - grouping: categories, - by: { $0.type } - ).compactMapValues(SiteCategory.init) + categoryDictionary = Dictionary(grouping: categories) { $0.type } + .compactMapValues(SiteCategory.init) languageDictionary = Dictionary( uniqueKeysWithValues: languages.map { ($0.type, $0) } ) diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift index 7946977..634511c 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift @@ -1,4 +1,6 @@ import Foundation + +// swiftlint:disable nesting extension SiteLanguageCategory { public struct Site: Codable { public let title: String diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift index 4b57a7e..de3cac7 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift @@ -10,8 +10,11 @@ extension PodcastChapters { init?(mimeType: MimeType) { switch mimeType { - case .json: self = .json - case .unknown: return nil + case .json: + self = .json + + case .unknown: + return nil } } } @@ -47,7 +50,8 @@ extension PodcastChapters { private init(knownMimeType: KnownMimeType) { switch knownMimeType { - case .json: self = .json + case .json: + self = .json } } } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift index d3359d1..d86e2d4 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift @@ -16,14 +16,29 @@ extension PodcastPerson { init?(role: Role) { switch role { - case .guest: self = .guest - case .host: self = .host - case .editor: self = .editor - case .writer: self = .writer - case .designer: self = .designer - case .composer: self = .composer - case .producer: self = .producer - case .unknown: return nil + case .guest: + self = .guest + + case .host: + self = .host + + case .editor: + self = .editor + + case .writer: + self = .writer + + case .designer: + self = .designer + + case .composer: + self = .composer + + case .producer: + self = .producer + + case .unknown: + return nil } } } @@ -65,13 +80,26 @@ extension PodcastPerson { private init(knownRole: KnownRole) { switch knownRole { - case .guest: self = .guest - case .host: self = .host - case .editor: self = .editor - case .writer: self = .writer - case .designer: self = .designer - case .composer: self = .composer - case .producer: self = .producer + case .guest: + self = .guest + + case .host: + self = .host + + case .editor: + self = .editor + + case .writer: + self = .writer + + case .designer: + self = .designer + + case .composer: + self = .composer + + case .producer: + self = .producer } } } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift index 654c9d6..95f118b 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift @@ -15,13 +15,26 @@ extension PodcastTranscript { init?(mimeType: MimeType) { switch mimeType { - case .plain: self = .plain - case .html: self = .html - case .srt: self = .srt - case .vtt: self = .vtt - case .json: self = .json - case .subrip: self = .subrip - case .unknown: return nil + case .plain: + self = .plain + + case .html: + self = .html + + case .srt: + self = .srt + + case .vtt: + self = .vtt + + case .json: + self = .json + + case .subrip: + self = .subrip + + case .unknown: + return nil } } } @@ -62,12 +75,23 @@ extension PodcastTranscript { private init(knownMimeType: KnownMimeType) { switch knownMimeType { - case .plain: self = .plain - case .html: self = .html - case .srt: self = .srt - case .vtt: self = .vtt - case .json: self = .json - case .subrip: self = .subrip + case .plain: + self = .plain + + case .html: + self = .html + + case .srt: + self = .srt + + case .vtt: + self = .vtt + + case .json: + self = .json + + case .subrip: + self = .subrip } } } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 5c2ab14..855a8cd 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -6,16 +6,6 @@ public enum WordPressError: Error, Equatable { case missingField(WordPressPost.Field) } -extension Entryable { - public var wpPost: WordPressPost? { - guard let rssItem = self as? RSSItem else { - return nil - } - - return try? WordPressPost(item: rssItem) - } -} - public struct WordPressPost { public typealias PostType = String public typealias CommentStatus = String @@ -184,11 +174,8 @@ extension WordPressPost { self.body = body.value tags = categoryDictionary["post_tag", default: []].map { $0.value } categories = categoryDictionary["category", default: []].map { $0.value } - self.meta = Dictionary(grouping: meta, by: { - $0.key.value - }).compactMapValues { - $0.last?.value.value - } + self.meta = Dictionary(grouping: meta) { $0.key.value } + .compactMapValues { $0.last?.value.value } self.status = status.value self.commentStatus = commentStatus.value self.pingStatus = pingStatus.value @@ -212,3 +199,13 @@ extension WordPressPost: Hashable { hasher.combine(id) } } + +extension Entryable { + public var wpPost: WordPressPost? { + guard let rssItem = self as? RSSItem else { + return nil + } + + return try? WordPressPost(item: rssItem) + } +} From a060f084c79576c58fd6f7cb1cbf4505563a1c9b Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Sun, 21 Jan 2024 22:35:54 +0200 Subject: [PATCH 10/18] fix: SiteCollectionDirectory.Instance to be internal than private --- .../SyndiKit/Formats/Blogs/SiteDirectory.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 29ca470..960183d 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -9,14 +9,14 @@ public struct SiteCollectionDirectory: SiteDirectory { public typealias CategorySequence = Dictionary.Values - private struct Instance { - private let allSites: [Site] - private let languageDictionary: [SiteLanguageType: SiteLanguage] - private let categoryDictionary: [SiteCategoryType: SiteCategory] - private let languageIndicies: [SiteLanguageType: Set] - private let categoryIndicies: [SiteCategoryType: Set] - - private func sites( + internal struct Instance { + internal let allSites: [Site] + internal let languageDictionary: [SiteLanguageType: SiteLanguage] + internal let categoryDictionary: [SiteCategoryType: SiteCategory] + internal let languageIndicies: [SiteLanguageType: Set] + internal let categoryIndicies: [SiteCategoryType: Set] + + internal func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? ) -> [Site] { @@ -56,7 +56,7 @@ public struct SiteCollectionDirectory: SiteDirectory { } // swiftlint:disable function_body_length - private init(blogs: SiteCollection) { + internal init(blogs: SiteCollection) { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() From bae639092a38493422e6fe89be34ff3f39461b18 Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Mon, 22 Jan 2024 00:20:35 +0200 Subject: [PATCH 11/18] mint-upgrade: periphery from 2.12.3 to 2.18.0 --- Mintfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mintfile b/Mintfile index 4769ce7..93228eb 100644 --- a/Mintfile +++ b/Mintfile @@ -1,3 +1,3 @@ nicklockwood/SwiftFormat@0.47.0 realm/SwiftLint@0.41.0 -peripheryapp/periphery@2.12.3 +peripheryapp/periphery@2.18.0 From 5eb28b879f55207fa58be441fc3fa6eeb80cf49b Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Mon, 22 Jan 2024 22:10:40 +0200 Subject: [PATCH 12/18] resolve PR conversations --- Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift index d86e2d4..f292a2e 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift @@ -14,6 +14,7 @@ extension PodcastPerson { self.init(rawValue: caseInsensitive.lowercased()) } + // swiftlint:disable:next function_body_length cyclomatic_complexity init?(role: Role) { switch role { case .guest: @@ -78,6 +79,7 @@ extension PodcastPerson { } } + // swiftlint:disable:next cyclomatic_complexity private init(knownRole: KnownRole) { switch knownRole { case .guest: From 0d18e194fbbdf0aeb182280b086319e2203b72db Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Mon, 22 Jan 2024 22:22:01 +0200 Subject: [PATCH 13/18] redo: post.id back to post.ID --- Tests/SyndiKitTests/WordpressTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/SyndiKitTests/WordpressTests.swift b/Tests/SyndiKitTests/WordpressTests.swift index 7fe364f..3682da0 100644 --- a/Tests/SyndiKitTests/WordpressTests.swift +++ b/Tests/SyndiKitTests/WordpressTests.swift @@ -143,7 +143,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.id, wpPostID) + XCTAssertEqual(post.ID, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -229,7 +229,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.id, wpPostID) + XCTAssertEqual(post.ID, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -312,7 +312,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.id, wpPostID) + XCTAssertEqual(post.ID, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) From 324fb80e96788c4178f5f8b06a6fe41f583f7524 Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Mon, 22 Jan 2024 22:58:45 +0200 Subject: [PATCH 14/18] fix: post.ID and make the property called ID --- .../Formats/Media/Wordpress/WordPressPost.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 855a8cd..61317a2 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -51,7 +51,7 @@ public struct WordPressPost { public let pingStatus: PingStatus public let parentID: Int? public let menuOrder: Int? - public let id: Int + public let ID: Int public let isSticky: Bool public let postDate: Date public let postDateGMT: Date? @@ -98,7 +98,7 @@ public struct WordPressPost { self.pingStatus = pingStatus self.parentID = parentID self.menuOrder = menuOrder - self.id = id + self.ID = id self.isSticky = isSticky self.postDate = postDate self.postDateGMT = postDateGMT @@ -181,7 +181,7 @@ extension WordPressPost { self.pingStatus = pingStatus.value self.parentID = parentID self.menuOrder = menuOrder - self.id = id + self.ID = id self.isSticky = (isSticky != 0) self.postDate = postDate postDateGMT = item.wpPostDateGMT @@ -192,11 +192,11 @@ extension WordPressPost { extension WordPressPost: Hashable { public static func == (lhs: WordPressPost, rhs: WordPressPost) -> Bool { - lhs.id == rhs.id + lhs.ID == rhs.ID } public func hash(into hasher: inout Hasher) { - hasher.combine(id) + hasher.combine(ID) } } From 50dd741263a5dd0cb2c4f3d4d4c7020acb75c886 Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Tue, 23 Jan 2024 00:07:41 +0200 Subject: [PATCH 15/18] Fix strict lint voilations (#67) --- Mintfile | 2 +- Sources/SyndiKit/Decoding/DecodingError.swift | 4 +- Sources/SyndiKit/Decoding/SynDecoder.swift | 2 +- .../SyndiKit/Formats/Blogs/SiteCategory.swift | 2 +- .../Formats/Blogs/SiteDirectory.swift | 24 ++++---- .../Blogs/SiteLanguageCategory+Site.swift | 2 + .../Podcast/PodcastChapters+MimeType.swift | 10 +++- .../Media/Podcast/PodcastPerson+Role.swift | 60 ++++++++++++++----- .../Podcast/PodcastTranscript+MimeType.swift | 50 ++++++++++++---- .../Media/Wordpress/WordPressPost.swift | 37 ++++++------ 10 files changed, 124 insertions(+), 69 deletions(-) diff --git a/Mintfile b/Mintfile index 4769ce7..93228eb 100644 --- a/Mintfile +++ b/Mintfile @@ -1,3 +1,3 @@ nicklockwood/SwiftFormat@0.47.0 realm/SwiftLint@0.41.0 -peripheryapp/periphery@2.12.3 +peripheryapp/periphery@2.18.0 diff --git a/Sources/SyndiKit/Decoding/DecodingError.swift b/Sources/SyndiKit/Decoding/DecodingError.swift index 0e96ebf..3cc52f0 100644 --- a/Sources/SyndiKit/Decoding/DecodingError.swift +++ b/Sources/SyndiKit/Decoding/DecodingError.swift @@ -1,7 +1,7 @@ import Foundation extension DecodingError { - private struct Dictionary: Error { + internal struct Dictionary: Error { internal init?(errors: [String: DecodingError]) { guard errors.count > 1 else { return nil @@ -9,7 +9,7 @@ extension DecodingError { self.errors = errors } - private let errors: [String: DecodingError] + internal let errors: [String: DecodingError] } internal static func failedAttempts(_ errors: [String: DecodingError]) -> Self { diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index b9ce0ce..ee7cb3a 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -58,7 +58,7 @@ public class SynDecoder { return (source.source, type.anyDecoding(using: decoder)) } - return Dictionary(grouping: decodings, by: { $0.0 }) + return Dictionary(grouping: decodings) { $0.0 } .mapValues { $0 .map { $0.1 } .map { (type(of: $0).label, $0) } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift index d62c468..71282e5 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift @@ -7,7 +7,7 @@ public struct SiteCategory { return nil } self.type = type - descriptors = Dictionary(grouping: languages, by: { $0.language }) + descriptors = Dictionary(grouping: languages) { $0.language } .compactMapValues { $0.first?.descriptor } } } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 23a27d4..960183d 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -9,14 +9,14 @@ public struct SiteCollectionDirectory: SiteDirectory { public typealias CategorySequence = Dictionary.Values - private struct Instance { - fileprivate let allSites: [Site] - fileprivate let languageDictionary: [SiteLanguageType: SiteLanguage] - fileprivate let categoryDictionary: [SiteCategoryType: SiteCategory] - fileprivate let languageIndicies: [SiteLanguageType: Set] - fileprivate let categoryIndicies: [SiteCategoryType: Set] - - fileprivate func sites( + internal struct Instance { + internal let allSites: [Site] + internal let languageDictionary: [SiteLanguageType: SiteLanguage] + internal let categoryDictionary: [SiteCategoryType: SiteCategory] + internal let languageIndicies: [SiteLanguageType: Set] + internal let categoryIndicies: [SiteCategoryType: Set] + + internal func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? ) -> [Site] { @@ -56,7 +56,7 @@ public struct SiteCollectionDirectory: SiteDirectory { } // swiftlint:disable function_body_length - fileprivate init(blogs: SiteCollection) { + internal init(blogs: SiteCollection) { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() @@ -90,10 +90,8 @@ public struct SiteCollectionDirectory: SiteDirectory { languages.append(language) } - categoryDictionary = Dictionary( - grouping: categories, - by: { $0.type } - ).compactMapValues(SiteCategory.init) + categoryDictionary = Dictionary(grouping: categories) { $0.type } + .compactMapValues(SiteCategory.init) languageDictionary = Dictionary( uniqueKeysWithValues: languages.map { ($0.type, $0) } ) diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift index 7946977..634511c 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift @@ -1,4 +1,6 @@ import Foundation + +// swiftlint:disable nesting extension SiteLanguageCategory { public struct Site: Codable { public let title: String diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift index 4b57a7e..de3cac7 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift @@ -10,8 +10,11 @@ extension PodcastChapters { init?(mimeType: MimeType) { switch mimeType { - case .json: self = .json - case .unknown: return nil + case .json: + self = .json + + case .unknown: + return nil } } } @@ -47,7 +50,8 @@ extension PodcastChapters { private init(knownMimeType: KnownMimeType) { switch knownMimeType { - case .json: self = .json + case .json: + self = .json } } } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift index d3359d1..f292a2e 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift @@ -14,16 +14,32 @@ extension PodcastPerson { self.init(rawValue: caseInsensitive.lowercased()) } + // swiftlint:disable:next function_body_length cyclomatic_complexity init?(role: Role) { switch role { - case .guest: self = .guest - case .host: self = .host - case .editor: self = .editor - case .writer: self = .writer - case .designer: self = .designer - case .composer: self = .composer - case .producer: self = .producer - case .unknown: return nil + case .guest: + self = .guest + + case .host: + self = .host + + case .editor: + self = .editor + + case .writer: + self = .writer + + case .designer: + self = .designer + + case .composer: + self = .composer + + case .producer: + self = .producer + + case .unknown: + return nil } } } @@ -63,15 +79,29 @@ extension PodcastPerson { } } + // swiftlint:disable:next cyclomatic_complexity private init(knownRole: KnownRole) { switch knownRole { - case .guest: self = .guest - case .host: self = .host - case .editor: self = .editor - case .writer: self = .writer - case .designer: self = .designer - case .composer: self = .composer - case .producer: self = .producer + case .guest: + self = .guest + + case .host: + self = .host + + case .editor: + self = .editor + + case .writer: + self = .writer + + case .designer: + self = .designer + + case .composer: + self = .composer + + case .producer: + self = .producer } } } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift index 654c9d6..95f118b 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift @@ -15,13 +15,26 @@ extension PodcastTranscript { init?(mimeType: MimeType) { switch mimeType { - case .plain: self = .plain - case .html: self = .html - case .srt: self = .srt - case .vtt: self = .vtt - case .json: self = .json - case .subrip: self = .subrip - case .unknown: return nil + case .plain: + self = .plain + + case .html: + self = .html + + case .srt: + self = .srt + + case .vtt: + self = .vtt + + case .json: + self = .json + + case .subrip: + self = .subrip + + case .unknown: + return nil } } } @@ -62,12 +75,23 @@ extension PodcastTranscript { private init(knownMimeType: KnownMimeType) { switch knownMimeType { - case .plain: self = .plain - case .html: self = .html - case .srt: self = .srt - case .vtt: self = .vtt - case .json: self = .json - case .subrip: self = .subrip + case .plain: + self = .plain + + case .html: + self = .html + + case .srt: + self = .srt + + case .vtt: + self = .vtt + + case .json: + self = .json + + case .subrip: + self = .subrip } } } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 5c2ab14..61317a2 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -6,16 +6,6 @@ public enum WordPressError: Error, Equatable { case missingField(WordPressPost.Field) } -extension Entryable { - public var wpPost: WordPressPost? { - guard let rssItem = self as? RSSItem else { - return nil - } - - return try? WordPressPost(item: rssItem) - } -} - public struct WordPressPost { public typealias PostType = String public typealias CommentStatus = String @@ -61,7 +51,7 @@ public struct WordPressPost { public let pingStatus: PingStatus public let parentID: Int? public let menuOrder: Int? - public let id: Int + public let ID: Int public let isSticky: Bool public let postDate: Date public let postDateGMT: Date? @@ -108,7 +98,7 @@ public struct WordPressPost { self.pingStatus = pingStatus self.parentID = parentID self.menuOrder = menuOrder - self.id = id + self.ID = id self.isSticky = isSticky self.postDate = postDate self.postDateGMT = postDateGMT @@ -184,17 +174,14 @@ extension WordPressPost { self.body = body.value tags = categoryDictionary["post_tag", default: []].map { $0.value } categories = categoryDictionary["category", default: []].map { $0.value } - self.meta = Dictionary(grouping: meta, by: { - $0.key.value - }).compactMapValues { - $0.last?.value.value - } + self.meta = Dictionary(grouping: meta) { $0.key.value } + .compactMapValues { $0.last?.value.value } self.status = status.value self.commentStatus = commentStatus.value self.pingStatus = pingStatus.value self.parentID = parentID self.menuOrder = menuOrder - self.id = id + self.ID = id self.isSticky = (isSticky != 0) self.postDate = postDate postDateGMT = item.wpPostDateGMT @@ -205,10 +192,20 @@ extension WordPressPost { extension WordPressPost: Hashable { public static func == (lhs: WordPressPost, rhs: WordPressPost) -> Bool { - lhs.id == rhs.id + lhs.ID == rhs.ID } public func hash(into hasher: inout Hasher) { - hasher.combine(id) + hasher.combine(ID) + } +} + +extension Entryable { + public var wpPost: WordPressPost? { + guard let rssItem = self as? RSSItem else { + return nil + } + + return try? WordPressPost(item: rssItem) } } From 81c0418593071695dd72ba225123a0466ab58082 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 25 Jan 2024 09:40:42 -0500 Subject: [PATCH 16/18] fixup! fix: post.ID and make the property called ID --- Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 61317a2..40aab6f 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -98,7 +98,7 @@ public struct WordPressPost { self.pingStatus = pingStatus self.parentID = parentID self.menuOrder = menuOrder - self.ID = id + ID = id self.isSticky = isSticky self.postDate = postDate self.postDateGMT = postDateGMT @@ -181,7 +181,7 @@ extension WordPressPost { self.pingStatus = pingStatus.value self.parentID = parentID self.menuOrder = menuOrder - self.ID = id + ID = id self.isSticky = (isSticky != 0) self.postDate = postDate postDateGMT = item.wpPostDateGMT From 85b8a2f7ccaccc6d2158188d0d6e6aeed6d65b4f Mon Sep 17 00:00:00 2001 From: Ahmed Shendy Date: Thu, 25 Jan 2024 18:23:41 +0200 Subject: [PATCH 17/18] Any other lint violations that are not 'Missing Docs Violation' (#68) * lint-fixes: any voilation that is not 'Missing Docs Violation' * fixup! Merge branch 'v0.4.0-STRICT-changes' into v0.4.0-STRICT-others --------- Co-authored-by: leogdion --- Sources/SyndiKit/Common/Author.swift | 2 +- .../Decoding/DateFormatterDecoder.swift | 4 - Sources/SyndiKit/Decoding/DecodingError.swift | 12 ++ Sources/SyndiKit/Decoding/SynDecoder.swift | 1 + .../Formats/Blogs/SiteDirectory.swift | 1 + .../Formats/Feeds/RSS/Enclosure.swift | 14 +- .../Formats/Feeds/RSS/RSSItem+decodings.swift | 117 ++++++++++ .../Formats/Feeds/RSS/RSSItem+inits.swift | 91 ++++++++ .../SyndiKit/Formats/Feeds/RSS/RSSItem.swift | 200 ------------------ .../Media/Podcast/PodcastEpisode.swift | 24 +-- .../Podcast/PodcastLocation+GeoURI.swift | 19 +- .../Podcast/PodcastLocation+OsmQuery.swift | 16 +- .../Podcast/PodcastTranscript+MimeType.swift | 1 + .../Wordpress/WordPressPost+RSSItem.swift | 83 ++++++++ .../Media/Wordpress/WordPressPost.swift | 130 ------------ 15 files changed, 340 insertions(+), 375 deletions(-) create mode 100644 Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift create mode 100644 Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift create mode 100644 Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift diff --git a/Sources/SyndiKit/Common/Author.swift b/Sources/SyndiKit/Common/Author.swift index 75bce78..8c2933e 100644 --- a/Sources/SyndiKit/Common/Author.swift +++ b/Sources/SyndiKit/Common/Author.swift @@ -11,7 +11,7 @@ public struct Author: Codable, Equatable { /// Contains a home page for the person. public let uri: URL? - internal init(name: String) { + public init(name: String) { self.name = name email = nil uri = nil diff --git a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift index 2cac0cc..56ffc21 100644 --- a/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift +++ b/Sources/SyndiKit/Decoding/DateFormatterDecoder.swift @@ -16,10 +16,6 @@ internal struct DateFormatterDecoder { private let formatters: [DateFormatter] - internal init(formatters: [DateFormatter]) { - self.formatters = formatters - } - internal init(basedOnFormats formats: [String]) { formatters = formats.map(Self.isoPOSIX(withFormat:)) } diff --git a/Sources/SyndiKit/Decoding/DecodingError.swift b/Sources/SyndiKit/Decoding/DecodingError.swift index 3cc52f0..081a0fa 100644 --- a/Sources/SyndiKit/Decoding/DecodingError.swift +++ b/Sources/SyndiKit/Decoding/DecodingError.swift @@ -20,4 +20,16 @@ extension DecodingError { ) return DecodingError.dataCorrupted(context) } + + internal static func dataCorrupted( + codingKey: CodingKey, + debugDescription: String + ) -> Self { + DecodingError.dataCorrupted( + .init( + codingPath: [codingKey], + debugDescription: debugDescription + ) + ) + } } diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index ee7cb3a..9bc0cf6 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -34,6 +34,7 @@ public class SynDecoder { return decoder }() + // swiftlint:disable:next closure_body_length private lazy var decodings: [DecoderSource: [String: AnyDecoding]] = { let decodings = types.map { type -> (DecoderSource, AnyDecoding) in let source = type.source diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index 960183d..b009695 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -16,6 +16,7 @@ public struct SiteCollectionDirectory: SiteDirectory { internal let languageIndicies: [SiteLanguageType: Set] internal let categoryIndicies: [SiteCategoryType: Set] + // swiftlint:disable:next function_body_length internal func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift index 41d2ea3..23f711d 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift @@ -15,21 +15,27 @@ public struct Enclosure: Codable { let container = try decoder.container(keyedBy: Self.CodingKeys.self) url = try container.decode(UTF8EncodedURL.self, forKey: .url).value type = try container.decode(String.self, forKey: .type) + length = try Self.decodeLength(from: container) + } + + private static func decodeLength( + from container: KeyedDecodingContainer + ) throws -> Int? { if container.contains(.length) { do { - length = try container.decode(Int.self, forKey: .length) + return try container.decode(Int.self, forKey: .length) } catch { let lengthString = try container.decode(String.self, forKey: .length) if lengthString.isEmpty { - length = nil + return nil } else if let length = Int(lengthString) { - self.length = length + return length } else { throw error } } } else { - length = nil + return nil } } } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift new file mode 100644 index 0000000..b271175 --- /dev/null +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift @@ -0,0 +1,117 @@ +import Foundation +import XMLCoder + +extension RSSItem { + // swiftlint:disable:next function_body_length + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + title = try container.decode(String.self, forKey: .title) + link = try container.decodeIfPresent(URL.self, forKey: .link) + description = try container.decodeIfPresent(CData.self, forKey: .description) + guid = try container.decode(EntryID.self, forKey: .guid) + pubDate = try container.decodeDateIfPresentAndValid(forKey: .pubDate) + contentEncoded = try container.decodeIfPresent(CData.self, forKey: .contentEncoded) + categoryTerms = try container.decode([RSSItemCategory].self, forKey: .categoryTerms) + content = try container.decodeIfPresent(String.self, forKey: .content) + itunesTitle = try container.decodeIfPresent(String.self, forKey: .itunesTitle) + itunesEpisode = try container.decodeIfPresent( + iTunesEpisode.self, forKey: .itunesEpisode + ) + itunesAuthor = try container.decodeIfPresent(String.self, forKey: .itunesAuthor) + itunesSubtitle = try container.decodeIfPresent(String.self, forKey: .itunesSubtitle) + itunesSummary = try container.decodeIfPresent(CData.self, forKey: .itunesSummary) + itunesExplicit = try container.decodeIfPresent(String.self, forKey: .itunesExplicit) + itunesDuration = try container.decodeIfPresent( + iTunesDuration.self, forKey: .itunesDuration + ) + itunesImage = try container.decodeIfPresent(iTunesImage.self, forKey: .itunesImage) + + podcastPeople = try container.decodeIfPresent( + [PodcastPerson].self, + forKey: .podcastPeople + ) ?? [] + podcastTranscripts = try container.decodeIfPresent( + [PodcastTranscript].self, + forKey: .podcastTranscripts + ) ?? [] + podcastChapters = try container.decodeIfPresent( + PodcastChapters.self, + forKey: .podcastChapters + ) + podcastSoundbites = try container.decodeIfPresent( + [PodcastSoundbite].self, + forKey: .podcastSoundbites + ) ?? [] + + podcastSeason = try container.decodeIfPresent( + PodcastSeason.self, + forKey: .podcastSeason + ) + + enclosure = try container.decodeIfPresent(Enclosure.self, forKey: .enclosure) + creators = try container.decode([String].self, forKey: .creators) + + mediaContent = + try container.decodeIfPresent(AtomMedia.self, forKey: .mediaContent) + mediaThumbnail = + try container.decodeIfPresent(AtomMedia.self, forKey: .mediaThumbnail) + + wpPostID = try container.decodeIfPresent(Int.self, forKey: .wpPostID) + wpPostDate = try container.decodeIfPresent(Date.self, forKey: .wpPostDate) + let wpPostDateGMT = try container.decodeIfPresent( + String.self, forKey: .wpPostDateGMT + ) + if let wpPostDateGMT = wpPostDateGMT { + if wpPostDateGMT == "0000-00-00 00:00:00" { + self.wpPostDateGMT = nil + } else { + self.wpPostDateGMT = try container.decode( + Date.self, forKey: .wpPostDateGMT + ) + } + } else { + self.wpPostDateGMT = nil + } + + wpModifiedDate = try container.decodeIfPresent( + Date.self, forKey: .wpModifiedDate + ) + + let wpModifiedDateGMT = try container.decodeIfPresent( + String.self, forKey: .wpModifiedDateGMT + ) + if let wpModifiedDateGMT = wpModifiedDateGMT { + if wpModifiedDateGMT == "0000-00-00 00:00:00" { + self.wpModifiedDateGMT = nil + } else { + self.wpModifiedDateGMT = try container.decode( + Date.self, forKey: .wpModifiedDateGMT + ) + } + } else { + self.wpModifiedDateGMT = nil + } + + let wpAttachmentURLCDData = try container.decodeIfPresent( + CData.self, + forKey: .wpAttachmentURL + ) + wpAttachmentURL = wpAttachmentURLCDData.map { $0.value }.flatMap(URL.init(string:)) + + wpPostName = try container.decodeIfPresent(CData.self, forKey: .wpPostName) + wpPostType = try container.decodeIfPresent(CData.self, forKey: .wpPostType) + wpPostMeta = try container.decodeIfPresent( + [WordPressElements.PostMeta].self, + forKey: .wpPostMeta + ) ?? [] + wpCommentStatus = try container.decodeIfPresent(CData.self, forKey: .wpCommentStatus) + wpPingStatus = try container.decodeIfPresent(CData.self, forKey: .wpPingStatus) + wpStatus = try container.decodeIfPresent(CData.self, forKey: .wpStatus) + wpPostParent = try container.decodeIfPresent(Int.self, forKey: .wpPostParent) + wpMenuOrder = try container.decodeIfPresent(Int.self, forKey: .wpMenuOrder) + wpIsSticky = try container.decodeIfPresent(Int.self, forKey: .wpIsSticky) + wpPostPassword = try container.decodeIfPresent( + CData.self, forKey: .wpPostPassword + ) + } +} diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift new file mode 100644 index 0000000..4431eea --- /dev/null +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift @@ -0,0 +1,91 @@ +import Foundation +import XMLCoder + +extension RSSItem { + // swiftlint:disable:next function_body_length + public init( + title: String, + link: URL, + description: String?, + guid: EntryID, + pubDate: Date? = nil, + contentEncoded: String? = nil, + categoryTerms: [RSSItemCategory] = [], + content: String? = nil, + itunesTitle: String? = nil, + itunesEpisode: Int? = nil, + itunesAuthor: String? = nil, + itunesSubtitle: String? = nil, + itunesSummary: CData? = nil, + itunesExplicit: String? = nil, + itunesDuration: TimeInterval? = nil, + itunesImage: iTunesImage? = nil, + podcastPeople: [PodcastPerson] = [], + podcastTranscripts: [PodcastTranscript] = [], + podcastChapters: PodcastChapters? = nil, + podcastSoundbites: [PodcastSoundbite] = [], + podcastSeason: PodcastSeason? = nil, + enclosure: Enclosure? = nil, + creators: [String] = [], + wpCommentStatus: String? = nil, + wpPingStatus: String? = nil, + wpStatus: String? = nil, + wpPostParent: Int? = nil, + wpMenuOrder: Int? = nil, + wpIsSticky: Int? = nil, + wpPostPassword: String? = nil, + wpPostID: Int? = nil, + wpPostDate: Date? = nil, + wpPostDateGMT: Date? = nil, + wpModifiedDate: Date? = nil, + wpModifiedDateGMT: Date? = nil, + wpPostName: String? = nil, + wpPostType: String? = nil, + wpPostMeta: [WordPressElements.PostMeta] = [], + wpAttachmentURL: URL? = nil, + mediaContent: AtomMedia? = nil, + mediaThumbnail: AtomMedia? = nil + ) { + self.title = title + self.link = link + self.description = description.map(CData.init) + self.guid = guid + self.pubDate = pubDate + self.contentEncoded = contentEncoded.map(CData.init) + self.categoryTerms = categoryTerms + self.content = content + self.itunesTitle = itunesTitle + self.itunesEpisode = itunesEpisode.map(iTunesEpisode.init) + self.itunesAuthor = itunesAuthor + self.itunesSubtitle = itunesSubtitle + self.itunesSummary = itunesSummary + self.itunesExplicit = itunesExplicit + self.itunesDuration = itunesDuration.map(iTunesDuration.init) + self.itunesImage = itunesImage + self.podcastPeople = podcastPeople + self.podcastTranscripts = podcastTranscripts + self.podcastChapters = podcastChapters + self.podcastSoundbites = podcastSoundbites + self.podcastSeason = podcastSeason + self.enclosure = enclosure + self.creators = creators + self.wpCommentStatus = wpCommentStatus.map(CData.init) + self.wpPingStatus = wpPingStatus.map(CData.init) + self.wpStatus = wpStatus.map(CData.init) + self.wpPostParent = wpPostParent + self.wpMenuOrder = wpMenuOrder + self.wpIsSticky = wpIsSticky + self.wpPostPassword = wpPostPassword.map(CData.init) + self.wpPostID = wpPostID + self.wpPostDate = wpPostDate + self.wpPostDateGMT = wpPostDateGMT + self.wpModifiedDate = wpModifiedDate + self.wpModifiedDateGMT = wpModifiedDateGMT + self.wpPostName = wpPostName.map(CData.init) + self.wpPostType = wpPostType.map(CData.init) + self.wpPostMeta = wpPostMeta + self.wpAttachmentURL = wpAttachmentURL + self.mediaContent = mediaContent + self.mediaThumbnail = mediaThumbnail + } +} diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift index 744515a..ab32f67 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem.swift @@ -90,206 +90,6 @@ public struct RSSItem: Codable { public let wpAttachmentURL: URL? public let mediaContent: AtomMedia? public let mediaThumbnail: AtomMedia? - - // swiftlint:disable:next function_body_length - public init( - title: String, - link: URL, - description: String?, - guid: EntryID, - pubDate: Date? = nil, - contentEncoded: String? = nil, - categoryTerms: [RSSItemCategory] = [], - content: String? = nil, - itunesTitle: String? = nil, - itunesEpisode: Int? = nil, - itunesAuthor: String? = nil, - itunesSubtitle: String? = nil, - itunesSummary: CData? = nil, - itunesExplicit: String? = nil, - itunesDuration: TimeInterval? = nil, - itunesImage: iTunesImage? = nil, - podcastPeople: [PodcastPerson] = [], - podcastTranscripts: [PodcastTranscript] = [], - podcastChapters: PodcastChapters? = nil, - podcastSoundbites: [PodcastSoundbite] = [], - podcastSeason: PodcastSeason? = nil, - enclosure: Enclosure? = nil, - creators: [String] = [], - wpCommentStatus: String? = nil, - wpPingStatus: String? = nil, - wpStatus: String? = nil, - wpPostParent: Int? = nil, - wpMenuOrder: Int? = nil, - wpIsSticky: Int? = nil, - wpPostPassword: String? = nil, - wpPostID: Int? = nil, - wpPostDate: Date? = nil, - wpPostDateGMT: Date? = nil, - wpModifiedDate: Date? = nil, - wpModifiedDateGMT: Date? = nil, - wpPostName: String? = nil, - wpPostType: String? = nil, - wpPostMeta: [WordPressElements.PostMeta] = [], - wpAttachmentURL: URL? = nil, - mediaContent: AtomMedia? = nil, - mediaThumbnail: AtomMedia? = nil - ) { - self.title = title - self.link = link - self.description = description.map(CData.init) - self.guid = guid - self.pubDate = pubDate - self.contentEncoded = contentEncoded.map(CData.init) - self.categoryTerms = categoryTerms - self.content = content - self.itunesTitle = itunesTitle - self.itunesEpisode = itunesEpisode.map(iTunesEpisode.init) - self.itunesAuthor = itunesAuthor - self.itunesSubtitle = itunesSubtitle - self.itunesSummary = itunesSummary - self.itunesExplicit = itunesExplicit - self.itunesDuration = itunesDuration.map(iTunesDuration.init) - self.itunesImage = itunesImage - self.podcastPeople = podcastPeople - self.podcastTranscripts = podcastTranscripts - self.podcastChapters = podcastChapters - self.podcastSoundbites = podcastSoundbites - self.podcastSeason = podcastSeason - self.enclosure = enclosure - self.creators = creators - self.wpCommentStatus = wpCommentStatus.map(CData.init) - self.wpPingStatus = wpPingStatus.map(CData.init) - self.wpStatus = wpStatus.map(CData.init) - self.wpPostParent = wpPostParent - self.wpMenuOrder = wpMenuOrder - self.wpIsSticky = wpIsSticky - self.wpPostPassword = wpPostPassword.map(CData.init) - self.wpPostID = wpPostID - self.wpPostDate = wpPostDate - self.wpPostDateGMT = wpPostDateGMT - self.wpModifiedDate = wpModifiedDate - self.wpModifiedDateGMT = wpModifiedDateGMT - self.wpPostName = wpPostName.map(CData.init) - self.wpPostType = wpPostType.map(CData.init) - self.wpPostMeta = wpPostMeta - self.wpAttachmentURL = wpAttachmentURL - self.mediaContent = mediaContent - self.mediaThumbnail = mediaThumbnail - } - - // swiftlint:disable:next function_body_length - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - title = try container.decode(String.self, forKey: .title) - link = try container.decodeIfPresent(URL.self, forKey: .link) - description = try container.decodeIfPresent(CData.self, forKey: .description) - guid = try container.decode(EntryID.self, forKey: .guid) - pubDate = try container.decodeDateIfPresentAndValid(forKey: .pubDate) - contentEncoded = try container.decodeIfPresent(CData.self, forKey: .contentEncoded) - categoryTerms = try container.decode([RSSItemCategory].self, forKey: .categoryTerms) - content = try container.decodeIfPresent(String.self, forKey: .content) - itunesTitle = try container.decodeIfPresent(String.self, forKey: .itunesTitle) - itunesEpisode = try container.decodeIfPresent( - iTunesEpisode.self, forKey: .itunesEpisode - ) - itunesAuthor = try container.decodeIfPresent(String.self, forKey: .itunesAuthor) - itunesSubtitle = try container.decodeIfPresent(String.self, forKey: .itunesSubtitle) - itunesSummary = try container.decodeIfPresent(CData.self, forKey: .itunesSummary) - itunesExplicit = try container.decodeIfPresent(String.self, forKey: .itunesExplicit) - itunesDuration = try container.decodeIfPresent( - iTunesDuration.self, forKey: .itunesDuration - ) - itunesImage = try container.decodeIfPresent(iTunesImage.self, forKey: .itunesImage) - - podcastPeople = try container.decodeIfPresent( - [PodcastPerson].self, - forKey: .podcastPeople - ) ?? [] - podcastTranscripts = try container.decodeIfPresent( - [PodcastTranscript].self, - forKey: .podcastTranscripts - ) ?? [] - podcastChapters = try container.decodeIfPresent( - PodcastChapters.self, - forKey: .podcastChapters - ) - podcastSoundbites = try container.decodeIfPresent( - [PodcastSoundbite].self, - forKey: .podcastSoundbites - ) ?? [] - - podcastSeason = try container.decodeIfPresent( - PodcastSeason.self, - forKey: .podcastSeason - ) - - enclosure = try container.decodeIfPresent(Enclosure.self, forKey: .enclosure) - creators = try container.decode([String].self, forKey: .creators) - - mediaContent = - try container.decodeIfPresent(AtomMedia.self, forKey: .mediaContent) - mediaThumbnail = - try container.decodeIfPresent(AtomMedia.self, forKey: .mediaThumbnail) - - wpPostID = try container.decodeIfPresent(Int.self, forKey: .wpPostID) - wpPostDate = try container.decodeIfPresent(Date.self, forKey: .wpPostDate) - let wpPostDateGMT = try container.decodeIfPresent( - String.self, forKey: .wpPostDateGMT - ) - if let wpPostDateGMT = wpPostDateGMT { - if wpPostDateGMT == "0000-00-00 00:00:00" { - self.wpPostDateGMT = nil - } else { - self.wpPostDateGMT = try container.decode( - Date.self, forKey: .wpPostDateGMT - ) - } - } else { - self.wpPostDateGMT = nil - } - - wpModifiedDate = try container.decodeIfPresent( - Date.self, forKey: .wpModifiedDate - ) - - let wpModifiedDateGMT = try container.decodeIfPresent( - String.self, forKey: .wpModifiedDateGMT - ) - if let wpModifiedDateGMT = wpModifiedDateGMT { - if wpModifiedDateGMT == "0000-00-00 00:00:00" { - self.wpModifiedDateGMT = nil - } else { - self.wpModifiedDateGMT = try container.decode( - Date.self, forKey: .wpModifiedDateGMT - ) - } - } else { - self.wpModifiedDateGMT = nil - } - - let wpAttachmentURLCDData = try container.decodeIfPresent( - CData.self, - forKey: .wpAttachmentURL - ) - wpAttachmentURL = wpAttachmentURLCDData.map { $0.value }.flatMap(URL.init(string:)) - - wpPostName = try container.decodeIfPresent(CData.self, forKey: .wpPostName) - wpPostType = try container.decodeIfPresent(CData.self, forKey: .wpPostType) - wpPostMeta = try container.decodeIfPresent( - [WordPressElements.PostMeta].self, - forKey: .wpPostMeta - ) ?? [] - wpCommentStatus = try container.decodeIfPresent(CData.self, forKey: .wpCommentStatus) - wpPingStatus = try container.decodeIfPresent(CData.self, forKey: .wpPingStatus) - wpStatus = try container.decodeIfPresent(CData.self, forKey: .wpStatus) - wpPostParent = try container.decodeIfPresent(Int.self, forKey: .wpPostParent) - wpMenuOrder = try container.decodeIfPresent(Int.self, forKey: .wpMenuOrder) - wpIsSticky = try container.decodeIfPresent(Int.self, forKey: .wpIsSticky) - wpPostPassword = try container.decodeIfPresent( - CData.self, forKey: .wpPostPassword - ) - } } extension RSSItem: Entryable { diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift index 1624e14..c868abd 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift @@ -1,18 +1,18 @@ import Foundation -internal struct PodcastEpisodeProperties: PodcastEpisode { - internal let title: String? - internal let episode: Int? - internal let author: String? - internal let subtitle: String? - internal let summary: String? - internal let explicit: String? - internal let duration: TimeInterval? - internal let image: iTunesImage? - internal let enclosure: Enclosure - internal let people: [PodcastPerson] +public struct PodcastEpisodeProperties: PodcastEpisode { + public let title: String? + public let episode: Int? + public let author: String? + public let subtitle: String? + public let summary: String? + public let explicit: String? + public let duration: TimeInterval? + public let image: iTunesImage? + public let enclosure: Enclosure + public let people: [PodcastPerson] - internal init?(rssItem: RSSItem) { + public init?(rssItem: RSSItem) { guard let enclosure = rssItem.enclosure else { return nil } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift index 6bc800b..9e1e235 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift @@ -37,6 +37,7 @@ extension PodcastLocation { try? self.init(singleValue: description) } + // swiftlint:disable:next function_body_length public init(singleValue: String) throws { let pathComponents = try Self.pathComponents(from: singleValue) @@ -46,10 +47,8 @@ extension PodcastLocation { let longitude = geoCoords[safe: 1]?.asDouble() else { throw DecodingError.dataCorrupted( - .init( - codingPath: [PodcastLocation.CodingKeys.geo], - debugDescription: "Invalid coordinates for geo attribute: \(singleValue)" - ) + codingKey: PodcastLocation.CodingKeys.geo, + debugDescription: "Invalid coordinates for geo attribute: \(singleValue)" ) } @@ -80,18 +79,14 @@ extension PodcastLocation { guard components[safe: 0] == "geo" else { throw DecodingError.dataCorrupted( - .init( - codingPath: [PodcastLocation.CodingKeys.geo], - debugDescription: "Invalid prefix for geo attribute: \(string)" - ) + codingKey: PodcastLocation.CodingKeys.geo, + debugDescription: "Invalid prefix for geo attribute: \(string)" ) } guard let geoPath = components[safe: 1] else { throw DecodingError.dataCorrupted( - .init( - codingPath: [PodcastLocation.CodingKeys.geo], - debugDescription: "Invalid path for geo attribute: \(string)" - ) + codingKey: PodcastLocation.CodingKeys.geo, + debugDescription: "Invalid path for geo attribute: \(string)" ) } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift index 2f14900..77abcc8 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift @@ -7,10 +7,6 @@ extension PodcastLocation { case node = "N" case way = "W" case relation = "R" - - internal static func isValid(_ rawValue: String) -> Bool { - OsmType(rawValue: rawValue) != nil - } } public let id: Int @@ -24,18 +20,14 @@ extension PodcastLocation { guard let osmType = osmStr.removeFirst().asOsmType() else { throw DecodingError.dataCorrupted( - .init( - codingPath: [PodcastLocation.CodingKeys.osmQuery], - debugDescription: "Invalid type for osm attribute: \(osmStr)" - ) + codingKey: PodcastLocation.CodingKeys.osmQuery, + debugDescription: "Invalid type for osm attribute: \(osmStr)" ) } guard let osmID = osmStr.split(separator: "#")[safe: 0]?.asExactInt() else { throw DecodingError.dataCorrupted( - .init( - codingPath: [PodcastLocation.CodingKeys.osmQuery], - debugDescription: "Invalid id of type Int for osm attribute: \(osmStr)" - ) + codingKey: PodcastLocation.CodingKeys.osmQuery, + debugDescription: "Invalid id of type Int for osm attribute: \(osmStr)" ) } let osmRevision = osmStr.split(separator: "#")[safe: 1]?.asInt() diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift index 95f118b..5f3d6e2 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift @@ -13,6 +13,7 @@ extension PodcastTranscript { self.init(rawValue: caseInsensitive) } + // swiftlint:disable:next cyclomatic_complexity init?(mimeType: MimeType) { switch mimeType { case .plain: diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift new file mode 100644 index 0000000..d290a97 --- /dev/null +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift @@ -0,0 +1,83 @@ +import Foundation + +extension WordPressPost { + // swiftlint:disable:next cyclomatic_complexity function_body_length + public init(item: RSSItem) throws { + guard let name = item.wpPostName else { + throw WordPressError.missingField(.name) + } + guard let type = item.wpPostType else { + throw WordPressError.missingField(.type) + } + guard let creator = item.creators.first else { + throw WordPressError.missingField(.creator) + } + guard let body = item.contentEncoded else { + throw WordPressError.missingField(.body) + } + guard let status = item.wpStatus else { + throw WordPressError.missingField(.status) + } + guard let commentStatus = item.wpCommentStatus else { + throw WordPressError.missingField(.commentStatus) + } + guard let pingStatus = item.wpPingStatus else { + throw WordPressError.missingField(.pingStatus) + } + guard let parentID = item.wpPostParent else { + throw WordPressError.missingField(.parentID) + } + guard let menuOrder = item.wpMenuOrder else { + throw WordPressError.missingField(.menuOrder) + } + guard let id = item.wpPostID else { + throw WordPressError.missingField(.id) + } + guard let isSticky = item.wpIsSticky else { + throw WordPressError.missingField(.isSticky) + } + guard let postDate = item.wpPostDate else { + throw WordPressError.missingField(.postDate) + } + guard let modifiedDate = item.wpModifiedDate else { + throw WordPressError.missingField(.modifiedDate) + } + guard let link = item.link else { + throw WordPressError.missingField(.link) + } + + let title = item.title + let categoryTerms = item.categoryTerms + let meta = item.wpPostMeta + let pubDate = item.pubDate + + let categoryDictionary = Dictionary( + grouping: categoryTerms) { + $0.domain + } + + modifiedDateGMT = item.wpModifiedDateGMT + self.name = name.value + self.title = title + self.type = type.value + self.link = link + self.pubDate = pubDate + self.creator = creator + self.body = body.value + tags = categoryDictionary["post_tag", default: []].map { $0.value } + categories = categoryDictionary["category", default: []].map { $0.value } + self.meta = Dictionary(grouping: meta) { $0.key.value } + .compactMapValues { $0.last?.value.value } + self.status = status.value + self.commentStatus = commentStatus.value + self.pingStatus = pingStatus.value + self.parentID = parentID + self.menuOrder = menuOrder + ID = id + self.isSticky = (isSticky != 0) + self.postDate = postDate + postDateGMT = item.wpPostDateGMT + self.modifiedDate = modifiedDate + attachmentURL = item.wpAttachmentURL + } +} diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index 40aab6f..f2929e0 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -58,136 +58,6 @@ public struct WordPressPost { public let modifiedDate: Date public let modifiedDateGMT: Date? public let attachmentURL: URL? - - internal init( - name: String, - title: String, - type: PostType, - link: URL, - pubDate: Date?, - creator: String, - body: String, - tags: [String], - categories: [String], - meta: [String: String], - status: Status, - commentStatus: CommentStatus, - pingStatus: PingStatus, - parentID: Int?, - menuOrder: Int?, - id: Int, - isSticky: Bool, - postDate: Date, - postDateGMT: Date?, - modifiedDate: Date, - modifiedDateGMT: Date?, - attachmentURL: URL? - ) { - self.name = name - self.title = title - self.type = type - self.link = link - self.pubDate = pubDate - self.creator = creator - self.body = body - self.tags = tags - self.categories = categories - self.meta = meta - self.status = status - self.commentStatus = commentStatus - self.pingStatus = pingStatus - self.parentID = parentID - self.menuOrder = menuOrder - ID = id - self.isSticky = isSticky - self.postDate = postDate - self.postDateGMT = postDateGMT - self.modifiedDate = modifiedDate - self.modifiedDateGMT = modifiedDateGMT - self.attachmentURL = attachmentURL - } -} - -extension WordPressPost { - // swiftlint:disable:next cyclomatic_complexity function_body_length - public init(item: RSSItem) throws { - guard let name = item.wpPostName else { - throw WordPressError.missingField(.name) - } - guard let type = item.wpPostType else { - throw WordPressError.missingField(.type) - } - guard let creator = item.creators.first else { - throw WordPressError.missingField(.creator) - } - guard let body = item.contentEncoded else { - throw WordPressError.missingField(.body) - } - guard let status = item.wpStatus else { - throw WordPressError.missingField(.status) - } - guard let commentStatus = item.wpCommentStatus else { - throw WordPressError.missingField(.commentStatus) - } - guard let pingStatus = item.wpPingStatus else { - throw WordPressError.missingField(.pingStatus) - } - guard let parentID = item.wpPostParent else { - throw WordPressError.missingField(.parentID) - } - guard let menuOrder = item.wpMenuOrder else { - throw WordPressError.missingField(.menuOrder) - } - guard let id = item.wpPostID else { - throw WordPressError.missingField(.id) - } - guard let isSticky = item.wpIsSticky else { - throw WordPressError.missingField(.isSticky) - } - guard let postDate = item.wpPostDate else { - throw WordPressError.missingField(.postDate) - } - guard let modifiedDate = item.wpModifiedDate else { - throw WordPressError.missingField(.modifiedDate) - } - guard let link = item.link else { - throw WordPressError.missingField(.link) - } - - let title = item.title - let categoryTerms = item.categoryTerms - let meta = item.wpPostMeta - let pubDate = item.pubDate - - let categoryDictionary = Dictionary( - grouping: categoryTerms) { - $0.domain - } - - modifiedDateGMT = item.wpModifiedDateGMT - self.name = name.value - self.title = title - self.type = type.value - self.link = link - self.pubDate = pubDate - self.creator = creator - self.body = body.value - tags = categoryDictionary["post_tag", default: []].map { $0.value } - categories = categoryDictionary["category", default: []].map { $0.value } - self.meta = Dictionary(grouping: meta) { $0.key.value } - .compactMapValues { $0.last?.value.value } - self.status = status.value - self.commentStatus = commentStatus.value - self.pingStatus = pingStatus.value - self.parentID = parentID - self.menuOrder = menuOrder - ID = id - self.isSticky = (isSticky != 0) - self.postDate = postDate - postDateGMT = item.wpPostDateGMT - self.modifiedDate = modifiedDate - attachmentURL = item.wpAttachmentURL - } } extension WordPressPost: Hashable { From 4dff9532cc0c334c50479d212785a8d6894bcd76 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 25 Jan 2024 17:35:01 -0500 Subject: [PATCH 18/18] Adding Documentation Comments --- .swiftlint.yml | 6 + Sources/SyndiKit/Common/EntryCategory.swift | 1 + .../SyndiKit/Common/Primitives/CData.swift | 1 - .../Common/Primitives/XMLStringInt.swift | 4 +- Sources/SyndiKit/Decoding/SynDecoder.swift | 6 +- .../Formats/Blogs/CategoryDescriptor.swift | 12 ++ .../Formats/Blogs/CategoryLanguage.swift | 25 ++++ Sources/SyndiKit/Formats/Blogs/Site.swift | 20 ++++ .../SyndiKit/Formats/Blogs/SiteCategory.swift | 28 +++++ .../Formats/Blogs/SiteCategoryType.swift | 1 + .../Formats/Blogs/SiteCollection.swift | 1 + .../Formats/Blogs/SiteDirectory.swift | 112 +++++++++++------- .../Formats/Blogs/SiteDirectoryBuilder.swift | 20 ++++ .../SyndiKit/Formats/Blogs/SiteLanguage.swift | 25 ++++ .../Blogs/SiteLanguageCategory+Site.swift | 12 ++ .../Formats/Blogs/SiteLanguageCategory.swift | 16 +++ .../Formats/Blogs/SiteLanguageContent.swift | 18 +++ .../Formats/Blogs/SiteLanguageType.swift | 1 + .../Formats/Feeds/Atom/AtomCategory.swift | 8 ++ .../Formats/Feeds/Atom/AtomEntry.swift | 37 +++--- .../Formats/Feeds/Atom/AtomFeed.swift | 21 +++- .../Formats/Feeds/Atom/AtomMedia.swift | 21 ++-- .../Formats/Feeds/Atom/AtomMediaGroup.swift | 9 ++ .../Formats/Feeds/JSONFeed/JSONFeed.swift | 28 +++++ .../Formats/Feeds/JSONFeed/JSONItem.swift | 27 +++++ .../Formats/Feeds/RSS/Enclosure.swift | 17 ++- .../SyndiKit/Formats/Feeds/RSS/EntryID.swift | 56 ++++----- .../Formats/Feeds/RSS/RSSChannel.swift | 64 ++++++++-- .../SyndiKit/Formats/Feeds/RSS/RSSFeed.swift | 3 + .../SyndiKit/Formats/Feeds/RSS/RSSImage.swift | 21 ++-- ...ecodings.swift => RSSItem+Decodings.swift} | 11 +- ...RSSItem+inits.swift => RSSItem+Init.swift} | 50 +++++++- .../Formats/Feeds/RSS/RSSItemCategory.swift | 50 ++++++++ .../SyndiKit/Formats/Media/MediaContent.swift | 9 ++ .../Podcast/PodcastChapters+MimeType.swift | 10 +- .../Media/Podcast/PodcastChapters.swift | 5 + .../Media/Podcast/PodcastEpisode.swift | 74 ++++++++++-- .../Media/Podcast/PodcastFunding.swift | 5 + .../Podcast/PodcastLocation+GeoURI.swift | 35 +++++- .../Podcast/PodcastLocation+OsmQuery.swift | 15 ++- .../Media/Podcast/PodcastLocation.swift | 6 + .../Formats/Media/Podcast/PodcastLocked.swift | 9 ++ .../Media/Podcast/PodcastPerson+Role.swift | 13 +- .../Formats/Media/Podcast/PodcastPerson.swift | 15 +++ .../Formats/Media/Podcast/PodcastSeason.swift | 5 + .../Media/Podcast/PodcastSoundbite.swift | 6 + .../Podcast/PodcastTranscript+MimeType.swift | 14 ++- .../Media/Podcast/PodcastTranscript.swift | 10 ++ Sources/SyndiKit/Formats/Media/Video.swift | 6 + .../Formats/Media/Wordpress/WPCategory.swift | 21 ++++ .../Formats/Media/Wordpress/WPPostMeta.swift | 22 ++++ .../Formats/Media/Wordpress/WPTag.swift | 17 +++ .../Wordpress/WordPressPost+RSSItem.swift | 16 ++- .../Media/Wordpress/WordPressPost.swift | 61 +++++++++- .../Formats/Media/YouTube/YouTubeID.swift | 43 +++++-- .../Formats/Media/iTunes/iTunesDuration.swift | 22 +++- .../Formats/Media/iTunes/iTunesEpisode.swift | 8 +- .../Formats/Media/iTunes/iTunesImage.swift | 3 +- .../Formats/Media/iTunes/iTunesOwner.swift | 20 +++- Sources/SyndiKit/SyndiKit.docc/SyndiKit.md | 6 +- Tests/SyndiKitTests/WordpressTests.swift | 6 +- 61 files changed, 1042 insertions(+), 172 deletions(-) rename Sources/SyndiKit/Formats/Feeds/RSS/{RSSItem+decodings.swift => RSSItem+Decodings.swift} (92%) rename Sources/SyndiKit/Formats/Feeds/RSS/{RSSItem+inits.swift => RSSItem+Init.swift} (51%) diff --git a/.swiftlint.yml b/.swiftlint.yml index db96f2b..141707c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -110,6 +110,12 @@ function_parameter_count: 8 line_length: - 90 - 90 +type_name: + excluded: + - iTunesDuration + - iTunesEpisode + - iTunesOwner + - iTunesImage identifier_name: excluded: - id diff --git a/Sources/SyndiKit/Common/EntryCategory.swift b/Sources/SyndiKit/Common/EntryCategory.swift index 4e39016..1ca3717 100644 --- a/Sources/SyndiKit/Common/EntryCategory.swift +++ b/Sources/SyndiKit/Common/EntryCategory.swift @@ -1,4 +1,5 @@ /// Abstract category type. public protocol EntryCategory { + /// Term used for the category var term: String { get } } diff --git a/Sources/SyndiKit/Common/Primitives/CData.swift b/Sources/SyndiKit/Common/Primitives/CData.swift index 13d4795..8bc0149 100644 --- a/Sources/SyndiKit/Common/Primitives/CData.swift +++ b/Sources/SyndiKit/Common/Primitives/CData.swift @@ -1,5 +1,4 @@ /// #CDATA XML element. -public typealias StringLiteralType = String public struct CData: Codable, ExpressibleByStringLiteral, Equatable { public enum CodingKeys: String, CodingKey { case value = "#CDATA" diff --git a/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift b/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift index a01fd8b..d6fc68f 100644 --- a/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift +++ b/Sources/SyndiKit/Common/Primitives/XMLStringInt.swift @@ -1,8 +1,8 @@ -/// XML Element which contains a `String` parsable into a `Integer`. +/// XML Element which contains a ``String`` parsable into a ``Integer``. public struct XMLStringInt: Codable, ExpressibleByIntegerLiteral { public typealias IntegerLiteralType = Int - /// The underlying `Int` value. + /// The underlying ``Int`` value. public let value: Int public init(integerLiteral value: Int) { diff --git a/Sources/SyndiKit/Decoding/SynDecoder.swift b/Sources/SyndiKit/Decoding/SynDecoder.swift index 9bc0cf6..1a02377 100644 --- a/Sources/SyndiKit/Decoding/SynDecoder.swift +++ b/Sources/SyndiKit/Decoding/SynDecoder.swift @@ -77,7 +77,7 @@ public class SynDecoder { self.defaultXMLDecoderSetup = defaultXMLDecoderSetup ?? Self.setupXMLDecoder(_:) } - /// Creates an instance of `RSSDecoder` + /// Creates an instance of ``RSSDecoder`` public convenience init() { self.init(types: nil, defaultJSONDecoderSetup: nil, defaultXMLDecoderSetup: nil) } @@ -93,9 +93,9 @@ public class SynDecoder { decoder.trimValueWhitespaces = false } - /// Returns a `Feedable` object of the type you specify, decoded from a JSON object. + /// Returns a ``Feedable`` object of the type you specify, decoded from a JSON object. /// - Parameter data: The JSON or XML object to decode. - /// - Returns: A `Feedable` object + /// - Returns: A ``Feedable`` object /// /// If the data is not valid RSS, this method throws the /// `DecodingError.dataCorrupted(_:)` error. diff --git a/Sources/SyndiKit/Formats/Blogs/CategoryDescriptor.swift b/Sources/SyndiKit/Formats/Blogs/CategoryDescriptor.swift index 55a8ca6..4d8a8c2 100644 --- a/Sources/SyndiKit/Formats/Blogs/CategoryDescriptor.swift +++ b/Sources/SyndiKit/Formats/Blogs/CategoryDescriptor.swift @@ -1,4 +1,16 @@ +/// A struct representing an Atom category. +/// A descriptor for a category. +/// +/// - Note: This struct is publicly accessible. +/// +/// - Important: The ``title`` and ``description`` properties are read-only. +/// +/// - SeeAlso: ``Category`` +/// - SeeAlso: ``EntryCategory`` public struct CategoryDescriptor { + /// The title of the category. public let title: String + + /// The description of the category. public let description: String } diff --git a/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift b/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift index ea16201..8ccbace 100644 --- a/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift +++ b/Sources/SyndiKit/Formats/Blogs/CategoryLanguage.swift @@ -1,8 +1,33 @@ +/// A struct representing an Atom category. +/// A struct representing a category in a specific language. +/// +/// - Parameters: +/// - languageCategory: The category in a specific language. +/// - language: The language of the category. +/// +/// - Note: This struct is used internally. +/// +/// - SeeAlso: ``SiteCategoryType`` +/// - SeeAlso: ``CategoryDescriptor`` +/// - SeeAlso: ``SiteLanguageType`` +/// - SeeAlso: ``EntryCategory`` public struct CategoryLanguage { + /// The type of the category. public let type: SiteCategoryType + + /// The descriptor of the category. public let descriptor: CategoryDescriptor + + /// The language of the category. public let language: SiteLanguageType + /// A struct representing an Atom category. + /// Initializes a ``CategoryLanguage`` instance. + /// + /// - Parameters: + /// - languageCategory: The category in a specific language. + /// - language: The language of the category. + /// - SeeAlso: ``EntryCategory`` internal init(languageCategory: SiteLanguageCategory, language: SiteLanguageType) { type = languageCategory.slug descriptor = CategoryDescriptor( diff --git a/Sources/SyndiKit/Formats/Blogs/Site.swift b/Sources/SyndiKit/Formats/Blogs/Site.swift index 92fde38..8924b44 100644 --- a/Sources/SyndiKit/Formats/Blogs/Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/Site.swift @@ -1,14 +1,34 @@ import Foundation +/// A struct representing a website. public struct Site { + /// The title of the website. public let title: String + + /// The author of the website. public let author: String + + /// The URL of the website. public let siteURL: URL + + /// The URL of the website's feed. public let feedURL: URL + + /// The URL of the website's Twitter page, if available. public let twitterURL: URL? + + /// The category of the website. public let category: SiteCategoryType + + /// The language of the website. public let language: SiteLanguageType + /// Initializes a new ``Site`` instance. + /// + /// - Parameters: + /// - site: The `SiteLanguageCategory.Site` instance to use as a base. + /// - categoryType: The category type of the website. + /// - languageType: The language type of the website. internal init( site: SiteLanguageCategory.Site, categoryType: SiteCategoryType, diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift index 71282e5..db98462 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCategory.swift @@ -1,7 +1,35 @@ +/// A struct representing an Atom category. +/// A struct representing a site category. +/// +/// - Note: This struct is used to categorize sites based on their type and descriptors. +/// +/// - Parameters: +/// - type: The type of the site category. +/// - descriptors: A dictionary mapping site language types to category descriptors. +/// +/// - Important: This struct should not be used directly. +/// Instead, use the ``SiteCategoryBuilder`` to create instances of ``SiteCategory``. +/// +/// - SeeAlso: ``SiteCategoryType`` +/// - SeeAlso: ``CategoryDescriptor`` +/// - SeeAlso: ``CategoryLanguage`` +/// - SeeAlso: ``SiteCategoryBuilder`` +/// - SeeAlso: ``EntryCategory`` public struct SiteCategory { + /// The type of the site category. public let type: SiteCategoryType + + /// A dictionary mapping site language types to category descriptors. public let descriptors: [SiteLanguageType: CategoryDescriptor] + /// A struct representing an Atom category. + /// Initializes a ``SiteCategory`` instance with the given languages. + /// + /// - Parameter languages: An array of ``CategoryLanguage`` instances. + /// + /// - Returns: A new ``SiteCategory`` instance + /// if at least one language is provided, ``nil`` otherwise. + /// - SeeAlso: ``EntryCategory`` internal init?(languages: [CategoryLanguage]) { guard let type = languages.first?.type else { return nil diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCategoryType.swift b/Sources/SyndiKit/Formats/Blogs/SiteCategoryType.swift index dcaf6fc..311038b 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCategoryType.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCategoryType.swift @@ -1 +1,2 @@ +/// A type alias representing a site category. public typealias SiteCategoryType = String diff --git a/Sources/SyndiKit/Formats/Blogs/SiteCollection.swift b/Sources/SyndiKit/Formats/Blogs/SiteCollection.swift index c3c7980..3fd9523 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteCollection.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteCollection.swift @@ -1 +1,2 @@ +/// A collection of site language content. public typealias SiteCollection = [SiteLanguageContent] diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift index b009695..2a01b2f 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectory.swift @@ -1,55 +1,58 @@ import Foundation +/// A directory of site collections. public struct SiteCollectionDirectory: SiteDirectory { + /// A sequence of sites. public typealias SiteSequence = [Site] - public typealias LanguageSequence = - Dictionary.Values + /// A sequence of languages. + public typealias LanguageSequence = Dictionary.Values - public typealias CategorySequence = - Dictionary.Values + /// A sequence of categories. + public typealias CategorySequence = Dictionary.Values + /// The internal structure of the site collection directory. internal struct Instance { internal let allSites: [Site] internal let languageDictionary: [SiteLanguageType: SiteLanguage] internal let categoryDictionary: [SiteCategoryType: SiteCategory] - internal let languageIndicies: [SiteLanguageType: Set] - internal let categoryIndicies: [SiteCategoryType: Set] + internal let languageIndices: [SiteLanguageType: Set] + internal let categoryIndices: [SiteCategoryType: Set] // swiftlint:disable:next function_body_length internal func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? ) -> [Site] { - let languageIndicies: Set? + let languageIndices: Set? if let language = language { - languageIndicies = self.languageIndicies[language] ?? .init() + languageIndices = self.languageIndices[language] ?? .init() } else { - languageIndicies = nil + languageIndices = nil } - let categoryIndicies: Set? + let categoryIndices: Set? if let category = category { - categoryIndicies = self.categoryIndicies[category] ?? .init() + categoryIndices = self.categoryIndices[category] ?? .init() } else { - categoryIndicies = nil + categoryIndices = nil } - var indicies: Set? + var indices: Set? - if let languageIndicies = languageIndicies { - indicies = languageIndicies + if let languageIndices = languageIndices { + indices = languageIndices } - if let categoryIndicies = categoryIndicies { - if let current = indicies { - indicies = current.intersection(categoryIndicies) + if let categoryIndices = categoryIndices { + if let current = indices { + indices = current.intersection(categoryIndices) } else { - indicies = categoryIndicies + indices = categoryIndices } } - if let current = indicies { + if let current = indices { return current.map { self.allSites[$0] } } else { return allSites @@ -61,14 +64,14 @@ public struct SiteCollectionDirectory: SiteDirectory { var categories = [CategoryLanguage]() var languages = [SiteLanguage]() var sites = [Site]() - var languageIndicies = [SiteLanguageType: Set]() - var categoryIndicies = [SiteCategoryType: Set]() + var languageIndices = [SiteLanguageType: Set]() + var categoryIndices = [SiteCategoryType: Set]() for languageContent in blogs { let language = SiteLanguage(content: languageContent) - var thisLanguageIndicies = [Int]() + var thisLanguageIndices = [Int]() for languageCategory in languageContent.categories { - var thisCategoryIndicies = [Int]() + var thisCategoryIndices = [Int]() let category = CategoryLanguage( languageCategory: languageCategory, language: language.type @@ -81,13 +84,13 @@ public struct SiteCollectionDirectory: SiteDirectory { languageType: language.type ) sites.append(site) - thisCategoryIndicies.append(index) - thisLanguageIndicies.append(index) + thisCategoryIndices.append(index) + thisLanguageIndices.append(index) } - categoryIndicies.formUnion(thisCategoryIndicies, key: category.type) + categoryIndices.formUnion(thisCategoryIndices, key: category.type) categories.append(category) } - languageIndicies.formUnion(thisLanguageIndicies, key: language.type) + languageIndices.formUnion(thisLanguageIndices, key: language.type) languages.append(language) } @@ -96,30 +99,37 @@ public struct SiteCollectionDirectory: SiteDirectory { languageDictionary = Dictionary( uniqueKeysWithValues: languages.map { ($0.type, $0) } ) - self.languageIndicies = languageIndicies - self.categoryIndicies = categoryIndicies + self.languageIndices = languageIndices + self.categoryIndices = categoryIndices allSites = sites } } private let instance: Instance - public var languages: Dictionary< - SiteLanguageType, SiteLanguage - >.Values { + /// A sequence of languages in the site collection directory. + public var languages: Dictionary.Values { instance.languageDictionary.values } - public var categories: Dictionary< - SiteCategoryType, SiteCategory - >.Values { + /// A sequence of categories in the site collection directory. + public var categories: Dictionary.Values { instance.categoryDictionary.values } + /// Initializes a new instance of the ``SiteCollectionDirectory`` struct. + /// + /// - Parameter blogs: The site collection to use. internal init(blogs: SiteCollection) { instance = .init(blogs: blogs) } + /// Retrieves a list of sites based on the specified language and category. + /// + /// - Parameters: + /// - language: The language of the sites to retrieve. + /// - category: The category of the sites to retrieve. + /// - Returns: A list of sites matching the specified language and category. public func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? @@ -128,17 +138,27 @@ public struct SiteCollectionDirectory: SiteDirectory { } } +/// A protocol for site directories. public protocol SiteDirectory { - associatedtype SiteSequence: Sequence - where SiteSequence.Element == Site - associatedtype LanguageSequence: Sequence - where LanguageSequence.Element == SiteLanguage - associatedtype CategorySequence: Sequence - where CategorySequence.Element == SiteCategory - + /// List of Sites + associatedtype SiteSequence: Sequence where SiteSequence.Element == Site + /// List of Languages + associatedtype LanguageSequence: Sequence where LanguageSequence.Element == SiteLanguage + /// List of Categories + associatedtype CategorySequence: Sequence where CategorySequence.Element == SiteCategory + + /// A sequence of languages in the site directory. var languages: LanguageSequence { get } + + /// A sequence of categories in the site directory. var categories: CategorySequence { get } + /// Retrieves a list of sites based on the specified language and category. + /// + /// - Parameters: + /// - language: The language of the sites to retrieve. + /// - category: The category of the sites to retrieve. + /// - Returns: A list of sites matching the specified language and category. func sites( withLanguage language: SiteLanguageType?, withCategory category: SiteCategoryType? @@ -146,6 +166,12 @@ public protocol SiteDirectory { } extension SiteDirectory { + /// Retrieves a list of sites based on the specified language and category. + /// + /// - Parameters: + /// - language: The language of the sites to retrieve. + /// - category: The category of the sites to retrieve. + /// - Returns: A list of sites matching the specified language and category. public func sites( withLanguage language: SiteLanguageType? = nil, withCategory category: SiteCategoryType? = nil diff --git a/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift b/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift index 6273543..7f6c53f 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteDirectoryBuilder.swift @@ -1,13 +1,33 @@ import Foundation +/// A builder for creating a site collection directory. public struct SiteCollectionDirectoryBuilder: SiteDirectoryBuilder { + /// Initializes a new instance of ``SiteCollectionDirectoryBuilder``. public init() {} + + /// A struct representing an Atom category. + /// Creates a site collection directory from a site collection. + /// + /// - Parameter blogs: The site collection to build the directory from. + /// + /// - Returns: A new instance of ``SiteCollectionDirectory``. + /// - SeeAlso: ``EntryCategory`` public func directory(fromCollection blogs: SiteCollection) -> SiteCollectionDirectory { SiteCollectionDirectory(blogs: blogs) } } +/// A protocol for building site directories. public protocol SiteDirectoryBuilder { + /// The type of site directory to build. associatedtype SiteDirectoryType: SiteDirectory + + /// A struct representing an Atom category. + /// Creates a site directory from a site collection. + /// + /// - Parameter blogs: The site collection to build the directory from. + /// + /// - Returns: A new instance of ``SiteDirectoryType``. + /// - SeeAlso: ``EntryCategory`` func directory(fromCollection blogs: SiteCollection) -> SiteDirectoryType } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift index 75733bb..23c684f 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguage.swift @@ -1,7 +1,32 @@ +/// A struct representing an Atom category. +/// A struct representing a site language. +/// +/// Use this struct to define the type and title of a site language. +/// +/// - Note: This struct is used internally and should not be directly instantiated. +/// +/// - Parameters: +/// - content: The content of the site language. +/// +/// - SeeAlso: ``SiteLanguageType`` +/// +/// - Author: Your Name +/// - SeeAlso: ``EntryCategory`` public struct SiteLanguage { + /// The type of the site language. public let type: SiteLanguageType + + /// The title of the site language. public let title: String + /// A struct representing an Atom category. + /// Initializes a new ``SiteLanguage`` instance. + /// + /// - Parameters: + /// - content: The content of the site language. + /// + /// - Returns: A new ``SiteLanguage`` instance. + /// - SeeAlso: ``EntryCategory`` internal init(content: SiteLanguageContent) { type = content.language title = content.title diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift index 634511c..b5a16b9 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory+Site.swift @@ -2,13 +2,24 @@ import Foundation // swiftlint:disable nesting extension SiteLanguageCategory { + /// A ``struct`` representing a site. public struct Site: Codable { + /// The title of the site. public let title: String + + /// The author of the site. public let author: String + + /// The URL of the site. public let siteURL: URL + + /// The URL of the site's feed. public let feedURL: URL + + /// The URL of the site's Twitter page. public let twitterURL: URL? + /// Coding keys to map properties to JSON keys. internal enum CodingKeys: String, CodingKey { case title case author @@ -19,4 +30,5 @@ extension SiteLanguageCategory { } } +/// A type alias for `SiteLanguageCategory.Site`. public typealias SiteStub = SiteLanguageCategory.Site diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.swift index d485780..d7e9290 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageCategory.swift @@ -1,6 +1,22 @@ +/// A struct representing an Atom category. +/// A struct representing a category of site languages. +/// +/// - Note: This struct conforms to the ``Codable`` protocol. +/// +/// - Important: All properties of this struct are read-only. +/// +/// - SeeAlso: ``Site`` +/// - SeeAlso: ``EntryCategory`` public struct SiteLanguageCategory: Codable { + /// The title of the category. public let title: String + + /// The slug of the category. public let slug: String + + /// A description of the category. public let description: String + + /// An array of sites belonging to this category. public let sites: [Site] } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageContent.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageContent.swift index 4141712..4f9c7ab 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageContent.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageContent.swift @@ -1,5 +1,23 @@ +/// A struct representing an Atom category. +/// A struct representing the content of a site in a specific language. +/// +/// - Note: This struct conforms to the ``Codable`` protocol. +/// +/// - Important: All properties of this struct are read-only. +/// +/// - SeeAlso: ``SiteLanguageCategory`` +/// +/// - Author: Your Name +/// +/// - Version: 1.0 +/// - SeeAlso: ``EntryCategory`` public struct SiteLanguageContent: Codable { + /// The language of the site content. public let language: String + + /// The title of the site. public let title: String + + /// The categories of the site. public let categories: [SiteLanguageCategory] } diff --git a/Sources/SyndiKit/Formats/Blogs/SiteLanguageType.swift b/Sources/SyndiKit/Formats/Blogs/SiteLanguageType.swift index 289a526..28fa6e6 100644 --- a/Sources/SyndiKit/Formats/Blogs/SiteLanguageType.swift +++ b/Sources/SyndiKit/Formats/Blogs/SiteLanguageType.swift @@ -1 +1,2 @@ +/// A type representing the language of a website. public typealias SiteLanguageType = String diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomCategory.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomCategory.swift index a5df7d6..e17c502 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomCategory.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomCategory.swift @@ -1,3 +1,11 @@ +/// A struct representing an Atom category. +/// A struct representing an Atom category. +/// +/// - Note: This struct conforms to the ``Codable`` and ``EntryCategory`` protocols. +/// +/// - SeeAlso: ``EntryCategory`` +/// - SeeAlso: ``EntryCategory`` public struct AtomCategory: Codable, EntryCategory { + /// The term of the category. public let term: String } diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift index fd6d70a..c6cd3b7 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomEntry.swift @@ -1,6 +1,8 @@ import Foundation +/// A struct representing an entry in an Atom feed. public struct AtomEntry: Codable { + /// The coding keys used for encoding and decoding. public enum CodingKeys: String, CodingKey { case id case title @@ -19,62 +21,67 @@ public struct AtomEntry: Codable { /// A permanent, universally unique identifier for an entry. public let id: EntryID - /// a Text construct that conveys a human-readable title + /// A human-readable title for the entry. public let title: String - /// The most recent instant in time when the entry was published + /// The most recent instant in time when the entry was published. public let published: Date? - /// Content of the trny. + /// The content of the entry. public let content: String? - /// The most recent instant in time when the entry was modified in a way - /// the publisher considers significant. + /// The most recent instant in time when the entry was modified. public let updated: Date - /// Cateogires of the entry. + /// The categories associated with the entry. public let atomCategories: [AtomCategory] - /// a reference to a Web resource. + /// The links associated with the entry. public let links: [Link] - /// The author of the entry. - public let authors: [Author] - /// YouTube channel ID, if from a YouTube channel. - public let youtubeChannelID: String? + /// The authors of the entry. + public let authors: [Author] - /// YouTube video ID, if from a YouTube channel. + /// The YouTube video ID, if the entry is from a YouTube channel. public let youtubeVideoID: String? - /// the person or entity who wrote an item + /// The YouTube channel ID, if the entry is from a YouTube channel. + public let youtubeChannelID: String? + + /// The creators of the entry. public let creators: [String] - /// Grouping of elements that are effectively the same content, - /// yet different representations. + /// The media group associated with the entry. public let mediaGroup: AtomMediaGroup? } extension AtomEntry: Entryable { + /// The categories associated with the entry. public var categories: [EntryCategory] { atomCategories } + /// The URL of the entry. public var url: URL? { links.first?.href } + /// The HTML content of the entry. public var contentHtml: String? { content?.trimmingCharacters(in: .whitespacesAndNewlines) } + /// The summary of the entry. public var summary: String? { nil } + /// The media content of the entry. public var media: MediaContent? { YouTubeIDProperties(entry: self).map(Video.youtube).map(MediaContent.video) } + /// The URL of the entry's image. public var imageURL: URL? { mediaGroup?.thumbnails.first?.url } diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift index 36217b1..ecb522d 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomFeed.swift @@ -1,9 +1,11 @@ import Foundation +/// A struct representing an Atom category. /// An XML-based Web content and metadata syndication format. /// /// Based on the /// [specifications here](https://datatracker.ietf.org/doc/html/rfc4287#section-4.1.2). +/// - SeeAlso: ``EntryCategory`` public struct AtomFeed { public enum CodingKeys: String, CodingKey { case id @@ -27,10 +29,10 @@ public struct AtomFeed { /// Often the same as the title of the associated website. public let title: String - /// Contains a human-readable description or subtitle for the feed + /// Contains a human-readable description or subtitle for the feed. public let description: String? - /// Contains a human-readable description or subtitle for the feed + /// Contains a human-readable description or subtitle for the feed. public let subtitle: String? /// The publication date for the content in the channel. @@ -39,12 +41,12 @@ public struct AtomFeed { /// The publication date for the content in the channel. public let pubDate: Date? - /// a reference from an entry or feed to a Web resource. + /// A reference from an entry or feed to a Web resource. public let links: [Link] - /// An individual entry, - /// acting as a container for metadata and data associated with the entry + /// Individual entries of the feed. public let entries: [AtomEntry] + /// The author of the feed. public let authors: [Author] @@ -53,34 +55,43 @@ public struct AtomFeed { } extension AtomFeed: DecodableFeed { + /// The source of the decoder for AtomFeed. internal static let source: DecoderSetup = DecoderSource.xml + /// The label for AtomFeed. internal static let label: String = "Atom" + /// The summary of the AtomFeed. public var summary: String? { description ?? subtitle } + /// The children of the AtomFeed. public var children: [Entryable] { entries } + /// The site URL of the AtomFeed. public var siteURL: URL? { links.first { $0.rel != "self" }?.href } + /// The updated date of the AtomFeed. public var updated: Date? { pubDate ?? published } + /// The copyright of the AtomFeed. public var copyright: String? { nil } + /// The image URL of the AtomFeed. public var image: URL? { nil } + /// The syndication update of the AtomFeed. public var syndication: SyndicationUpdate? { nil } diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMedia.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMedia.swift index 1cbf53e..0c6c54d 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMedia.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMedia.swift @@ -1,18 +1,21 @@ import Foundation -/// Media structure which enables -/// content publishers and bloggers to syndicate multimedia content -/// such as TV and video clips, movies, images and audio. +/// A struct representing an Atom category. +/// Media structure which enables content publishers and bloggers +/// to syndicate multimedia content such as TV and video clips, movies, images and audio. /// -/// Fore more detils check out +/// For more details, check out /// [the Media RSS Specification](https://www.rssboard.org/media-rss). +/// - SeeAlso: ``EntryCategory`` public struct AtomMedia: Codable { - /// The type of object. + /// A struct representing an Atom category. + /// The type of object. /// - /// While this attribute can at times seem redundant if type is supplied, - /// it is included because it simplifies decision making on the reader side, - /// as well as flushes out any ambiguities between MIME type and object type. - /// It is an optional attribute. + /// While this attribute can at times seem redundant if type is supplied, + /// it is included because it simplifies decision making on the reader side, + /// as well as flushes out any ambiguities between MIME type and object type. + /// It is an optional attribute. + /// - SeeAlso: ``EntryCategory`` public let url: URL /// The direct URL to the media object. diff --git a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift index ebb59c8..bae80db 100644 --- a/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift +++ b/Sources/SyndiKit/Formats/Feeds/Atom/AtomMediaGroup.swift @@ -1,6 +1,8 @@ import Foundation +/// A group of media elements in an Atom feed. public struct AtomMediaGroup: Codable { + /// Coding keys for encoding and decoding. public enum CodingKeys: String, CodingKey { case title = "media:title" case descriptions = "media:description" @@ -8,8 +10,15 @@ public struct AtomMediaGroup: Codable { case thumbnails = "media:thumbnail" } + /// The title of the media group. public let title: String? + + /// The media elements within the group. public let contents: [AtomMedia] + + /// The thumbnail images associated with the media group. public let thumbnails: [AtomMedia] + + /// The descriptions of the media group. public let descriptions: [String] } diff --git a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift index 86e1191..633930f 100644 --- a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONFeed.swift @@ -1,52 +1,80 @@ import Foundation +/// A struct representing an Atom category. +/// A struct representing a JSON feed. +/// +/// - Note: This struct conforms to the ``DecodableFeed`` protocol. +/// +/// - SeeAlso: ``DecodableFeed`` +/// - SeeAlso: ``EntryCategory`` public struct JSONFeed { + /// The version of the JSON feed. public let version: URL + + /// The title of the JSON feed. public let title: String + + /// The URL of the home page associated with the feed. public let homePageUrl: URL + + /// A description of the JSON feed. public let description: String? /// The author of the feed. public let author: Author? + + /// The items in the JSON feed. public let items: [JSONItem] } extension JSONFeed: DecodableFeed { + /// The source of the decoder for JSON feed. internal static let source: DecoderSetup = DecoderSource.json + + /// The label for the JSON feed. internal static let label: String = "JSON" + /// The YouTube channel ID associated with the feed. public var youtubeChannelID: String? { nil } + /// The children of the JSON feed. public var children: [Entryable] { items } + /// The summary of the JSON feed. public var summary: String? { description } + /// The site URL associated with the feed. public var siteURL: URL? { homePageUrl } + /// The last updated date of the JSON feed. public var updated: Date? { nil } + /// The copyright information of the JSON feed. public var copyright: String? { nil } + /// The image URL associated with the feed. public var image: URL? { nil } + /// The syndication update information of the JSON feed. public var syndication: SyndicationUpdate? { nil } + /// The authors of the JSON feed. public var authors: [Author] { guard let author = author else { return [] diff --git a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift index e324310..74703bb 100644 --- a/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift +++ b/Sources/SyndiKit/Formats/Feeds/JSONFeed/JSONItem.swift @@ -1,11 +1,25 @@ import Foundation +/// A struct representing an Atom category. +/// A struct representing an item in JSON format. +/// - SeeAlso: ``EntryCategory`` public struct JSONItem: Codable { + /// The unique identifier of the item. public let guid: EntryID + + /// The URL associated with the item. public let url: URL? + + /// The title of the item. public let title: String + + /// The HTML content of the item. public let contentHtml: String? + + /// A summary of the item. public let summary: String? + + /// The date the item was published. public let datePublished: Date? /// The author of the item. @@ -13,6 +27,9 @@ public struct JSONItem: Codable { } extension JSONItem: Entryable { + /// A struct representing an Atom category. + /// Returns an array of authors for the item. + /// - SeeAlso: ``EntryCategory`` public var authors: [Author] { guard let author = author else { return [] @@ -20,26 +37,36 @@ extension JSONItem: Entryable { return [author] } + /// The URL of the item's image. public var imageURL: URL? { nil } + /// A struct representing an Atom category. + /// An array of creators associated with the item. + /// - SeeAlso: ``EntryCategory`` public var creators: [String] { [] } + /// The date the item was published. public var published: Date? { datePublished } + /// The unique identifier of the item. public var id: EntryID { guid } + /// A struct representing an Atom category. + /// An array of categories associated with the item. + /// - SeeAlso: ``EntryCategory`` public var categories: [EntryCategory] { [] } + /// The media content associated with the item. public var media: MediaContent? { nil } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift index 23f711d..3df9ccf 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/Enclosure.swift @@ -1,5 +1,6 @@ import Foundation +/// A struct representing an enclosure for a resource. public struct Enclosure: Codable { internal enum CodingKeys: String, CodingKey { case url @@ -7,17 +8,31 @@ public struct Enclosure: Codable { case length } + /// The URL of the enclosure. public let url: URL + + /// The type of the enclosure. public let type: String + + /// The length of the enclosure, if available. public let length: Int? + /// Initializes a new ``Enclosure`` instance from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if the decoding process fails. public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: Self.CodingKeys.self) + let container = try decoder.container(keyedBy: CodingKeys.self) url = try container.decode(UTF8EncodedURL.self, forKey: .url).value type = try container.decode(String.self, forKey: .type) length = try Self.decodeLength(from: container) } + /// Decodes the length of the enclosure from the given container. + /// + /// - Parameter container: The container to decode from. + /// - Returns: The length of the enclosure, or ``nil`` if not available. + /// - Throws: An error if the decoding process fails. private static func decodeLength( from container: KeyedDecodingContainer ) throws -> Int? { diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift b/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift index 11e0377..ed2d004 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/EntryID.swift @@ -1,43 +1,28 @@ import Foundation -/// Entry identifier based on the RSS guid. -/// ## Topics +/// An identifier for an entry based on the RSS guid. /// -/// ### Enumeration Cases -/// -/// - ``url(_:)`` -/// - ``uuid(_:)`` -/// - ``path(_:separatedBy:)`` -/// - ``string(_:)`` -/// -/// ### String Conversion -/// -/// - ``init(string:)`` -/// - ``description`` -/// - ``init(_:)`` -/// -/// ### Codable Overrides -/// -/// - ``init(from:)`` -/// - ``encode(to:)`` +/// - Note: This enum conforms to +/// ``Codable``, ``Equatable``, and ``LosslessStringConvertible``. public enum EntryID: Codable, Equatable, LosslessStringConvertible { - /// URL format. + /// An identifier in URL format. case url(URL) - /// UUID format. + /// An identifier in UUID format. case uuid(UUID) - /// String path separated by a character string. + /// An identifier in string path format. /// - /// This is generally used by YouTube's RSS feed. in the format of: + /// This format is commonly used by YouTube's RSS feed, in the format of: /// ``` /// yt:video:(YouTube Video ID) /// ``` case path([String], separatedBy: String) - /// Plain un-parsable String. + /// An identifier in plain un-parsable string format. case string(String) + /// A string representation of the entry identifier. public var description: String { let string: String switch self { @@ -56,16 +41,19 @@ public enum EntryID: Codable, Equatable, LosslessStringConvertible { return string } - /// Implementation of ``LosslessStringConvertible`` initializer. - /// This will never return a nil instance. - /// Therefore you should use ``init(string:)``to avoid the `Optional` result. + /// Initializes an ``EntryID`` from a string. + /// + /// - Parameter description: The string representation of the entry identifier. + /// - Note: This initializer will never return a ``nil`` instance. + /// To avoid the ``Optional`` result, use `init(string:)` instead. public init?(_ description: String) { self.init(string: description) } - /// Parses the String into a ``EntryID`` - /// - Parameter string: The String to parse. - /// You should use this rather than ``init(_:)`` to avoid the `Optional` result. + /// Initializes an ``EntryID`` from a string. + /// + /// - Parameter string: The string representation of the entry identifier. + /// - Note: Use this initializer instead of `init(_:)` to avoid the ``Optional`` result. public init(string: String) { if let url = URL(strict: string) { self = .url(url) @@ -86,12 +74,20 @@ public enum EntryID: Codable, Equatable, LosslessStringConvertible { } } + /// Initializes an ``EntryID`` from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if the decoding process fails. public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let string = try container.decode(String.self) self.init(string: string) } + /// Encodes the ``EntryID`` into the given encoder. + /// + /// - Parameter encoder: The encoder to write data to. + /// - Throws: An error if the encoding process fails. public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(description) diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift index f7a64b6..c90d527 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSChannel.swift @@ -1,6 +1,22 @@ import Foundation -/// information about the channel (metadata) and its contents. +/// A struct representing an Atom category. +/// A struct representing information about the channel (metadata) and its contents. +/// +/// - Note: This struct conforms to the ``Codable`` protocol. +/// +/// - Remark: The ``CodingKeys`` enum is used to specify the coding keys for the struct. +/// +/// - SeeAlso: ``RSSItem`` +/// - SeeAlso: ``RSSImage`` +/// - SeeAlso: ``Author`` +/// - SeeAlso: ``WordPressElements.Category`` +/// - SeeAlso: ``WordPressElements.Tag`` +/// - SeeAlso: ``iTunesOwner`` +/// - SeeAlso: ``PodcastLocked`` +/// - SeeAlso: ``PodcastFunding`` +/// - SeeAlso: ``PodcastPerson`` +/// - SeeAlso: ``EntryCategory`` public struct RSSChannel: Codable { internal enum CodingKeys: String, CodingKey { case title @@ -39,24 +55,31 @@ public struct RSSChannel: Codable { /// The last time the content of the channel changed. public let lastBuildDate: Date? - /// indicates the publication date and time of the feed's content + /// Indicates the publication date and time of the feed's content. public let pubDate: Date? - /// ttl stands for time to live. - /// It's a number of minutes - /// that indicates how long a channel can be cached - /// before refreshing from the source. + /// TTL stands for time to live. + /// It's a number of minutes that indicates + /// how long a channel can be cached before refreshing from the source. public let ttl: Int? + /// Describes the period over which the channel format is updated. public let syUpdatePeriod: SyndicationUpdatePeriod? - /// Used to describe the frequency of updates - /// in relation to the update period. - /// A positive integer indicates - /// how many times in that period the channel is updated. + + /// Used to describe the frequency of updates in relation to the update period. + /// A positive integer indicates how many times in that period the channel is updated. public let syUpdateFrequency: SyndicationUpdateFrequency? + + /// The items contained in the channel. public let items: [RSSItem] + + /// The author of the channel. public let itunesAuthor: String? + + /// The image associated with the channel. public let itunesImage: String? + + /// The owner of the channel. public let itunesOwner: iTunesOwner? /// Copyright notice for content in the channel. @@ -64,18 +87,39 @@ public struct RSSChannel: Codable { /// Specifies a GIF, JPEG or PNG image that can be displayed with the channel. public let image: RSSImage? + + /// The author of the channel. public let author: Author? + + /// The categories associated with the channel. public let wpCategories: [WordPressElements.Category] + + /// The tags associated with the channel. public let wpTags: [WordPressElements.Tag] + + /// The base site URL of the channel. public let wpBaseSiteURL: URL? + + /// The base blog URL of the channel. public let wpBaseBlogURL: URL? + /// Indicates whether the podcast is locked. public let podcastLocked: PodcastLocked? + + /// The fundings associated with the podcast. public let podcastFundings: [PodcastFunding] + + /// The people associated with the podcast. public let podcastPeople: [PodcastPerson] } extension RSSChannel { + /// A struct representing an Atom category. + /// A computed property that returns a ``SyndicationUpdate`` object + /// based on the ``syUpdatePeriod`` and ``syUpdateFrequency`` properties. + /// + /// - Returns: A ``SyndicationUpdate`` object. + /// - SeeAlso: ``EntryCategory`` public var syndication: SyndicationUpdate? { SyndicationUpdate( period: syUpdatePeriod, diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift index 250b5db..81f4800 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSFeed.swift @@ -1,5 +1,6 @@ import Foundation +/// A struct representing an Atom category. /// RSS is a Web content syndication format. /// /// Its name is an acronym for Really Simple Syndication. @@ -13,7 +14,9 @@ import Foundation /// the version attribute must be 2.0. /// For more details, check out the /// [W3 sepcifications.](https://validator.w3.org/feed/docs/rss2.html) +/// - SeeAlso: ``EntryCategory`` public struct RSSFeed { + /// Root Channel of hte RSS Feed public let channel: RSSChannel } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSImage.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSImage.swift index 022d8b3..6c89510 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSImage.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSImage.swift @@ -1,21 +1,20 @@ import Foundation -/// Specifies a GIF, JPEG or PNG image. +/// Represents a GIF, JPEG, or PNG image. public struct RSSImage: Codable { - /// The URL of a GIF, JPEG or PNG image + /// The URL of the image. public let url: URL - /// Describes the image. + /// The title or description of the image. /// - /// It's used in the ALT attribute of the HTML tag + /// This is used in the ``alt`` attribute of the HTML `` tag /// when the channel is rendered in HTML. public let title: String - /// The URL of the site, when the channel is rendered, - /// the image is a link to the site. + /// The URL of the site that the image links to. /// - /// In practice the image and <link> should have - /// the same value as the channel's <title> and <link> + /// In practice, the image ``title`` and ``link`` should have + /// the same value as the channel's ``title`` and ``link``. public let link: URL /// The width of the image in pixels. @@ -24,7 +23,9 @@ public struct RSSImage: Codable { /// The height of the image in pixels. public let height: Int? - /// This contains text that is included in the TITLE attribute - /// of the link formed around the image in the HTML rendering. + /// Additional description of the image. + /// + /// This text is included in the ``title`` attribute of the link + /// formed around the image in the HTML rendering. public let description: String? } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Decodings.swift similarity index 92% rename from Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift rename to Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Decodings.swift index b271175..7d2cd94 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+decodings.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Decodings.swift @@ -2,7 +2,14 @@ import Foundation import XMLCoder extension RSSItem { - // swiftlint:disable:next function_body_length + // swiftlint:disable function_body_length + /// A struct representing an Atom category. + /// Initializes a new ``RSSItem`` by decoding data from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// + /// - Throws: An error if the decoding fails. + /// - SeeAlso: ``EntryCategory`` public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) title = try container.decode(String.self, forKey: .title) @@ -114,4 +121,6 @@ extension RSSItem { CData.self, forKey: .wpPostPassword ) } + + // swiftlint:enable function_body_length } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Init.swift similarity index 51% rename from Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift rename to Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Init.swift index 4431eea..a6671c0 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+inits.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItem+Init.swift @@ -2,7 +2,53 @@ import Foundation import XMLCoder extension RSSItem { - // swiftlint:disable:next function_body_length + // swiftlint:disable function_body_length + /// A struct representing an Atom category. + /// Initializes a new ``RSSItem`` instance. + /// + /// - Parameters: + /// - title: The title of the RSS item. + /// - link: The URL link of the RSS item. + /// - description: The description of the RSS item. + /// - guid: The globally unique identifier of the RSS item. + /// - pubDate: The publication date of the RSS item. + /// - contentEncoded: The encoded content of the RSS item. + /// - categoryTerms: The category terms of the RSS item. + /// - content: The content of the RSS item. + /// - itunesTitle: The iTunes title of the RSS item. + /// - itunesEpisode: The iTunes episode of the RSS item. + /// - itunesAuthor: The iTunes author of the RSS item. + /// - itunesSubtitle: The iTunes subtitle of the RSS item. + /// - itunesSummary: The iTunes summary of the RSS item. + /// - itunesExplicit: The iTunes explicit flag of the RSS item. + /// - itunesDuration: The iTunes duration of the RSS item. + /// - itunesImage: The iTunes image of the RSS item. + /// - podcastPeople: The podcast people of the RSS item. + /// - podcastTranscripts: The podcast transcripts of the RSS item. + /// - podcastChapters: The podcast chapters of the RSS item. + /// - podcastSoundbites: The podcast soundbites of the RSS item. + /// - podcastSeason: The podcast season of the RSS item. + /// - enclosure: The enclosure of the RSS item. + /// - creators: The creators of the RSS item. + /// - wpCommentStatus: The WordPress comment status of the RSS item. + /// - wpPingStatus: The WordPress ping status of the RSS item. + /// - wpStatus: The WordPress status of the RSS item. + /// - wpPostParent: The WordPress post parent of the RSS item. + /// - wpMenuOrder: The WordPress menu order of the RSS item. + /// - wpIsSticky: The WordPress sticky flag of the RSS item. + /// - wpPostPassword: The WordPress post password of the RSS item. + /// - wpPostID: The WordPress post ID of the RSS item. + /// - wpPostDate: The WordPress post date of the RSS item. + /// - wpPostDateGMT: The WordPress post date in GMT of the RSS item. + /// - wpModifiedDate: The WordPress modified date of the RSS item. + /// - wpModifiedDateGMT: The WordPress modified date in GMT of the RSS item. + /// - wpPostName: The WordPress post name of the RSS item. + /// - wpPostType: The WordPress post type of the RSS item. + /// - wpPostMeta: The WordPress post meta of the RSS item. + /// - wpAttachmentURL: The WordPress attachment URL of the RSS item. + /// - mediaContent: The media content of the RSS item. + /// - mediaThumbnail: The media thumbnail of the RSS item. + /// - SeeAlso: ``EntryCategory`` public init( title: String, link: URL, @@ -88,4 +134,6 @@ extension RSSItem { self.mediaContent = mediaContent self.mediaThumbnail = mediaThumbnail } + + // swiftlint:enable function_body_length } diff --git a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift index 048271c..676d797 100644 --- a/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift +++ b/Sources/SyndiKit/Formats/Feeds/RSS/RSSItemCategory.swift @@ -1,24 +1,65 @@ +/// A struct representing an Atom category. +/// A struct representing a category for an RSS item. +/// +/// This struct conforms to the ``Codable`` and ``EntryCategory`` protocols. +/// +/// - Note: The ``CodingKeys`` enum is used to specify the coding keys for the struct. +/// +/// - SeeAlso: ``EntryCategory`` +/// +/// - Remark: This struct is ``public`` to allow access from other modules. +/// +/// - Warning: Do not modify the ``CodingKeys`` enum. +/// +/// - Version: 1.0 +/// - SeeAlso: ``EntryCategory`` public struct RSSItemCategory: Codable, EntryCategory { + /// The coding keys for the struct. internal enum CodingKeys: String, CodingKey { case value = "#CDATA" case domain case nicename } + /// The term of the category. public var term: String { value } + /// The value of the category. public let value: String + + /// The domain of the category. public let domain: String? + + /// The nicename of the category. public let nicename: String? + /// A struct representing an Atom category. + /// Initializes a new instance of ``RSSItemCategory``. + /// + /// - Parameters: + /// - value: The value of the category. + /// - domain: The domain of the category. Default value is ``nil``. + /// - nicename: The nicename of the category. Default value is ``nil``. + /// + /// - Returns: A new instance of ``RSSItemCategory``. + /// - SeeAlso: ``EntryCategory`` public init(value: String, domain: String? = nil, nicename: String? = nil) { self.value = value self.domain = domain self.nicename = nicename } + /// A struct representing an Atom category. + /// Initializes a new instance of ``RSSItemCategory`` from a decoder. + /// + /// - Parameter decoder: The decoder to use for decoding. + /// + /// - Throws: An error if the decoding fails. + /// + /// - Returns: A new instance of ``RSSItemCategory``. + /// - SeeAlso: ``EntryCategory`` public init(from decoder: Decoder) throws { let value: String let container: KeyedDecodingContainer<CodingKeys>? @@ -38,6 +79,15 @@ public struct RSSItemCategory: Codable, EntryCategory { } extension RSSItemCategory: Equatable { + /// A struct representing an Atom category. + /// Checks if two ``RSSItemCategory`` instances are equal. + /// + /// - Parameters: + /// - lhs: The left-hand side ``RSSItemCategory`` instance. + /// - rhs: The right-hand side ``RSSItemCategory`` instance. + /// + /// - Returns: ``true`` if the instances are equal, otherwise ``false``. + /// - SeeAlso: ``EntryCategory`` public static func == (lhs: RSSItemCategory, rhs: RSSItemCategory) -> Bool { lhs.value == rhs.value && lhs.domain == rhs.domain diff --git a/Sources/SyndiKit/Formats/Media/MediaContent.swift b/Sources/SyndiKit/Formats/Media/MediaContent.swift index 59593f1..fe76944 100644 --- a/Sources/SyndiKit/Formats/Media/MediaContent.swift +++ b/Sources/SyndiKit/Formats/Media/MediaContent.swift @@ -1,4 +1,13 @@ +/// A struct representing an Atom category. +/// Represents different types of media content. +/// +/// - podcast: A podcast episode. +/// - video: A video. +/// - SeeAlso: ``EntryCategory`` public enum MediaContent { + /// A podcast episode. case podcast(PodcastEpisode) + + /// A video. case video(Video) } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift index de3cac7..e8f19a1 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters+MimeType.swift @@ -1,13 +1,16 @@ import Foundation extension PodcastChapters { + /// A private enum representing known MIME types for podcast chapters. private enum KnownMimeType: String, Codable { case json = "application/json+chapters" + /// Initializes a ``KnownMimeType`` from a case-insensitive string. init?(caseInsensitive: String) { self.init(rawValue: caseInsensitive) } + /// Initializes a ``KnownMimeType`` from a ``MimeType``. init?(mimeType: MimeType) { switch mimeType { case .json: @@ -19,10 +22,12 @@ extension PodcastChapters { } } + /// An enum representing the MIME type of podcast chapters. public enum MimeType: Codable, Equatable, RawRepresentable { case json case unknown(String) + /// The raw value of the MIME type. public var rawValue: String { if let knownMimeType = KnownMimeType(mimeType: self) { return knownMimeType.rawValue @@ -31,15 +36,17 @@ extension PodcastChapters { } else { fatalError( // swiftlint:disable:next line_length - "Type attribute of <podcast:chapters> with value: \(self) should either be `KnownMimeType`, or unknown!" + "Type attribute of <podcast:chapters> with value: \(self) should either be ``KnownMimeType``, or unknown!" ) } } + /// Initializes a ``MimeType`` from a raw value. public init?(rawValue: String) { self.init(caseInsensitive: rawValue) } + /// Initializes a ``MimeType`` from a case-insensitive string. public init(caseInsensitive: String) { if let knownMimeType = KnownMimeType(caseInsensitive: caseInsensitive) { self = .init(knownMimeType: knownMimeType) @@ -48,6 +55,7 @@ extension PodcastChapters { } } + /// Initializes a ``MimeType`` from a ``KnownMimeType``. private init(knownMimeType: KnownMimeType) { switch knownMimeType { case .json: diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift index e843458..a3ef7e5 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastChapters.swift @@ -1,11 +1,16 @@ import Foundation +/// A struct representing chapters of a podcast. public struct PodcastChapters: Codable, Equatable { + /// The coding keys for encoding and decoding. public enum CodingKeys: String, CodingKey { case url case type } + /// The URL of the chapter file. public let url: URL + + /// The MIME type of the chapter file. public let type: MimeType } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift index c868abd..7ccaa71 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastEpisode.swift @@ -1,18 +1,46 @@ import Foundation -public struct PodcastEpisodeProperties: PodcastEpisode { - public let title: String? - public let episode: Int? - public let author: String? - public let subtitle: String? - public let summary: String? - public let explicit: String? - public let duration: TimeInterval? - public let image: iTunesImage? - public let enclosure: Enclosure - public let people: [PodcastPerson] - - public init?(rssItem: RSSItem) { +/// A struct representing properties of a podcast episode. +internal struct PodcastEpisodeProperties: PodcastEpisode { + /// The title of the episode. + internal let title: String? + + /// The episode number. + internal let episode: Int? + + /// The author of the episode. + internal let author: String? + + /// The subtitle of the episode. + internal let subtitle: String? + + /// A summary of the episode. + internal let summary: String? + + /// Indicates if the episode contains explicit content. + internal let explicit: String? + + /// The duration of the episode. + internal let duration: TimeInterval? + + /// The image associated with the episode. + internal let image: iTunesImage? + + /// The enclosure of the episode. + internal let enclosure: Enclosure + + /// The people involved in the episode. + internal let people: [PodcastPerson] + + /// A struct representing an Atom category. + /// Initializes a ``PodcastEpisodeProperties`` instance from an ``RSSItem``. + /// + /// - Parameter rssItem: The ``RSSItem`` to extract the properties from. + /// + /// - Returns: An initialized ``PodcastEpisodeProperties`` instance, + /// or ``nil`` if the ``enclosure`` property is missing. + /// - SeeAlso: ``EntryCategory`` + internal init?(rssItem: RSSItem) { guard let enclosure = rssItem.enclosure else { return nil } @@ -29,15 +57,35 @@ public struct PodcastEpisodeProperties: PodcastEpisode { } } +/// A protocol representing a podcast episode. public protocol PodcastEpisode { + /// The title of the episode. var title: String? { get } + + /// The episode number. var episode: Int? { get } + + /// The author of the episode. var author: String? { get } + + /// The subtitle of the episode. var subtitle: String? { get } + + /// A summary of the episode. var summary: String? { get } + + /// Indicates if the episode contains explicit content. var explicit: String? { get } + + /// The duration of the episode. var duration: TimeInterval? { get } + + /// The image associated with the episode. var image: iTunesImage? { get } + + /// The enclosure of the episode. var enclosure: Enclosure { get } + + /// The people involved in the episode. var people: [PodcastPerson] { get } } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift index 61dd52b..ab8d23a 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastFunding.swift @@ -1,11 +1,16 @@ import Foundation +/// A struct representing funding information for a podcast. public struct PodcastFunding: Codable, Equatable { + /// The coding keys used for encoding and decoding. public enum CodingKeys: String, CodingKey { case url case description = "" } + /// The URL for the funding source. public let url: URL + + /// A description of the funding source. public let description: String? } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift index 9e1e235..6acfe6d 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift @@ -1,12 +1,21 @@ import Foundation extension PodcastLocation { + /// A ``struct`` representing a geographic URI for a podcast location. public struct GeoURI: Codable, Equatable, LosslessStringConvertible { + /// The latitude coordinate. public let latitude: Double + + /// The longitude coordinate. public let longitude: Double + + /// The altitude coordinate, if available. public let altitude: Double? + + /// The accuracy of the coordinates, if available. public let accuracy: Double? + /// A string representation of the geographic URI. public var description: String { var description = "geo:\(latitude),\(longitude)" @@ -21,6 +30,13 @@ extension PodcastLocation { return description } + /// Initializes a ``GeoURI`` instance with the specified coordinates. + /// + /// - Parameters: + /// - latitude: The latitude coordinate. + /// - longitude: The longitude coordinate. + /// - altitude: The altitude coordinate, if available. + /// - accuracy: The accuracy of the coordinates, if available. public init( latitude: Double, longitude: Double, @@ -33,11 +49,18 @@ extension PodcastLocation { self.accuracy = accuracy } + /// Initializes a ``GeoURI`` instance from a string representation. + /// + /// - Parameter description: The string representation of the geographic URI. public init?(_ description: String) { try? self.init(singleValue: description) } - // swiftlint:disable:next function_body_length + // swiftlint:disable function_body_length + /// Initializes a ``GeoURI`` instance from a single value string. + /// + /// - Parameter singleValue: The single value string representing the geographic URI. + /// - Throws: A ``DecodingError`` if the single value string is invalid. public init(singleValue: String) throws { let pathComponents = try Self.pathComponents(from: singleValue) @@ -66,6 +89,12 @@ extension PodcastLocation { ) } + // swiftlint:enable function_body_length + + /// Initializes a ``GeoURI`` instance from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: A ``DecodingError`` if the decoding process fails. public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let singleValue = try container.decode(String.self) @@ -93,6 +122,10 @@ extension PodcastLocation { return geoPath.split(separator: ";") } + /// Encodes the ``GeoURI`` instance into the given encoder. + /// + /// - Parameter encoder: The encoder to write data to. + /// - Throws: An error if the encoding process fails. public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(description) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift index 77abcc8..4bb03e9 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+OsmQuery.swift @@ -1,18 +1,31 @@ import Foundation extension PodcastLocation { + /// Represents a query for OpenStreetMap (OSM) data. public struct OsmQuery: Codable, Equatable { - // swiftlint:disable:next nesting + // swiftlint:disable nesting + /// The type of OSM element. public enum OsmType: String, Codable, CaseIterable { case node = "N" case way = "W" case relation = "R" } + // swiftlint:enable nesting + + /// The ID of the OSM element. public let id: Int + + /// The type of the OSM element. public let type: OsmType + + /// The revision number of the OSM element. public let revision: Int? + /// Initializes an ``OsmQuery`` instance from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: `DecodingError.dataCorrupted` if the data is invalid. public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift index 4517282..1513654 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation.swift @@ -1,6 +1,8 @@ import Foundation +/// A struct representing the location of a podcast. public struct PodcastLocation: Codable, Equatable { + /// The geographic coordinates of the location. internal enum CodingKeys: String, CodingKey { case geo case osmQuery = "osm" @@ -8,8 +10,12 @@ public struct PodcastLocation: Codable, Equatable { case name = "" } + /// The geographic coordinates of the location. public let geo: GeoURI? + + /// The OpenStreetMap query for the location. public let osmQuery: OsmQuery? + /// The name of the location. public let name: String } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift index c62cdf3..6a80f65 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastLocked.swift @@ -1,14 +1,23 @@ import Foundation +/// A struct representing a locked podcast. public struct PodcastLocked: Codable, Equatable { + /// Coding keys for encoding and decoding. public enum CodingKeys: String, CodingKey { case owner case isLocked = "" } + /// The owner of the podcast. public let owner: String? + + /// Indicates whether the podcast is locked. public let isLocked: Bool + /// Initializes a new instance of ``PodcastLocked`` from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if the decoding process fails. public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) owner = try container.decodeIfPresent(String.self, forKey: .owner) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift index f292a2e..29cb86e 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson+Role.swift @@ -1,6 +1,7 @@ import Foundation extension PodcastPerson { + /// A private enum representing known roles for a podcast person. private enum KnownRole: String { case guest case host @@ -10,11 +11,13 @@ extension PodcastPerson { case composer case producer + /// Initializes a ``KnownRole`` with a case-insensitive string. init?(caseInsensitive: String) { self.init(rawValue: caseInsensitive.lowercased()) } - // swiftlint:disable:next function_body_length cyclomatic_complexity + // swiftlint:disable function_body_length cyclomatic_complexity + /// Initializes a ``KnownRole`` with a ``Role`` value. init?(role: Role) { switch role { case .guest: @@ -44,6 +47,9 @@ extension PodcastPerson { } } + // swiftlint:enable function_body_length cyclomatic_complexity + + /// An enum representing the role of a podcast person. public enum Role: Codable, Equatable, RawRepresentable { case guest case host @@ -54,6 +60,7 @@ extension PodcastPerson { case producer case unknown(String) + /// The raw value of the role. public var rawValue: String { if let knownRole = KnownRole(role: self) { return knownRole.rawValue @@ -62,15 +69,17 @@ extension PodcastPerson { } else { fatalError( // swiftlint:disable:next line_length - "Role attribute of <podcast:person> with value: \(self) should either be a `KnownRole`, or unknown!" + "Role attribute of <podcast:person> with value: \(self) should either be a ``KnownRole``, or unknown!" ) } } + /// Initializes a ``Role`` with a raw value. public init?(rawValue: String) { self.init(caseInsensitive: rawValue) } + /// Initializes a ``Role`` with a case-insensitive string. public init(caseInsensitive: String) { if let knownRole = KnownRole(caseInsensitive: caseInsensitive) { self = .init(knownRole: knownRole) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift index dcdf454..9b1c795 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastPerson.swift @@ -1,6 +1,8 @@ import Foundation +/// A struct representing a person associated with a podcast. public struct PodcastPerson: Codable, Equatable { + /// The role of the person. public enum CodingKeys: String, CodingKey { case role case group @@ -9,13 +11,26 @@ public struct PodcastPerson: Codable, Equatable { case fullname = "" } + /// The role of the person. public let role: Role? + + /// The group the person belongs to. public let group: String? + + /// The URL associated with the person. public let href: URL? + + /// The URL of the person's image. public let img: URL? + /// The full name of the person. public let fullname: String + /// Initializes a new instance of ``PodcastPerson`` + /// by decoding data from the given decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if the decoding process fails. public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) role = try container.decodeIfPresent(Role.self, forKey: .role) diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift index 226bb36..962ac4f 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSeason.swift @@ -1,11 +1,16 @@ import Foundation +/// A struct representing a season of a podcast. public struct PodcastSeason: Codable, Equatable { + /// The coding keys for the ``PodcastSeason`` struct. public enum CodingKeys: String, CodingKey { case name case number = "" } + /// The name of the season. public let name: String? + + /// The number of the season. public let number: Int } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift index 02f709f..14740d3 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastSoundbite.swift @@ -1,6 +1,8 @@ import Foundation +/// A struct representing a soundbite from a podcast. public struct PodcastSoundbite: Codable, Equatable { + /// The coding keys used for encoding and decoding. public enum CodingKeys: String, CodingKey { case startTime case duration @@ -8,8 +10,12 @@ public struct PodcastSoundbite: Codable, Equatable { case title = "" } + /// The start time of the soundbite. public let startTime: TimeInterval + + /// The duration of the soundbite. public let duration: TimeInterval + /// The title of the soundbite. public let title: String? } diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift index 5f3d6e2..0cfb4bd 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript+MimeType.swift @@ -1,6 +1,7 @@ import Foundation extension PodcastTranscript { + /// A private enum representing known MIME types for the transcript. private enum KnownMimeType: String, Codable { case plain = "text/plain" case html = "text/html" @@ -9,11 +10,13 @@ extension PodcastTranscript { case json = "application/json" case subrip = "application/x-subrip" + /// Initializes a ``KnownMimeType`` with a case-insensitive string. init?(caseInsensitive: String) { self.init(rawValue: caseInsensitive) } - // swiftlint:disable:next cyclomatic_complexity + // swiftlint:disable cyclomatic_complexity + /// Initializes a ``KnownMimeType`` with a ``MimeType``. init?(mimeType: MimeType) { switch mimeType { case .plain: @@ -38,8 +41,11 @@ extension PodcastTranscript { return nil } } + + // swiftlint:enable cyclomatic_complexity } + /// An enum representing the MIME type of the transcript. public enum MimeType: Codable, Equatable, RawRepresentable { case plain case html @@ -49,6 +55,7 @@ extension PodcastTranscript { case subrip case unknown(String) + /// The raw value of the MIME type. public var rawValue: String { if let knownMimeType = KnownMimeType(mimeType: self) { return knownMimeType.rawValue @@ -57,15 +64,17 @@ extension PodcastTranscript { } else { fatalError( // swiftlint:disable:next line_length - "Type attribute of <podcast:transcript> with value: \(self) should either be a `KnownMimeType`, or unknown!" + "Type attribute of <podcast:transcript> with value: \(self) should either be a ``KnownMimeType``, or unknown!" ) } } + /// Initializes a ``MimeType`` with a raw value. public init?(rawValue: String) { self.init(caseInsensitive: rawValue) } + /// Initializes a ``MimeType`` with a case-insensitive string. public init(caseInsensitive: String) { if let knownMimeType = KnownMimeType(caseInsensitive: caseInsensitive) { self = .init(knownMimeType: knownMimeType) @@ -74,6 +83,7 @@ extension PodcastTranscript { } } + /// Initializes a ``MimeType`` with a ``KnownMimeType``. private init(knownMimeType: KnownMimeType) { switch knownMimeType { case .plain: diff --git a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift index 67f5eba..89bf654 100644 --- a/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift +++ b/Sources/SyndiKit/Formats/Media/Podcast/PodcastTranscript.swift @@ -1,6 +1,8 @@ import Foundation +/// A struct representing a podcast transcript. public struct PodcastTranscript: Codable, Equatable { + /// The coding keys for the podcast transcript. public enum CodingKeys: String, CodingKey { case url case type @@ -8,12 +10,20 @@ public struct PodcastTranscript: Codable, Equatable { case rel } + /// The relationship between the podcast transcript and the podcast. public enum Relationship: String, Codable { case captions } + /// The URL of the podcast transcript. public let url: URL + + /// The MIME type of the podcast transcript. public let type: MimeType + + /// The language of the podcast transcript. public let language: String? + + /// The relationship of the podcast transcript. public let rel: Relationship? } diff --git a/Sources/SyndiKit/Formats/Media/Video.swift b/Sources/SyndiKit/Formats/Media/Video.swift index 5882919..120bc6d 100644 --- a/Sources/SyndiKit/Formats/Media/Video.swift +++ b/Sources/SyndiKit/Formats/Media/Video.swift @@ -1,3 +1,9 @@ +/// A struct representing an Atom category. +/// An enumeration representing different types of videos. +/// - SeeAlso: ``EntryCategory`` public enum Video { + /// A video from YouTube. + /// - Parameters: + /// - id: The ID of the YouTube video. case youtube(YouTubeID) } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift index ae4bf37..54078e1 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPCategory.swift @@ -1,10 +1,13 @@ import Foundation +/// A typealias for the `WordPressElements.Category` type. public typealias WPCategory = WordPressElements.Category // swiftlint:disable nesting extension WordPressElements { + /// A struct representing a category in WordPress. public struct Category: Codable { + /// The coding keys for the ``Category`` struct. internal enum CodingKeys: String, CodingKey { case termID = "wp:termId" case niceName = "wp:categoryNicename" @@ -12,10 +15,27 @@ extension WordPressElements { case name = "wp:catName" } + /// The unique identifier of the category. public let termID: Int + + /// The nice name of the category. public let niceName: CData + + /// The parent category of the category. public let parent: CData + + /// The name of the category. public let name: String + + /// A struct representing an Atom category. + /// Initializes a new ``Category`` instance. + /// + /// - Parameters: + /// - termID: The unique identifier of the category. + /// - niceName: The nice name of the category. + /// - parent: The parent category of the category. + /// - name: The name of the category. + /// - SeeAlso: ``EntryCategory`` public init(termID: Int, niceName: CData, parent: CData, name: String) { self.termID = termID self.niceName = niceName @@ -26,6 +46,7 @@ extension WordPressElements { } extension WordPressElements.Category: Equatable { + /// Checks if two ``Category`` instances are equal. public static func == ( lhs: WordPressElements.Category, rhs: WordPressElements.Category diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift index f3537b3..9d87606 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPPostMeta.swift @@ -1,18 +1,31 @@ import Foundation +/// A typealias for `WordPressElements.Category`. public typealias WPPostMeta = WordPressElements.Category // swiftlint:disable nesting extension WordPressElements { + /// A struct representing metadata for a WordPress post. public struct PostMeta: Codable { + /// The coding keys for encoding and decoding. internal enum CodingKeys: String, CodingKey { case key = "wp:metaKey" case value = "wp:metaValue" } + /// The key of the metadata. public let key: CData + + /// The value of the metadata. public let value: CData + /// A struct representing an Atom category. + /// Initializes a new ``PostMeta`` instance. + /// + /// - Parameters: + /// - key: The key of the metadata. + /// - value: The value of the metadata. + /// - SeeAlso: ``EntryCategory`` public init(key: String, value: String) { self.key = .init(stringLiteral: key) self.value = .init(stringLiteral: value) @@ -21,6 +34,15 @@ extension WordPressElements { } extension WordPressElements.PostMeta: Equatable { + /// A struct representing an Atom category. + /// Checks if two ``PostMeta`` instances are equal. + /// + /// - Parameters: + /// - lhs: The left-hand side ``PostMeta`` instance. + /// - rhs: The right-hand side ``PostMeta`` instance. + /// + /// - Returns: ``true`` if the two instances are equal, ``false`` otherwise. + /// - SeeAlso: ``EntryCategory`` public static func == ( lhs: WordPressElements.PostMeta, rhs: WordPressElements.PostMeta diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift index 1b82837..51122cb 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WPTag.swift @@ -1,20 +1,36 @@ import Foundation +/// A typealias for `WordPressElements.Tag` public typealias WPTag = WordPressElements.Tag // swiftlint:disable nesting extension WordPressElements { + /// A struct representing a tag in WordPress. public struct Tag: Codable { + /// The term ID of the tag. internal enum CodingKeys: String, CodingKey { case termID = "wp:termId" case slug = "wp:tagSlug" case name = "wp:tagName" } + /// The term ID of the tag. public let termID: Int + + /// The slug of the tag. public let slug: CData + + /// The name of the tag. public let name: CData + /// A struct representing an Atom category. + /// Initializes a new ``Tag`` instance. + /// + /// - Parameters: + /// - termID: The term ID of the tag. + /// - slug: The slug of the tag. + /// - name: The name of the tag. + /// - SeeAlso: ``EntryCategory`` public init(termID: Int, slug: CData, name: CData) { self.termID = termID self.slug = slug @@ -24,6 +40,7 @@ extension WordPressElements { } extension WordPressElements.Tag: Equatable { + /// Checks if two ``Tag`` instances are equal. public static func == (lhs: WordPressElements.Tag, rhs: WordPressElements.Tag) -> Bool { lhs.termID == rhs.termID && lhs.slug == rhs.slug diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift index d290a97..8409dd8 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost+RSSItem.swift @@ -1,7 +1,17 @@ import Foundation extension WordPressPost { - // swiftlint:disable:next cyclomatic_complexity function_body_length + // swiftlint:disable cyclomatic_complexity function_body_length + /// A struct representing an Atom category. + /// Initializes a ``WordPressPost`` instance from an ``RSSItem``. + /// + /// - Parameter item: The ``RSSItem`` to initialize from. + /// + /// - Throws: `WordPressError.missingField` if any required field is missing. + /// + /// - Note: This initializer is marked as ``public`` to allow external usage. + /// + /// - SeeAlso: ``EntryCategory`` public init(item: RSSItem) throws { guard let name = item.wpPostName else { throw WordPressError.missingField(.name) @@ -73,11 +83,13 @@ extension WordPressPost { self.pingStatus = pingStatus.value self.parentID = parentID self.menuOrder = menuOrder - ID = id + self.id = id self.isSticky = (isSticky != 0) self.postDate = postDate postDateGMT = item.wpPostDateGMT self.modifiedDate = modifiedDate attachmentURL = item.wpAttachmentURL } + + // swiftlint:enable cyclomatic_complexity function_body_length } diff --git a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift index f2929e0..830ed86 100644 --- a/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift +++ b/Sources/SyndiKit/Formats/Media/Wordpress/WordPressPost.swift @@ -1,17 +1,28 @@ import Foundation +/// A namespace for WordPress related elements. public enum WordPressElements {} +/// An error type representing a missing field in a WordPress post. public enum WordPressError: Error, Equatable { case missingField(WordPressPost.Field) } +/// A struct representing a WordPress post. public struct WordPressPost { + /// The type of the post. public typealias PostType = String + + /// The comment status of the post. public typealias CommentStatus = String + + /// The ping status of the post. public typealias PingStatus = String + + /// The status of the post. public typealias Status = String + /// An enum representing the fields of a WordPress post. public enum Field: Equatable { case name case title @@ -36,41 +47,85 @@ public struct WordPressPost { case modifiedDateGMT } + /// The name of the post. public let name: String + + /// The title of the post. public let title: String + + /// The type of the post. public let type: PostType + + /// The link of the post. public let link: URL + + /// The publication date of the post. public let pubDate: Date? + + /// The creator of the post. public let creator: String + + /// The body of the post. public let body: String + + /// The tags of the post. public let tags: [String] + + /// The categories of the post. public let categories: [String] + + /// The meta data of the post. public let meta: [String: String] + + /// The status of the post. public let status: Status + + /// The comment status of the post. public let commentStatus: CommentStatus + + /// The ping status of the post. public let pingStatus: PingStatus + + /// The parent ID of the post. public let parentID: Int? + + /// The menu order of the post. public let menuOrder: Int? - public let ID: Int + + /// The ID of the post. + public let id: Int + + /// A boolean indicating if the post is sticky. public let isSticky: Bool + + /// The post date of the post. public let postDate: Date + + /// The post date in GMT of the post. public let postDateGMT: Date? + + /// The modified date of the post. public let modifiedDate: Date + + /// The modified date in GMT of the post. public let modifiedDateGMT: Date? + + /// The attachment URL of the post. public let attachmentURL: URL? } extension WordPressPost: Hashable { public static func == (lhs: WordPressPost, rhs: WordPressPost) -> Bool { - lhs.ID == rhs.ID + lhs.id == rhs.id } public func hash(into hasher: inout Hasher) { - hasher.combine(ID) + hasher.combine(id) } } extension Entryable { + /// Returns a WordPress post if the entry is an RSS item. public var wpPost: WordPressPost? { guard let rssItem = self as? RSSItem else { return nil diff --git a/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift b/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift index b36ec27..43d65e0 100644 --- a/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift +++ b/Sources/SyndiKit/Formats/Media/YouTube/YouTubeID.swift @@ -1,8 +1,29 @@ +/// A struct representing an Atom category. +/// A struct representing the properties of a YouTube ID. +/// +/// - Note: This struct conforms to the ``YouTubeID`` protocol. +/// +/// - SeeAlso: ``YouTubeID`` +/// +/// - Important: This struct is internal. +/// +/// - Parameters: +/// - videoID: The YouTube video ID. +/// - channelID: The YouTube channel ID. +/// - SeeAlso: ``EntryCategory`` internal struct YouTubeIDProperties: YouTubeID { internal let videoID: String - internal let channelID: String + /// A struct representing an Atom category. + /// Initializes a ``YouTubeIDProperties`` instance with the given AtomEntry. + /// + /// - Parameters: + /// - entry: The AtomEntry containing the YouTube ID properties. + /// + /// - Returns: A new ``YouTubeIDProperties`` instance, + /// or ``nil`` if the required properties are missing. + /// - SeeAlso: ``EntryCategory`` internal init?(entry: AtomEntry) { guard let channelID = entry.youtubeChannelID, @@ -14,15 +35,21 @@ internal struct YouTubeIDProperties: YouTubeID { } } -/// Specific type abstracting the id properties a YouTube RSS Feed. -/// ```xml -/// <yt:videoId>3hccNoPE59U</yt:videoId> -/// <yt:channelId>UCv75sKQFFIenWHrprnrR9aA</yt:channelId> -/// ``` +/// A struct representing an Atom category. +/// A protocol abstracting the ID properties of a YouTube RSS Feed. +/// +/// - Note: This protocol is public. +/// +/// - SeeAlso: ``YouTubeIDProperties`` +/// +/// - Important: This protocol is specific to YouTube. +/// +/// - Requires: Conforming types must provide a ``videoID`` and a ``channelID``. +/// - SeeAlso: ``EntryCategory`` public protocol YouTubeID { - /// YouTube video ID. + /// The YouTube video ID. var videoID: String { get } - /// YouTube channel ID. + /// The YouTube channel ID. var channelID: String { get } } diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift index 98dc513..47aa2f6 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesDuration.swift @@ -1,14 +1,25 @@ import Foundation -// swiftlint:disable:next type_name + +/// A struct representing the duration of an iTunes track. public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { + /// The type used to represent a floating-point literal. public typealias FloatLiteralType = TimeInterval + /// The value of the duration in seconds. public let value: TimeInterval + /// Creates a new instance with the specified floating-point literal value. + /// + /// - Parameter value: The value of the duration in seconds. public init(floatLiteral value: TimeInterval) { self.value = value } + /// Creates a new instance by decoding from the given decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if reading from the decoder fails, + /// or if the data is corrupted or invalid. public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let stringValue = try container.decode(String.self) @@ -23,6 +34,10 @@ public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { self.value = value } + /// Creates a new instance from the given description string. + /// + /// - Parameter description: The description string representing the duration. + /// - Returns: A new instance if the description is valid, otherwise ``nil``. public init?(_ description: String) { guard let value = Self.timeInterval(description) else { return nil @@ -30,6 +45,11 @@ public struct iTunesDuration: Codable, ExpressibleByFloatLiteral { self.value = value } + /// Converts a time string to a ``TimeInterval`` value. + /// + /// - Parameter timeString: The time string to convert. + /// - Returns: The ``TimeInterval`` value representing the time string, + /// or ``nil`` if the string is invalid. internal static func timeInterval(_ timeString: String) -> TimeInterval? { let timeStrings = timeString.components(separatedBy: ":").prefix(3) let doubles = timeStrings.compactMap(Double.init) diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesEpisode.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesEpisode.swift index 3e54ed2..ac8ff5e 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesEpisode.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesEpisode.swift @@ -1,2 +1,8 @@ -// swiftlint:disable:next type_name +/// A struct representing an Atom category. +/// A type alias for an iTunes episode. +/// +/// - Note: This type is an alias for ``XMLStringInt``. +/// +/// - SeeAlso: ``XMLStringInt`` +/// - SeeAlso: ``EntryCategory`` public typealias iTunesEpisode = XMLStringInt diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesImage.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesImage.swift index a4fac9f..0f34f66 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesImage.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesImage.swift @@ -1,3 +1,4 @@ import Foundation -// swiftlint:disable:next type_name + +/// A type alias for iTunes image links. public typealias iTunesImage = Link diff --git a/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift b/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift index b5e3383..3cfa2f3 100644 --- a/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift +++ b/Sources/SyndiKit/Formats/Media/iTunes/iTunesOwner.swift @@ -1,10 +1,28 @@ -// swiftlint:disable:next type_name +/// A struct representing an Atom category. +/// A struct representing the owner of an iTunes account. +/// +/// - Note: This struct conforms to the ``Codable`` protocol. +/// +/// - Warning: Do not modify the ``CodingKeys`` enum. +/// +/// - SeeAlso: ``CodingKeys`` +/// +/// - Remark: The ``email`` property is optional. +/// +/// - Important: The ``name`` property is required. +/// +/// - Version: 1.0 +/// - SeeAlso: ``EntryCategory`` public struct iTunesOwner: Codable { + /// The coding keys used to encode and decode the struct. internal enum CodingKeys: String, CodingKey { case name = "itunes:name" case email = "itunes:email" } + /// The name of the iTunes owner. public let name: String + + /// The email address of the iTunes owner. public let email: String? } diff --git a/Sources/SyndiKit/SyndiKit.docc/SyndiKit.md b/Sources/SyndiKit/SyndiKit.docc/SyndiKit.md index e2c3281..8db5229 100644 --- a/Sources/SyndiKit/SyndiKit.docc/SyndiKit.md +++ b/Sources/SyndiKit/SyndiKit.docc/SyndiKit.md @@ -6,7 +6,7 @@ Swift Package for Decoding RSS Feeds. ![SyndiKit Logo](logo.png) -Built on top of [XMLCoder](https://github.com/CoreOffice/XMLCoder) and , **SyndiKit** provides models and utilities for decoding RSS feeds of various formats and extensions. +Built on top of [XMLCoder](https://github.com/CoreOffice/XMLCoder), **SyndiKit** provides models and utilities for decoding RSS feeds of various formats and extensions. ### Features @@ -251,6 +251,10 @@ Specific extension properties provided by WordPress. - ``WordPressElements`` - ``WordPressPost`` +- ``WPTag`` +- ``WPCategory`` +- ``WPPostMeta`` +- ``WordPressError`` ### YouTube Extensions diff --git a/Tests/SyndiKitTests/WordpressTests.swift b/Tests/SyndiKitTests/WordpressTests.swift index 3682da0..7fe364f 100644 --- a/Tests/SyndiKitTests/WordpressTests.swift +++ b/Tests/SyndiKitTests/WordpressTests.swift @@ -143,7 +143,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -229,7 +229,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName) @@ -312,7 +312,7 @@ final class WordpressTests: XCTestCase { XCTAssertEqual(post.parentID, wpPostParent) XCTAssertEqual(post.menuOrder, wpMenuOrder) XCTAssertEqual(post.isSticky, wpIsSticky != 0) - XCTAssertEqual(post.ID, wpPostID) + XCTAssertEqual(post.id, wpPostID) XCTAssertEqual(post.postDate, wpPostDate) XCTAssertEqual(post.modifiedDate, wpModifiedDate) XCTAssertEqual(post.name, wpPostName)