badpod 0.9.1

A Rust crate for working with imperfect feeds of podcasts.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
use badpod::*;
use chrono::prelude::*;

#[test]
fn deserialize() {
    let conditions : &[(&str, Result<Rss, badpod::Error>)] = &[
        (
            include_str!("data/empty_rss.xml"),
            Ok(Rss{
                ..Default::default()
            })
        ),
        (
            include_str!("data/empty_xml.xml"),
            Err(Error::NoRoot)
        ),
        (
            include_str!("data/empty_file.xml"),
            Err(Error::NoRoot)
        ),
        (
            include_str!("data/root_not_rss.xml"),
            Err(Error::RootNotRss)
        ),
        (
            include_str!("data/strange_feed.xml"),
            Ok(Rss{
                channel: vec![Channel{
                    itunes_category: vec![itunes::Category{
                        ..Default::default()
                    }],
                    ..Default::default()
                }],
                ..Default::default()
            })
        ),
        (
            include_str!("data/simple_feed.xml"),
            Ok(Rss{
                version: Some("2.0".to_string()),
                channel: vec![Channel{
                    description: vec!["My description".to_string()],
                    title: vec!["My title".to_string()],
                    ..Default::default()
                }],
            })
        ),
        (
            include_str!("data/test_feed.xml"),
            Ok(Rss {
                version: Some("2.0".to_string()),
                channel: vec![Channel {
                    copyright: vec!["© Example Company".to_string()],
                    description: vec!["<p><strong>Example HTML description</strong></p>".to_string()],
                    language: vec![Language::Lithuanian],
                    link: vec![Url::Ok(url::Url::parse("https://example.com").unwrap())],
                    title: vec!["Example Podcast".to_string()],
                    content_encoded: vec!["<p><strong>Example HTML description</strong></p>".to_string()
                    ],
                    itunes_author: vec!["Jane Doe".to_string()],
                    itunes_block: vec![itunes::Yes::Ok],
                    itunes_complete: vec![itunes::Yes::Other(("No".to_string(), "should be \"Yes\"".to_string()))],
                    itunes_category: vec! {
                        itunes::Category{
                            text: Some(itunes::CategoryName::Music),
                            subcategory: vec![itunes::Subcategory{
                                text: Some(itunes::SubcategoryName::Other(
                                              (
                                              "Entertainment News".to_string(),
                                              "subcategory \"Entertainment News\" is not allowed for category \"Music\"; valid subcategories: \"Music Commentary\", \"Music History\", \"Music Interviews\"".to_string(),
                                              ),
                                              )),
                            }],
                        },
                        itunes::Category{
                            text: Some(itunes::CategoryName::Technology),
                            subcategory: vec![],
                        },
                    },
                    itunes_explicit: vec![Bool::Ok(false)],
                    itunes_owner: vec![itunes::Owner {
                        email: vec!["jane@example.com".to_string()],
                        name: vec!["Jane Doe".to_string()],
                    }],
                    itunes_type: vec![itunes::PodcastType::Serial],
                    podcast_locked: vec![podcast::Locked {
                        owner: None,
                        value: Some(Bool::Ok(false)),
                    }],
                    podcast_funding: vec![
                        podcast::Funding{
                            url: Some(Url::Ok(url::Url::parse("https://www.example.com/donations").unwrap())),
                            value: Some("Support the show!".to_string()),
                        },
                        podcast::Funding{
                            url: Some(Url::Ok(url::Url::parse("https://www.example.com/members").unwrap())),
                            value: Some("Become a member!".to_string()),
                        },
                    ],
                    podcast_person: vec! {
                        podcast::Person{
                            href: Some(Url::Ok(url::Url::parse("https://example.com/johnsmith/blog").unwrap())),
                            img: Some(Url::Other(("http://example.com/images/johnsmith.jpg".to_string(), "protocol must be `https`".to_string()))),
                            value: Some("John Smith".to_string()),
                            ..Default::default()
                        },
                        podcast::Person{
                            role: Some(podcast::PersonRole::Guest),
                            href: Some(Url::Ok(url::Url::parse("https://www.imdb.com/name/nm0427852888/").unwrap())),
                            img: Some(Url::Other(("http://example.com/images/janedoe.jpg".to_string(), "protocol must be `https`".to_string()))),
                            value: Some("Jane Doe".to_string()),
                            ..Default::default()
                        },
                    },
                    podcast_block: vec! {
                        podcast::Block{
                            id: None,
                            value: Some(Bool::Ok(true)),
                        },
                        podcast::Block{
                            id: Some(podcast::Service::YouTube),
                            value: Some(Bool::Ok(false)),
                        },
                        podcast::Block{
                            id: Some(podcast::Service::Amazon),
                            value: Some(Bool::Ok(false)),
                        },
                    },
                    podcast_location: vec![podcast::Location {
                        geo: Some(podcast::Geo::Ok{
                            latitude: 33.51601,
                            longitude: -86.81455,
                            altitude: None,
                            uncertainty: None
                        }),
                        osm: Some(podcast::Osm::Ok{
                            type_: podcast::OsmType::Relation,
                            id: 6930627,
                            revision: None,
                        }),
                        value: Some("Birmingham Civil Rights Museum".to_string()),
                    }],
                    podcast_trailer: vec! {
                        podcast::Trailer{
                            pub_date: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(5*60*60).unwrap().with_ymd_and_hms(2021, 4, 1, 8, 0, 0).unwrap())),
                            url: Some(Url::Ok(url::Url::parse("https://example.org/trailers/teaser").unwrap())),
                            length: Some(Integer::Ok(12345678)),
                            type_: Some(MimeEnclosure::AudioMp3),
                            season: None,
                            value: Some("Coming April 1st, 2021".to_string()),
                        },
                    },
                    podcast_license: vec![podcast::License {
                        url: None,
                        value: Some(podcast::LicenseType::CreativeCommonsAttribution4_0International),
                    }],
                    podcast_guid: vec![podcast::Guid::Ok(
                                      "917393e3-1b1e-5cef-ace4-edaa54e1f810".to_string()
                                      )
                    ],
                    podcast_value: vec![podcast::Value {
                        type_: Some(podcast::ValueType::Lightning),
                        method: Some(podcast::ValueMethod::Keysend),
                        suggested: Some(Float::Ok(0.00000015)),
                        value_recipient: vec! {
                            podcast::ValueRecipient{
                                name: Some("Host".to_string()),
                                type_: Some(podcast::ValueRecipientType::Node),
                                address: Some("032f4ffbbafffbe51726ad3c164a3d0d37ec27bc67b29a159b0f49ae8ac21b8508".to_string()),
                                split: Some(Integer::Ok(40)),
                                ..Default::default()
                            },
                            podcast::ValueRecipient{
                                name: Some("Producer".to_string()),
                                type_: Some(podcast::ValueRecipientType::Node),
                                address: Some("03ae9f91a0cb8ff43840e3c322c4c61f019d8c1c3cea15a25cfc425ac605e61a4a".to_string()),
                                split: Some(Integer::Ok(10)),
                                ..Default::default()
                            },
                        },
                        value_time_split: vec![],
                    }],
                    podcast_medium: vec![podcast::Medium::Music],
                    podcast_images: vec![podcast::Images {
                        srcset: podcast::ImageSrcSet::Other(("https://example.com/images/ep1/pci_avatar-massive.jpg 1500w,       https://example.com/images/ep1/pci_avatar-middle.jpg 600w,       https://example.com/images/ep1/pci_avatar-small.jpg -300w".to_string(), "invalid image at index 2: invalid width (should be positive)".to_string())),
                    }],
                    item: vec! {
                        Item{
                            title: vec!["Example Episode".to_string()],
                            enclosure: vec![Enclosure{
                                url: Some(Url::Ok(url::Url::parse("http://example.com/episode-1.mp3").unwrap())),
                                length: Some(Integer::Ok(100200)),
                                type_: Some(MimeEnclosure::AudioMp3),
                            }],
                            itunes_duration: vec![
                                itunes::Duration::Duration(chrono::Duration::seconds(1079)),
                                itunes::Duration::Duration(chrono::Duration::minutes(13) + chrono::Duration::seconds(24)),
                            ],
                            itunes_explicit: vec![Bool::Ok(true)],
                            pub_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2022, 10, 10, 6, 10, 5).unwrap())],

                            podcast_chapters: vec![podcast::Chapters{
                                url: Some(Url::Ok(url::Url::parse("https://example.com/episode-1/chapters.json").unwrap())),
                                type_: Some(MimeChapters::ApplicationJsonChapters),
                            }],
                            podcast_soundbite: vec! {
                                podcast::Soundbite{
                                    start_time: Some(Float::Ok(73.0)),
                                    duration: Some(Float::Ok(60.0)),
                                    value: None,
                                },
                                podcast::Soundbite{
                                    start_time: Some(Float::Ok(1234.5)),
                                    duration: Some(Float::Other(("-42.25".to_string(), "should be positive".to_string()))),
                                    value: Some("Why the Podcast Namespace Matters".to_string()),
                                },
                            },
                            itunes_type: vec![itunes::EpisodeType::Full],
                            podcast_person: vec! {
                                podcast::Person{
                                    role: Some(podcast::PersonRole::Guest),
                                    href: Some(Url::Ok(url::Url::parse("https://www.wikipedia/alicebrown").unwrap())),
                                    img: Some(Url::Other(("http://example.com/images/alicebrown.jpg".to_string(), "protocol must be `https`".to_string()))),
                                    value: Some("Alice Brown".to_string()),
                                    ..Default::default()
                                },
                                podcast::Person{
                                    group: Some(podcast::PersonGroup::Writing),
                                    role: Some(podcast::PersonRole::Guest),
                                    href: Some(Url::Ok(url::Url::parse("https://www.wikipedia/alicebrown").unwrap())),
                                    img: Some(Url::Other(("http://example.com/images/alicebrown.jpg".to_string(), "protocol must be `https`".to_string()))),
                                    value: Some("Alice Brown".to_string()),
                                },
                                podcast::Person{
                                    group: Some(podcast::PersonGroup::Other(("non-existent group".to_string(), "should be one of the groups defined at <https://podcasttaxonomy.com>".to_string()))),
                                    role: Some(podcast::PersonRole::Other(("Non-existent role".to_string(), "should be one of the roles defined at <https://podcasttaxonomy.com>".to_string()))),
                                    href: Some(Url::Ok(url::Url::parse("https://example.com/artist/beckysmith").unwrap())),
                                    value: Some("Becky Smith".to_string()),
                                    ..Default::default()
                                },
                            },
                            podcast_location: vec![podcast::Location {
                                geo: Some(podcast::Geo::Other(("GEO:-27.86159,153.3169".to_string(), "should start with \"geo:\"".to_string()))),
                                osm: Some(podcast::Osm::Ok{
                                    type_: podcast::OsmType::Way,
                                    id: 43678282,
                                    revision: None,
                                }),
                                value: Some("Dreamworld (Queensland)".to_string()),
                            }],
                            podcast_season: vec![podcast::Season{
                                name: Some("Egyptology: The 19th Century".to_string()),
                                value: Some(Integer::Ok(1)),
                            }],
                            podcast_episode: vec![podcast::Episode{
                                display: Some("Ch.3".to_string()),
                                value: Some(Number::Integer(204)),
                            }],
                            itunes_episode: vec![Integer::Ok(204)],
                            itunes_season: vec![
                                Integer::Other(("0".to_string(), "should be positive".to_string())),
                            ],
                            podcast_transcript: vec! {
                                podcast::Transcript{
                                    url: Some(Url::Ok(url::Url::parse("https://example.com/episode1/transcript.json").unwrap())),
                                    type_: Some(MimeTranscript::ApplicationJson),
                                    language: Some(Language::Spanish(LanguageSpanish::Default)),
                                    rel: Some(podcast::TranscriptRel::Captions),
                                },
                            },
                            itunes_block: vec![itunes::Yes::Other(("yes".to_string(), "should be \"Yes\"".to_string()))],
                            podcast_alternate_enclosure: vec!{
                                podcast::AlternateEnclosure{
                                    type_: Some(MimeEnclosure::AudioMp3),
                                    length: Some(Integer::Ok(2490970)),
                                    bit_rate: Some(Float::Ok(160707.74)),
                                    podcast_source: vec!{
                                        podcast::Source{
                                            uri: Some(Url::Ok(url::Url::parse("https://example.com/file-0.mp3").unwrap())),
                                            type_: None,
                                        },
                                        podcast::Source{
                                            uri: Some(Url::Ok(url::Url::parse("ipfs://QmdwGqd3d2gFPGeJNLLCshdiPert45fMu84552Y4XHTy4y").unwrap())),
                                            type_: None,
                                        },
                                        podcast::Source{
                                            uri: Some(Url::Ok(url::Url::parse("https://example.com/file-0.torrent").unwrap())),
                                            type_: Some(MimeEnclosure::Other(("application/x-bittorrent".to_string(), "unrecognized mime type".to_string()))),
                                        },
                                        podcast::Source{
                                            uri: Some(Url::Other(("http://example.onion/file-0.mp3".to_string(), "protocol must not be `http`".to_string()))),
                                            type_: None,
                                        },
                                    },
                                    ..Default::default()
                                },
                                podcast::AlternateEnclosure{
                                    type_: Some(MimeEnclosure::VideoMp4),
                                    length: Some(Integer::Ok(10562995)),
                                    bit_rate: Some(Float::Ok(681483.55)),
                                    height: Some(Integer::Ok(1080)),
                                    podcast_source: vec!{
                                        podcast::Source{
                                            uri: Some(Url::Ok(url::Url::parse("https://example.com/file-1080.mp4").unwrap())),
                                            type_: None,
                                        },
                                    },
                                    ..Default::default()
                                },
                            },
                            podcast_value: vec![podcast::Value {
                                type_: Some(podcast::ValueType::Lightning),
                                method: Some(podcast::ValueMethod::Keysend),
                                suggested: Some(Float::Ok(0.00000015)),
                                value_recipient: vec!{},
                        value_time_split: vec![],
                            }],
                            podcast_social_interact: vec! {
                                podcast::SocialInteract{
                                    uri: Some(Url::Ok(url::Url::parse("https://podcastindex.social/web/@dave/108013847520053258").unwrap())),
                                    protocol: Some(podcast::SocialProtocol::ActivityPub),
                                    account_id: Some("@dave".to_string()),
                                    ..Default::default()
                                },
                            },
                            podcast_txt: vec![
                                podcast::Txt{
                                    purpose: None,
                                    value: Some("naj3eEZaWVVY9a38uhX8FekACyhtqP4JN".to_string()),
                                },
                                podcast::Txt{
                                    purpose: Some(podcast::TxtPurpose::Verify),
                                    value: Some("S6lpp-7ZCn8-dZfGc-OoyaG".to_string()),
                                },
                                podcast::Txt{
                                    purpose: Some(podcast::TxtPurpose::Other(("release".to_string(), "should be \"verify\"".to_string()))),
                                    value: Some("2022-10-26T04:45:30.742Z".to_string()),
                                },
                            ],
                            ..Default::default()
                        },
                        Item{
                            title: vec!["Episode minus 1".to_string()],
                            ..Default::default()
                        },
                        Item{
                            title: vec!["Episode minus 2".to_string()],
                            ..Default::default()
                        },
                    },
                    podcast_live_item: vec! {
                        podcast::LiveItem{
                            status: Some(podcast::LiveItemStatus::Live),
                            start: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(6*60*60).unwrap().with_ymd_and_hms(2021, 9, 26, 7, 30, 0).unwrap())),
                            end: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(6*60*60).unwrap().with_ymd_and_hms(2021, 9, 26, 9, 30, 0).unwrap())),
                            title: vec!["Podcasting 2.0 Live Stream".to_string()],
                            guid: vec![Guid{
                                is_permalink: None,
                                value: Some(GuidValue::Other(("e32b4890-983b-4ce5-8b46-f2d6bc1d8819".to_string(), "should be a URL when `isPermalink` is not set".to_string()))),
                            }],
                            enclosure: vec![Enclosure{
                                url: Some(Url::Ok(url::Url::parse("https://example.com/pc20/livestream?format=.mp3").unwrap())),
                                length: Some(Integer::Ok(312)),
                                type_: Some(MimeEnclosure::AudioMp3),
                            }],
                            podcast_content_link: vec!{
                                podcast::ContentLink{
                                    href: Some(Url::Ok(url::Url::parse("https://example.com/html/livestream").unwrap())),
                                    value: Some("Listen Live!".to_string()),
                                },
                            },
                            ..Default::default()
                        },
                    },
                    ..Default::default()
                }],
            })
    ),
    (
        include_str!("data/podcast_namespace_example.xml"),
        Ok(Rss{
            version: Some("2.0".to_string()),
            channel: vec![Channel{
                title: vec!["Podcasting 2.0 Namespace Example".to_string()],
                description: vec!["This is a fake show that exists only as an example of the \"podcast\" namespace tag usage.".to_string()],
                docs: vec![Url::Ok(url::Url::parse("http://blogs.law.harvard.edu/tech/rss").unwrap())],
                link: vec![Url::Ok(url::Url::parse("http://example.com/podcast").unwrap())],
                language: vec![Language::English(LanguageEnglish::UnitedStates)],
                generator: vec!["Freedom Controller".to_string()],
                managing_editor: vec!["john@example.com (John Doe)".to_string()],
                web_master: vec!["support@example.com (Tech Support)".to_string()],
                pub_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2020, 10, 9, 4, 30, 38).unwrap())],
                last_build_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2020, 10, 9, 4, 30, 38).unwrap())],

                podcast_guid: vec![podcast::Guid::Other(("y0ur-gu1d-g035-h3r3".to_string(), r#"should be a [UUIDv5](https://tools.rssblue.com/podcast-guid)"#.to_string()))],
                podcast_license: vec![podcast::License{
                    url: Some(Url::Ok(url::Url::parse("https://example.org/mypodcastlicense/full.pdf").unwrap())),
                    value: Some(podcast::LicenseType::Other(("my-podcast-license-v1".to_string(), "unrecognized license type".to_string()))),
                }],
                podcast_locked: vec![podcast::Locked{
                    owner: Some("podcastowner@example.com".to_string()),
                    value: Some(Bool::Ok(true)),
                }],
                podcast_block: vec! {
                    podcast::Block{
                        id: None,
                        value: Some(Bool::Ok(true)),
                    },
                    podcast::Block{
                        id: Some(podcast::Service::Google),
                        value: Some(Bool::Ok(false)),
                    },
                    podcast::Block{
                        id: Some(podcast::Service::Amazon),
                        value: Some(Bool::Ok(false)),
                    },
                },
                podcast_funding: vec! {
                    podcast::Funding{
                        url: Some(Url::Ok(url::Url::parse("https://example.com/donate").unwrap())),
                        value: Some("Support the show!".to_string()),
                    },
                },
                podcast_location: vec![podcast::Location{
                    geo: Some(podcast::Geo::Ok{
                        latitude: 30.2672,
                        longitude: 97.7431,
                        altitude: None,
                        uncertainty: None,
                    }),
                    osm: Some(podcast::Osm::Ok{
                        type_: podcast::OsmType::Relation,
                        id: 113314,
                        revision: None,
                    }),
                    value: Some("Austin, TX".to_string()),
                }],
                podcast_medium: vec![podcast::Medium::Podcast],
                podcast_remote_item: vec![
                    podcast::RemoteItem {
                        feed_guid: Some(podcast::Guid::Ok("ff519475-6e90-5231-91a0-37d092088d88".into())),
                        feed_url: Some(Url::Ok(url::Url::parse("https://media.rss.com/joemartinmusic/feed.xml").unwrap())),
                        item_guid: Some(GuidValue::Text("e75771b1-e8d4-4133-9392-c579822247d9".into())),
                        medium: Some(podcast::Medium::Music),
                    }
                ],
                podcast_value: vec![podcast::Value {
                    type_: Some(podcast::ValueType::Lightning),
                    method: Some(podcast::ValueMethod::Keysend),
                    suggested: Some(Float::Ok(0.00000005)),
                    value_recipient: vec!{
                        podcast::ValueRecipient{
                            name: Some("podcaster".to_string()),
                            type_: Some(podcast::ValueRecipientType::Node),
                            address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                            split: Some(Integer::Ok(99)),
                            ..Default::default()
                        },
                        podcast::ValueRecipient{
                            name: Some("hosting company".to_string()),
                            type_: Some(podcast::ValueRecipientType::Node),
                            address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                            split: Some(Integer::Ok(1)),
                            ..Default::default()
                        },
                    },
                    value_time_split: vec![
                        podcast::ValueTimeSplit{
                            start_time: Some(badpod::Duration::Duration(chrono::Duration::minutes(1))),
                            duration: Some(badpod::Duration::Duration(chrono::Duration::minutes(3) + chrono::Duration::seconds(57) + chrono::Duration::milliseconds(500))),
                            remote_start_time: None,
                            remote_percentage: Some(Integer::Ok(95)),
                            value_recipient: vec![],
                            remote_item: vec![podcast::RemoteItem{
                                feed_guid: Some(podcast::Guid::Ok("a94f5cc9-8c58-55fc-91fe-a324087a655b".into())),
                                feed_url: None,
                                item_guid: Some(GuidValue::Url(url::Url::parse("https://podcastindex.org/podcast/4148683#1").unwrap())),
                                medium: Some(podcast::Medium::Music),
                            }],
                        }
                    ],
                }],
                podcast_trailer: vec!{
                    podcast::Trailer{
                        url: Some(Url::Ok(url::Url::parse("https://example.org/trailers/teaser").unwrap())),
                        pub_date: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(5*60*60).unwrap().with_ymd_and_hms(2021, 4, 1, 8, 0, 0).unwrap())),
                        length: Some(Integer::Ok(12345678)),
                        type_: Some(MimeEnclosure::Other(("audio/mp3".to_string(), "unrecognized mime type".to_string()))),
                        value: Some("Coming April 1st, 2021".to_string()),
                        season: None,
                    },
                },
                podcast_live_item: vec!{
                    podcast::LiveItem{
                        status: Some(podcast::LiveItemStatus::Live),
                        start: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(6*60*60).unwrap().with_ymd_and_hms(2021, 9, 26, 7, 30, 0).unwrap())),
                        end: Some(badpod::DateTime::Ok(chrono::FixedOffset::west_opt(6*60*60).unwrap().with_ymd_and_hms(2021, 9, 26, 9, 30, 0).unwrap())),
                        title: vec!["Podcasting 2.0 Live Show".to_string()],
                        description: vec!["A look into the future of podcasting and how we get to Podcasting 2.0!".to_string()],
                        link: vec![Url::Ok(url::Url::parse("https://example.com/podcast/live").unwrap())],
                        guid: vec![Guid{
                            is_permalink: Some(Bool::Ok(true)),
                            value: Some(GuidValue::Url(url::Url::parse("https://example.com/live").unwrap())),
                        }],
                        podcast_alternate_enclosure: vec!{
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioMp3),
                                length: Some(Integer::Ok(312)),
                                default: Some(Bool::Ok(true)),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/pc20/livestream").unwrap())),
                                        type_: None,
                                    },
                                },
                                ..Default::default()
                            },
                        },
                        enclosure: vec![Enclosure {
                            url: Some(Url::Ok(url::Url::parse("https://example.com/pc20/livestream?format=.mp3").unwrap())),
                            type_: Some(MimeEnclosure::AudioMp3),
                            length: Some(Integer::Ok(312)),
                        }],
                        podcast_content_link: vec!{
                            podcast::ContentLink{
                                href: Some(Url::Ok(url::Url::parse("https://youtube.com/pc20/livestream").unwrap())),
                                value: Some("YouTube!".to_string()),
                            },
                            podcast::ContentLink{
                                href: Some(Url::Ok(url::Url::parse("https://twitch.com/pc20/livestream").unwrap())),
                                value: Some("Twitch!".to_string()),
                            },
                        },
                        podcast_live_value: vec! {
                            podcast::LiveValue {
                                uri: Some(Url::Ok(url::Url::parse("https://curiohoster.com/event?event_id=8a2cfa5a-7a4d-466a-9d32-362ad68f945e").unwrap())),
                                protocol: Some("socket.io".to_string()),
                            }
                        },
                        ..Default::default()
                    },
                },

                itunes_author: vec!["John Doe".to_string()],
                itunes_explicit: vec![Bool::Other(("no".to_string(), "should be \"true\" or \"false\"".to_string()))],
                itunes_type: vec![itunes::PodcastType::Episodic],
                itunes_category: vec!{
                    itunes::Category{
                        text: Some(itunes::CategoryName::News),
                        subcategory: vec![],
                    },
                    itunes::Category{
                        text: Some(itunes::CategoryName::Technology),
                        subcategory: vec![],
                    },
                },
                itunes_owner: vec![itunes::Owner{
                    email: vec!["johndoe@example.com".to_string()],
                    name: vec!["John Doe".to_string()],
                }],
                itunes_image: vec![itunes::Image{
                    href: None,
                }],

                item: vec!{
                    Item{
                        title: vec!["Episode 3 - The Future".to_string()],
                        description: vec!["<p>A look into the future of podcasting and how we get to Podcasting 2.0!</p>".to_string()],
                        link: vec![Url::Ok(url::Url::parse("https://example.com/podcast/ep0003").unwrap())],
                        guid: vec![Guid{
                            is_permalink: Some(Bool::Ok(true)),
                            value: Some(GuidValue::Url(url::Url::parse("https://example.com/ep0003").unwrap())),
                        }],
                        pub_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2020, 10, 9, 4, 30, 38).unwrap())],
                        enclosure: vec![Enclosure{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/file-03.mp3").unwrap())),
                            length: Some(Integer::Ok(43200000)),
                            type_: Some(MimeEnclosure::AudioMp3),
                        }],

                        itunes_image: vec![itunes::Image{
                            href: None,
                        }],
                        itunes_explicit: vec![Bool::Other(("no".to_string(), "should be \"true\" or \"false\"".to_string()))],

                        podcast_images: vec![podcast::Images {
                            srcset: podcast::ImageSrcSet::Ok(vec![
                                (url::Url::parse("https://example.com/images/ep3/pci_avatar-massive.jpg").unwrap(), 1500),
                                (url::Url::parse("https://example.com/images/ep3/pci_avatar-middle.jpg").unwrap(), 600),
                                (url::Url::parse("https://example.com/images/ep3/pci_avatar-small.jpg").unwrap(), 300),
                                (url::Url::parse("https://example.com/images/ep3/pci_avatar-tiny.jpg").unwrap(), 150),
                            ]),
                        }],
                        podcast_season: vec![podcast::Season{
                            name: Some("Podcasting 2.0".to_string()),
                            value: Some(Integer::Ok(1)),
                        }],
                        podcast_episode: vec![podcast::Episode{
                            display: None,
                            value: Some(Number::Integer(3)),
                        }],
                        podcast_chapters: vec![podcast::Chapters{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/ep3_chapters.json").unwrap())),
                            type_: Some(MimeChapters::ApplicationJson),
                        }],
                        podcast_soundbite: vec!{
                            podcast::Soundbite{
                                start_time: Some(Float::Ok(33.833)),
                                duration: Some(Float::Ok(60.0)),
                                value: None,
                            },
                        },
                        podcast_transcript: vec!{
                            podcast::Transcript{
                                url: Some(Url::Ok(url::Url::parse("https://example.com/ep3/transcript.txt").unwrap())),
                                type_: Some(MimeTranscript::TextPlain),
                                ..Default::default()
                            },
                        },
                        podcast_person: vec!{
                            podcast::Person{
                                href: Some(Url::Ok(url::Url::parse("https://www.podchaser.com/creators/adam-curry-107ZzmWE5f").unwrap())),
                                img: Some(Url::Ok(url::Url::parse("https://example.com/images/adamcurry.jpg").unwrap())),
                                value: Some("Adam Curry".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                role: Some(podcast::PersonRole::Guest),
                                href: Some(Url::Ok(url::Url::parse("https://github.com/daveajones/").unwrap())),
                                img: Some(Url::Ok(url::Url::parse("https://example.com/images/davejones.jpg").unwrap())),
                                value: Some("Dave Jones".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                group: Some(podcast::PersonGroup::Visuals),
                                role: Some(podcast::PersonRole::CoverArtDesigner),
                                href: Some(Url::Ok(url::Url::parse("https://example.com/artist/beckysmith").unwrap())),
                                value: Some("Becky Smith".to_string()),
                                ..Default::default()
                            },
                        },
                        podcast_alternate_enclosure: vec!{
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioMp3),
                                length: Some(Integer::Ok(43200000)),
                                bit_rate: Some(Float::Ok(128000.0)),
                                default: Some(Bool::Ok(true)),
                                title: Some("Standard".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-03.mp3").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomMpegFile03").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(32400000)),
                                bit_rate: Some(Float::Ok(96000.0)),
                                title: Some("High quality".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-high-03.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomHighBitrateOpusFile03").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioAac),
                                length: Some(Integer::Ok(54000000)),
                                bit_rate: Some(Float::Ok(160000.0)),
                                title: Some("High quality AAC".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-proprietary-03.aac").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomProprietaryAACFile03").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(5400000)),
                                bit_rate: Some(Float::Ok(16000.0)),
                                title: Some("Low bandwidth".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-low-03.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomLowBitrateOpusFile03").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::VideoMp4),
                                length: Some(Integer::Ok(7924786)),
                                bit_rate: Some(Float::Ok(511276.52)),
                                height: Some(Integer::Ok(720)),
                                title: Some("Video version".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-720.mp4").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://QmX33FYehk6ckGQ6g1D9D3FqZPix5JpKstKQKbaS8quUFb").unwrap())),
                                        type_: None,
                                    }
                                },
                                podcast_integrity: vec![podcast::Integrity{
                                    type_: Some(podcast::IntegrityType::Sri),
                                    value: Some("sha384-ExVqijgYHm15PqQqdXfW95x+Rs6C+d6E/ICxyQOeFevnxNLR/wtJNrNYTjIysUBo".to_string()),
                                }],
                                ..Default::default()
                            },
                        },
                        podcast_value: vec![podcast::Value {
                            type_: Some(podcast::ValueType::Lightning),
                            method: Some(podcast::ValueMethod::Keysend),
                            suggested: Some(Float::Ok(0.00000005)),
                            value_recipient: vec! {
                                podcast::ValueRecipient{
                                    name: Some("podcaster".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                                    split: Some(Integer::Ok(49)),
                                    ..Default::default()
                                },
                                podcast::ValueRecipient{
                                    name: Some("hosting company".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                                    split: Some(Integer::Ok(1)),
                                    ..Default::default()
                                },
                                podcast::ValueRecipient{
                                    name: Some("Gigi (Guest)".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("02e12fea95f576a680ec1938b7ed98ef0855eadeced493566877d404e404bfbf52".to_string()),
                                    split: Some(Integer::Ok(50)),
                                    ..Default::default()
                                },
                            },
                        value_time_split: vec![],
                        }],
                        podcast_social_interact: vec!{
                            podcast::SocialInteract{
                                priority: Some(Integer::Ok(1)),
                                uri: Some(Url::Ok(url::Url::parse("https://podcastindex.social/web/@dave/108013847520053258").unwrap())),
                                protocol: Some(podcast::SocialProtocol::ActivityPub),
                                account_id: Some("@dave".to_string()),
                                account_url: Some(Url::Ok(url::Url::parse("https://podcastindex.social/web/@dave").unwrap())),
                            },
                            podcast::SocialInteract{
                                priority: Some(Integer::Ok(2)),
                                uri: Some(Url::Ok(url::Url::parse("https://twitter.com/PodcastindexOrg/status/1507120226361647115").unwrap())),
                                protocol: Some(podcast::SocialProtocol::Twitter),
                                account_id: Some("@podcastindexorg".to_string()),
                                account_url: Some(Url::Ok(url::Url::parse("https://twitter.com/PodcastindexOrg").unwrap())),
                            },
                        },

                        ..Default::default()
                    },
                    Item{
                        title: vec!["Episode 2 - The Present".to_string()],
                        description: vec!["<p>Where are we at now in the podcasting era. What are the current challenges?</p>".to_string()],
                        link: vec![Url::Ok(url::Url::parse("https://example.com/podcast/ep0002").unwrap())],
                        guid: vec![Guid{
                            is_permalink: Some(Bool::Ok(true)),
                            value: Some(GuidValue::Url(url::Url::parse("https://example.com/ep0002").unwrap())),
                        }],
                        pub_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2020, 10, 8, 4, 30, 38).unwrap())],
                        enclosure: vec![Enclosure{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/file-02.mp3").unwrap())),
                            length: Some(Integer::Ok(43113000)),
                            type_: Some(MimeEnclosure::AudioMp3),
                        }],

                        itunes_image: vec![itunes::Image{
                            href: None,
                        }],
                        itunes_explicit: vec![Bool::Other(("no".to_string(), "should be \"true\" or \"false\"".to_string()))],
                        podcast_images: vec![podcast::Images {
                            srcset: podcast::ImageSrcSet::Ok(vec![
                                (url::Url::parse("https://example.com/images/ep2/pci_avatar-massive.jpg").unwrap(), 1500),
                                (url::Url::parse("https://example.com/images/ep2/pci_avatar-middle.jpg").unwrap(), 600),
                                (url::Url::parse("https://example.com/images/ep2/pci_avatar-small.jpg").unwrap(), 300),
                                (url::Url::parse("https://example.com/images/ep2/pci_avatar-tiny.jpg").unwrap(), 150),
                            ]),
                        }],

                        podcast_season: vec![podcast::Season{
                            name: Some("Podcasting 2.0".to_string()),
                            value: Some(Integer::Ok(1)),
                        }],
                        podcast_episode: vec![podcast::Episode{
                            display: None,
                            value: Some(Number::Integer(2)),
                        }],
                        podcast_chapters: vec![podcast::Chapters{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/ep2_chapters.json").unwrap())),
                            type_: Some(MimeChapters::ApplicationJson),
                        }],
                        podcast_soundbite: vec!{
                            podcast::Soundbite{
                                start_time: Some(Float::Ok(45.4)),
                                duration: Some(Float::Ok(56.0)),
                                value: None,
                            },
                        },
                        podcast_transcript: vec!{
                            podcast::Transcript{
                                url: Some(Url::Ok(url::Url::parse("https://example.com/ep2/transcript.txt").unwrap())),
                                type_: Some(MimeTranscript::TextPlain),
                                ..Default::default()
                            },
                        },
                        podcast_person: vec!{
                            podcast::Person{
                                href: Some(Url::Ok(url::Url::parse("https://en.wikipedia.org/wiki/Adam_Curry").unwrap())),
                                img: Some(Url::Other(("http://example.com/images/adamcurry.jpg".to_string(), "protocol must be `https`".to_string()))),
                                value: Some("Adam Curry".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                role: Some(podcast::PersonRole::Guest),
                                href: Some(Url::Ok(url::Url::parse("https://example.com/blog/daveajones/").unwrap())),
                                img: Some(Url::Other(("http://example.com/images/davejones.jpg".to_string(), "protocol must be `https`".to_string()))),
                                value: Some("Dave Jones".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                group: Some(podcast::PersonGroup::Visuals),
                                role: Some(podcast::PersonRole::CoverArtDesigner),
                                href: Some(Url::Ok(url::Url::parse("https://example.com/artist/marcusbrown").unwrap())),
                                value: Some("Marcus Brown".to_string()),
                                ..Default::default()
                            },
                        },
                        podcast_alternate_enclosure: vec!{
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioMp3),
                                length: Some(Integer::Ok(43200000)),
                                bit_rate: Some(Float::Ok(128000.0)),
                                default: Some(Bool::Ok(true)),
                                title: Some("Standard".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-02.mp3").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomMpegFile02").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(32400000)),
                                bit_rate: Some(Float::Ok(96000.0)),
                                title: Some("High quality".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-high-02.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomHighBitrateOpusFile02").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioAac),
                                length: Some(Integer::Ok(54000000)),
                                bit_rate: Some(Float::Ok(160000.0)),
                                title: Some("High quality AAC".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-proprietary-02.aac").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomProprietaryAACFile02").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(5400000)),
                                bit_rate: Some(Float::Ok(16000.0)),
                                title: Some("Low bandwidth".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-low-02.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomLowBitrateOpusFile02").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                        },
                        podcast_value: vec![podcast::Value {
                            type_: Some(podcast::ValueType::Lightning),
                            method: Some(podcast::ValueMethod::Keysend),
                            suggested: Some(Float::Ok(0.00000005)),
                            value_recipient: vec! {
                                podcast::ValueRecipient{
                                    name: Some("podcaster".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                                    split: Some(Integer::Ok(49)),
                                    ..Default::default()
                                },
                                podcast::ValueRecipient{
                                    name: Some("hosting company".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("036557ea56b3b86f08be31bcd2557cae8021b0e3a9413f0c0e52625c6696972e57".to_string()),
                                    split: Some(Integer::Ok(1)),
                                    ..Default::default()
                                },
                                podcast::ValueRecipient{
                                    name: Some("Paul Itoi (Guest)".to_string()),
                                    type_: Some(podcast::ValueRecipientType::Node),
                                    address: Some("03a9a8d953fe747d0dd94dd3c567ddc58451101e987e2d2bf7a4d1e10a2c89ff38".to_string()),
                                    split: Some(Integer::Ok(50)),
                                    ..Default::default()
                                },
                            },
                        value_time_split: vec![],
                        }],
                        ..Default::default()
                    },
                    Item{
                        title: vec!["Episode 1 - The Past".to_string()],
                        description: vec!["<p>How did podcasting get started? What was it like in the beginning?</p>".to_string()],
                        link: vec![Url::Ok(url::Url::parse("https://example.com/podcast/ep0001").unwrap())],
                        guid: vec![Guid{
                            is_permalink: Some(Bool::Ok(true)),
                            value: Some(GuidValue::Url(url::Url::parse("https://example.com/ep0001").unwrap())),
                        }],
                        pub_date: vec![badpod::DateTime::Ok(chrono::FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(2020, 10, 7, 4, 30, 38).unwrap())],
                        enclosure: vec![Enclosure{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/file-01.mp3").unwrap())),
                            length: Some(Integer::Ok(43111403)),
                            type_: Some(MimeEnclosure::AudioMp3),
                        }],

                        itunes_image: vec![itunes::Image{
                            href: None,
                        }],
                        itunes_explicit: vec![Bool::Other(("no".to_string(), "should be \"true\" or \"false\"".to_string()))],
                        podcast_images: vec![podcast::Images {
                            srcset: podcast::ImageSrcSet::Ok(vec![
                                (url::Url::parse("https://example.com/images/ep1/pci_avatar-massive.jpg").unwrap(), 1500),
                                (url::Url::parse("https://example.com/images/ep1/pci_avatar-middle.jpg").unwrap(), 600),
                                (url::Url::parse("https://example.com/images/ep1/pci_avatar-small.jpg").unwrap(), 300),
                                (url::Url::parse("https://example.com/images/ep1/pci_avatar-tiny.jpg").unwrap(), 150),
                            ]),
                        }],
                        podcast_season: vec![podcast::Season{
                            name: Some("Podcasting 2.0".to_string()),
                            value: Some(Integer::Ok(1)),
                        }],
                        podcast_episode: vec![podcast::Episode{
                            display: None,
                            value: Some(Number::Integer(1)),
                        }],
                        podcast_chapters: vec![podcast::Chapters{
                            url: Some(Url::Ok(url::Url::parse("https://example.com/ep1_chapters.json").unwrap())),
                            type_: Some(MimeChapters::ApplicationJson),
                        }],
                        podcast_soundbite: vec!{
                            podcast::Soundbite{
                                start_time: Some(Float::Ok(29.32)),
                                duration: Some(Float::Ok(34.0)),
                                value: None,
                            },
                        },
                        podcast_transcript: vec!{
                            podcast::Transcript{
                                url: Some(Url::Ok(url::Url::parse("https://example.com/ep1/transcript.txt").unwrap())),
                                type_: Some(MimeTranscript::TextPlain),
                                ..Default::default()
                            },
                        },
                        podcast_person: vec!{
                            podcast::Person{
                                href: Some(Url::Ok(url::Url::parse("https://curry.com").unwrap())),
                                img: Some(Url::Other(("http://example.com/images/adamcurry.jpg".to_string(), "protocol must be `https`".to_string()))),
                                value: Some("Adam Curry".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                role: Some(podcast::PersonRole::Guest),
                                href: Some(Url::Ok(url::Url::parse("https://www.imdb.com/name/nm0427852888/").unwrap())),
                                img: Some(Url::Other(("http://example.com/images/davejones.jpg".to_string(), "protocol must be `https`".to_string()))),
                                value: Some("Dave Jones".to_string()),
                                ..Default::default()
                            },
                            podcast::Person{
                                group: Some(podcast::PersonGroup::Visuals),
                                role: Some(podcast::PersonRole::CoverArtDesigner),
                                href: Some(Url::Ok(url::Url::parse("https://example.com/artist/jebickmorton").unwrap())),
                                value: Some("Jebick Morton".to_string()),
                                ..Default::default()
                            },
                        },
                        podcast_alternate_enclosure: vec!{
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioMp3),
                                length: Some(Integer::Ok(43203200)),
                                bit_rate: Some(Float::Ok(128000.0)),
                                default: Some(Bool::Ok(true)),
                                title: Some("Standard".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-01.mp3").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomMpegFile01").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(32406000)),
                                bit_rate: Some(Float::Ok(96000.0)),
                                title: Some("High quality".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-high-01.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomHighBitrateOpusFile01").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioAac),
                                length: Some(Integer::Ok(5400300)),
                                bit_rate: Some(Float::Ok(160000.0)),
                                title: Some("High quality AAC".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-proprietary-01.aac").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomProprietaryAACFile01").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                            podcast::AlternateEnclosure{
                                type_: Some(MimeEnclosure::AudioOpus),
                                length: Some(Integer::Ok(5042000)),
                                bit_rate: Some(Float::Ok(16000.0)),
                                title: Some("Low bandwidth".to_string()),
                                podcast_source: vec!{
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("https://example.com/file-low-01.opus").unwrap())),
                                        type_: None,
                                    },
                                    podcast::Source{
                                        uri: Some(Url::Ok(url::Url::parse("ipfs://someRandomLowBitrateOpusFile01").unwrap())),
                                        type_: None,
                                    }
                                },
                                ..Default::default()
                            },
                        },
                        ..Default::default()
                    },
                },

                ..Default::default()
            }],
        })
    ),
    ];

    for (input, expected) in conditions {
        let output = badpod::from_str(input);
        match (output, expected) {
            (Ok(output), Ok(expected)) => {
                pretty_assertions::assert_eq!(output, *expected);
            }
            (Err(output), Err(expected)) => {
                pretty_assertions::assert_eq!(output, *expected);
            }
            (Err(output), Ok(_)) => {
                panic!("Unexpected error: {output:?}");
            }
            (Ok(output), Err(_)) => {
                panic!("Unexpected success: {output:?}");
            }
        }
    }
}

// Ensures that example feeds from a number of different hosting companies can be parsed.
#[test]
fn no_error() {
    struct Test {
        url: &'static str,
        title: &'static str,
    }

    let tests = vec![
        // Simplecast/NYT
        Test {
            url: "https://feeds.simplecast.com/oCXij9l2",
            title: "Rabbit Hole",
        },
        // Megaphone
        Test {
            url: "https://feeds.megaphone.fm/heavyweight-spot",
            title: "Heavyweight",
        },
        // NPR
        Test {
            url: "https://feeds.npr.org/510307/podcast.xml",
            title: "Invisibilia",
        },
        // Buzzsprout
        Test {
            url: "https://feeds.buzzsprout.com/1.rss",
            title: "How to Start a Podcast",
        },
        // Blubrry/PowerPress
        Test {
            url: "https://lexfridman.com/feed/podcast/",
            title: "Lex Fridman Podcast",
        },
        // Podcasting 2.0
        Test {
            url: "https://feeds.podcastindex.org/pc20.xml",
            title: "Podcasting 2.0",
        },
    ];

    for test in tests {
        let feed_str = match reqwest::blocking::get(test.url) {
            Ok(response) => response.text(),
            Err(_) => {
                log::warn!("Failed to fetch feed: {}", test.url);
                continue;
            }
        };
        let feed_str = match feed_str {
            Ok(feed_str) => feed_str,
            Err(_) => {
                log::warn!("Failed to read the feed: {}", test.url);
                continue;
            }
        };
        let feed = badpod::from_str(&feed_str);
        pretty_assertions::assert_eq!(test.title, feed.unwrap().channel[0].title[0]);
    }
}