1 use std::collections::BTreeMap;
2 use std::io::{BufRead, Write};
3 use std::str::{self, FromStr};
4 
5 use quick_xml::events::attributes::Attributes;
6 use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
7 use quick_xml::Error as XmlError;
8 use quick_xml::Reader;
9 use quick_xml::Writer;
10 
11 use crate::category::Category;
12 use crate::entry::Entry;
13 use crate::error::Error;
14 use crate::extension::util::{extension_name, parse_extension};
15 use crate::extension::ExtensionMap;
16 use crate::fromxml::FromXml;
17 use crate::generator::Generator;
18 use crate::link::Link;
19 use crate::person::Person;
20 use crate::text::Text;
21 use crate::toxml::{ToXml, WriterExt};
22 use crate::util::{atom_datetime, atom_text, default_fixed_datetime, FixedDateTime};
23 
24 /// Represents an Atom feed
25 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
26 #[derive(Debug, Clone, PartialEq)]
27 #[cfg_attr(feature = "builders", derive(Builder))]
28 #[cfg_attr(
29     feature = "builders",
30     builder(
31         setter(into),
32         default,
33         build_fn(name = "build_impl", private, error = "never::Never")
34     )
35 )]
36 pub struct Feed {
37     /// A human-readable title for the feed.
38     pub title: Text,
39     /// A universally unique and permanent URI.
40     pub id: String,
41     /// The last time the feed was modified in a significant way.
42     pub updated: FixedDateTime,
43     /// The authors of the feed.
44     #[cfg_attr(feature = "builders", builder(setter(each = "author")))]
45     pub authors: Vec<Person>,
46     /// The categories that the feed belongs to.
47     #[cfg_attr(feature = "builders", builder(setter(each = "category")))]
48     pub categories: Vec<Category>,
49     /// The contributors to the feed.
50     #[cfg_attr(feature = "builders", builder(setter(each = "contributor")))]
51     pub contributors: Vec<Person>,
52     /// The software used to generate the feed.
53     pub generator: Option<Generator>,
54     /// A small image which provides visual identification for the feed.
55     pub icon: Option<String>,
56     /// The Web pages related to the feed.
57     #[cfg_attr(feature = "builders", builder(setter(each = "link")))]
58     pub links: Vec<Link>,
59     /// A larger image which provides visual identification for the feed.
60     pub logo: Option<String>,
61     /// Information about rights held in and over the feed.
62     pub rights: Option<Text>,
63     /// A human-readable description or subtitle for the feed.
64     pub subtitle: Option<Text>,
65     /// The entries contained in the feed.
66     #[cfg_attr(feature = "builders", builder(setter(each = "entry")))]
67     pub entries: Vec<Entry>,
68     /// The extensions for the feed.
69     #[cfg_attr(feature = "builders", builder(setter(each = "extension")))]
70     pub extensions: ExtensionMap,
71     /// The namespaces present in the feed tag.
72     #[cfg_attr(feature = "builders", builder(setter(each = "namespace")))]
73     pub namespaces: BTreeMap<String, String>,
74 }
75 
76 impl Feed {
77     /// Attempt to read an Atom feed from the reader.
78     ///
79     /// # Examples
80     ///
81     /// ```no_run
82     /// use std::io::BufReader;
83     /// use std::fs::File;
84     /// use atom_syndication::Feed;
85     ///
86     /// let file = File::open("example.xml").unwrap();
87     /// let feed = Feed::read_from(BufReader::new(file)).unwrap();
88     /// ```
read_from<B: BufRead>(reader: B) -> Result<Feed, Error>89     pub fn read_from<B: BufRead>(reader: B) -> Result<Feed, Error> {
90         let mut reader = Reader::from_reader(reader);
91         reader.expand_empty_elements(true);
92 
93         let mut buf = Vec::new();
94 
95         loop {
96             match reader.read_event(&mut buf)? {
97                 Event::Start(element) => {
98                     if element.name() == b"feed" {
99                         let mut feed = Feed::from_xml(&mut reader, element.attributes())?;
100 
101                         for attr in element.attributes().with_checks(false).flatten() {
102                             if !attr.key.starts_with(b"xmlns:") || attr.key == b"xmlns:dc" {
103                                 continue;
104                             }
105 
106                             let key = str::from_utf8(&attr.key[6..])?.to_string();
107                             let value = attr.unescape_and_decode_value(&reader)?;
108                             feed.namespaces.insert(key, value);
109                         }
110 
111                         return Ok(feed);
112                     } else {
113                         return Err(Error::InvalidStartTag);
114                     }
115                 }
116                 Event::Eof => break,
117                 _ => {}
118             }
119 
120             buf.clear();
121         }
122 
123         Err(Error::Eof)
124     }
125 
126     /// Attempt to write this Atom feed to a writer.
127     ///
128     /// # Examples
129     ///
130     /// ```no_run
131     /// use std::io::BufReader;
132     /// use std::fs::File;
133     /// use atom_syndication::Feed;
134     ///
135     /// let file = File::open("example.xml").unwrap();
136     /// let feed = Feed::read_from(BufReader::new(file)).unwrap();
137     /// let out = File::create("out.xml").unwrap();
138     /// feed.write_to(out).unwrap();
139     /// ```
write_to<W: Write>(&self, writer: W) -> Result<W, Error>140     pub fn write_to<W: Write>(&self, writer: W) -> Result<W, Error> {
141         let mut writer = Writer::new(writer);
142         writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
143         writer.write_event(Event::Text(BytesText::from_escaped("\n".as_bytes())))?;
144         self.to_xml(&mut writer)?;
145         Ok(writer.into_inner())
146     }
147 
148     /// Return the title of this feed.
149     ///
150     /// # Examples
151     ///
152     /// ```
153     /// use atom_syndication::Feed;
154     ///
155     /// let mut feed = Feed::default();
156     /// feed.set_title("Feed Title");
157     /// assert_eq!(feed.title(), "Feed Title");
158     /// ```
title(&self) -> &Text159     pub fn title(&self) -> &Text {
160         &self.title
161     }
162 
163     /// Set the title of this feed.
164     ///
165     /// # Examples
166     ///
167     /// ```
168     /// use atom_syndication::Feed;
169     ///
170     /// let mut feed = Feed::default();
171     /// feed.set_title("Feed Title");
172     /// ```
set_title<V>(&mut self, title: V) where V: Into<Text>,173     pub fn set_title<V>(&mut self, title: V)
174     where
175         V: Into<Text>,
176     {
177         self.title = title.into();
178     }
179 
180     /// Return the unique URI of this feed.
181     ///
182     /// # Examples
183     ///
184     /// ```
185     /// use atom_syndication::Feed;
186     ///
187     /// let mut feed = Feed::default();
188     /// feed.set_id("urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
189     /// assert_eq!(feed.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
190     /// ```
id(&self) -> &str191     pub fn id(&self) -> &str {
192         self.id.as_str()
193     }
194 
195     /// Set the unique URI of this feed.
196     ///
197     /// # Examples
198     ///
199     /// ```
200     /// use atom_syndication::Feed;
201     ///
202     /// let mut feed = Feed::default();
203     /// feed.set_id("urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
204     /// ```
set_id<V>(&mut self, id: V) where V: Into<String>,205     pub fn set_id<V>(&mut self, id: V)
206     where
207         V: Into<String>,
208     {
209         self.id = id.into();
210     }
211 
212     /// Return the last time that this feed was modified.
213     ///
214     /// # Examples
215     ///
216     /// ```
217     /// use atom_syndication::Feed;
218     /// use atom_syndication::FixedDateTime;
219     /// use std::str::FromStr;
220     ///
221     /// let mut feed = Feed::default();
222     /// feed.set_updated(FixedDateTime::from_str("2017-06-03T15:15:44-05:00").unwrap());
223     /// assert_eq!(feed.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
224     /// ```
updated(&self) -> &FixedDateTime225     pub fn updated(&self) -> &FixedDateTime {
226         &self.updated
227     }
228 
229     /// Set the last time that this feed was modified.
230     ///
231     /// # Examples
232     ///
233     /// ```
234     /// use atom_syndication::Feed;
235     /// use atom_syndication::FixedDateTime;
236     /// use std::str::FromStr;
237     ///
238     /// let mut feed = Feed::default();
239     /// feed.set_updated(FixedDateTime::from_str("2017-06-03T15:15:44-05:00").unwrap());
240     /// ```
set_updated<V>(&mut self, updated: V) where V: Into<FixedDateTime>,241     pub fn set_updated<V>(&mut self, updated: V)
242     where
243         V: Into<FixedDateTime>,
244     {
245         self.updated = updated.into();
246     }
247 
248     /// Return the authors of this feed.
249     ///
250     /// # Examples
251     ///
252     /// ```
253     /// use atom_syndication::{Feed, Person};
254     ///
255     /// let mut feed = Feed::default();
256     /// feed.set_authors(vec![Person::default()]);
257     /// assert_eq!(feed.authors().len(), 1);
258     /// ```
authors(&self) -> &[Person]259     pub fn authors(&self) -> &[Person] {
260         self.authors.as_slice()
261     }
262 
263     /// Set the authors of this feed.
264     ///
265     /// # Examples
266     ///
267     /// ```
268     /// use atom_syndication::{Feed, Person};
269     ///
270     /// let mut feed = Feed::default();
271     /// feed.set_authors(vec![Person::default()]);
272     /// ```
set_authors<V>(&mut self, authors: V) where V: Into<Vec<Person>>,273     pub fn set_authors<V>(&mut self, authors: V)
274     where
275         V: Into<Vec<Person>>,
276     {
277         self.authors = authors.into();
278     }
279 
280     /// Return the categories this feed belongs to.
281     ///
282     /// # Examples
283     ///
284     /// ```
285     /// use atom_syndication::{Feed, Category};
286     ///
287     /// let mut feed = Feed::default();
288     /// feed.set_categories(vec![Category::default()]);
289     /// assert_eq!(feed.categories().len(), 1);
290     /// ```
categories(&self) -> &[Category]291     pub fn categories(&self) -> &[Category] {
292         self.categories.as_slice()
293     }
294 
295     /// Set the categories this feed belongs to.
296     ///
297     /// # Examples
298     ///
299     /// ```
300     /// use atom_syndication::{Feed, Category};
301     ///
302     /// let mut feed = Feed::default();
303     /// feed.set_categories(vec![Category::default()]);
304     /// ```
set_categories<V>(&mut self, categories: V) where V: Into<Vec<Category>>,305     pub fn set_categories<V>(&mut self, categories: V)
306     where
307         V: Into<Vec<Category>>,
308     {
309         self.categories = categories.into();
310     }
311 
312     /// Return the contributors to this feed.
313     ///
314     /// # Examples
315     ///
316     /// ```
317     /// use atom_syndication::{Feed, Person};
318     ///
319     /// let mut feed = Feed::default();
320     /// feed.set_contributors(vec![Person::default()]);
321     /// assert_eq!(feed.contributors().len(), 1);
322     /// ```
contributors(&self) -> &[Person]323     pub fn contributors(&self) -> &[Person] {
324         self.contributors.as_slice()
325     }
326 
327     /// Set the contributors to this feed.
328     ///
329     /// # Examples
330     ///
331     /// ```
332     /// use atom_syndication::{Feed, Person};
333     ///
334     /// let mut feed = Feed::default();
335     /// feed.set_contributors(vec![Person::default()]);
336     /// ```
set_contributors<V>(&mut self, contributors: V) where V: Into<Vec<Person>>,337     pub fn set_contributors<V>(&mut self, contributors: V)
338     where
339         V: Into<Vec<Person>>,
340     {
341         self.contributors = contributors.into();
342     }
343 
344     /// Return the name of the software used to generate this feed.
345     ///
346     /// # Examples
347     ///
348     /// ```
349     /// use atom_syndication::{Feed, Generator};
350     ///
351     /// let mut feed = Feed::default();
352     /// feed.set_generator(Generator::default());
353     /// assert!(feed.generator().is_some());
354     /// ```
generator(&self) -> Option<&Generator>355     pub fn generator(&self) -> Option<&Generator> {
356         self.generator.as_ref()
357     }
358 
359     /// Set the name of the software used to generate this feed.
360     ///
361     /// # Examples
362     ///
363     /// ```
364     /// use atom_syndication::{Feed, Generator};
365     ///
366     /// let mut feed = Feed::default();
367     /// feed.set_generator(Generator::default());
368     /// ```
set_generator<V>(&mut self, generator: V) where V: Into<Option<Generator>>,369     pub fn set_generator<V>(&mut self, generator: V)
370     where
371         V: Into<Option<Generator>>,
372     {
373         self.generator = generator.into()
374     }
375 
376     /// Return the icon for this feed.
377     ///
378     /// # Examples
379     ///
380     /// ```
381     /// use atom_syndication::Feed;
382     ///
383     /// let mut feed = Feed::default();
384     /// feed.set_icon("http://example.com/icon.png".to_string());
385     /// assert_eq!(feed.icon(), Some("http://example.com/icon.png"));
386     /// ```
icon(&self) -> Option<&str>387     pub fn icon(&self) -> Option<&str> {
388         self.icon.as_deref()
389     }
390 
391     /// Set the icon for this feed.
392     ///
393     /// # Examples
394     ///
395     /// ```
396     /// use atom_syndication::Feed;
397     ///
398     /// let mut feed = Feed::default();
399     /// feed.set_icon("http://example.com/icon.png".to_string());
400     /// ```
set_icon<V>(&mut self, icon: V) where V: Into<Option<String>>,401     pub fn set_icon<V>(&mut self, icon: V)
402     where
403         V: Into<Option<String>>,
404     {
405         self.icon = icon.into()
406     }
407 
408     /// Return the Web pages related to this feed.
409     ///
410     /// # Examples
411     ///
412     /// ```
413     /// use atom_syndication::{Feed, Link};
414     ///
415     /// let mut feed = Feed::default();
416     /// feed.set_links(vec![Link::default()]);
417     /// assert_eq!(feed.links().len(), 1);
418     /// ```
links(&self) -> &[Link]419     pub fn links(&self) -> &[Link] {
420         self.links.as_slice()
421     }
422 
423     /// Set the Web pages related to this feed.
424     ///
425     /// # Examples
426     ///
427     /// ```
428     /// use atom_syndication::{Feed, Link};
429     ///
430     /// let mut feed = Feed::default();
431     /// feed.set_links(vec![Link::default()]);
432     /// ```
set_links<V>(&mut self, links: V) where V: Into<Vec<Link>>,433     pub fn set_links<V>(&mut self, links: V)
434     where
435         V: Into<Vec<Link>>,
436     {
437         self.links = links.into();
438     }
439 
440     /// Return the logo for this feed.
441     ///
442     /// # Examples
443     ///
444     /// ```
445     /// use atom_syndication::Feed;
446     ///
447     /// let mut feed = Feed::default();
448     /// feed.set_logo("http://example.com/logo.png".to_string());
449     /// assert_eq!(feed.logo(), Some("http://example.com/logo.png"));
450     /// ```
logo(&self) -> Option<&str>451     pub fn logo(&self) -> Option<&str> {
452         self.logo.as_deref()
453     }
454 
455     /// Set the logo for this feed.
456     ///
457     /// # Examples
458     ///
459     /// ```
460     /// use atom_syndication::Feed;
461     ///
462     /// let mut feed = Feed::default();
463     /// feed.set_logo("http://example.com/logo.png".to_string());
464     /// ```
set_logo<V>(&mut self, logo: V) where V: Into<Option<String>>,465     pub fn set_logo<V>(&mut self, logo: V)
466     where
467         V: Into<Option<String>>,
468     {
469         self.logo = logo.into()
470     }
471 
472     /// Return the information about the rights held in and over this feed.
473     ///
474     /// # Examples
475     ///
476     /// ```
477     /// use atom_syndication::{Feed, Text};
478     ///
479     /// let mut feed = Feed::default();
480     /// feed.set_rights(Text::from("© 2017 John Doe"));
481     /// assert_eq!(feed.rights().map(Text::as_str), Some("© 2017 John Doe"));
482     /// ```
rights(&self) -> Option<&Text>483     pub fn rights(&self) -> Option<&Text> {
484         self.rights.as_ref()
485     }
486 
487     /// Set the information about the rights held in and over this feed.
488     ///
489     /// # Examples
490     ///
491     /// ```
492     /// use atom_syndication::{Feed, Text};
493     ///
494     /// let mut feed = Feed::default();
495     /// feed.set_rights(Text::from("© 2017 John Doe"));
496     /// ```
set_rights<V>(&mut self, rights: V) where V: Into<Option<Text>>,497     pub fn set_rights<V>(&mut self, rights: V)
498     where
499         V: Into<Option<Text>>,
500     {
501         self.rights = rights.into()
502     }
503 
504     /// Return the description or subtitle of this feed.
505     ///
506     /// # Examples
507     ///
508     /// ```
509     /// use atom_syndication::{Feed, Text};
510     ///
511     /// let mut feed = Feed::default();
512     /// feed.set_subtitle(Text::from("Feed subtitle"));
513     /// assert_eq!(feed.subtitle().map(Text::as_str), Some("Feed subtitle"));
514     /// ```
subtitle(&self) -> Option<&Text>515     pub fn subtitle(&self) -> Option<&Text> {
516         self.subtitle.as_ref()
517     }
518 
519     /// Set the description or subtitle of this feed.
520     ///
521     /// # Examples
522     ///
523     /// ```
524     /// use atom_syndication::{Feed, Text};
525     ///
526     /// let mut feed = Feed::default();
527     /// feed.set_subtitle(Text::from("Feed subtitle"));
528     /// ```
set_subtitle<V>(&mut self, subtitle: V) where V: Into<Option<Text>>,529     pub fn set_subtitle<V>(&mut self, subtitle: V)
530     where
531         V: Into<Option<Text>>,
532     {
533         self.subtitle = subtitle.into()
534     }
535 
536     /// Return the entries in this feed.
537     ///
538     /// # Examples
539     ///
540     /// ```
541     /// use atom_syndication::{Feed, Entry};
542     ///
543     /// let mut feed = Feed::default();
544     /// feed.set_entries(vec![Entry::default()]);
545     /// assert_eq!(feed.entries().len(), 1);
546     /// ```
entries(&self) -> &[Entry]547     pub fn entries(&self) -> &[Entry] {
548         self.entries.as_slice()
549     }
550 
551     /// Set the entries in this feed.
552     ///
553     /// # Examples
554     ///
555     /// ```
556     /// use atom_syndication::{Feed, Entry};
557     ///
558     /// let mut feed = Feed::default();
559     /// feed.set_entries(vec![Entry::default()]);
560     /// ```
set_entries<V>(&mut self, entries: V) where V: Into<Vec<Entry>>,561     pub fn set_entries<V>(&mut self, entries: V)
562     where
563         V: Into<Vec<Entry>>,
564     {
565         self.entries = entries.into();
566     }
567 
568     /// Return the extensions for this feed.
569     ///
570     /// # Examples
571     ///
572     /// ```
573     /// use std::collections::BTreeMap;
574     /// use atom_syndication::Feed;
575     /// use atom_syndication::extension::{ExtensionMap, Extension};
576     ///
577     /// let extension = Extension::default();
578     ///
579     /// let mut item_map = BTreeMap::<String, Vec<Extension>>::new();
580     /// item_map.insert("ext:name".to_string(), vec![extension]);
581     ///
582     /// let mut extension_map = ExtensionMap::default();
583     /// extension_map.insert("ext".to_string(), item_map);
584     ///
585     /// let mut feed = Feed::default();
586     /// feed.set_extensions(extension_map);
587     /// assert_eq!(feed.extensions()
588     ///                .get("ext")
589     ///                .and_then(|m| m.get("ext:name"))
590     ///                .map(|v| v.len()),
591     ///            Some(1));
592     /// ```
extensions(&self) -> &ExtensionMap593     pub fn extensions(&self) -> &ExtensionMap {
594         &self.extensions
595     }
596 
597     /// Set the extensions for this feed.
598     ///
599     /// # Examples
600     ///
601     /// ```
602     /// use atom_syndication::Feed;
603     /// use atom_syndication::extension::ExtensionMap;
604     ///
605     /// let mut feed = Feed::default();
606     /// feed.set_extensions(ExtensionMap::default());
607     /// ```
set_extensions<V>(&mut self, extensions: V) where V: Into<ExtensionMap>,608     pub fn set_extensions<V>(&mut self, extensions: V)
609     where
610         V: Into<ExtensionMap>,
611     {
612         self.extensions = extensions.into()
613     }
614 
615     /// Return the namespaces for this feed.
616     ///
617     /// # Examples
618     ///
619     /// ```
620     /// use std::collections::BTreeMap;
621     /// use atom_syndication::Feed;
622     ///
623     /// let mut namespaces = BTreeMap::new();
624     /// namespaces.insert("ext".to_string(), "http://example.com".to_string());
625     ///
626     /// let mut feed = Feed::default();
627     /// feed.set_namespaces(namespaces);
628     /// assert_eq!(feed.namespaces().get("ext").map(|s| s.as_str()), Some("http://example.com"));
629     /// ```
namespaces(&self) -> &BTreeMap<String, String>630     pub fn namespaces(&self) -> &BTreeMap<String, String> {
631         &self.namespaces
632     }
633 
634     /// Set the namespaces for this feed.
635     ///
636     /// # Examples
637     ///
638     /// ```
639     /// use std::collections::BTreeMap;
640     /// use atom_syndication::Feed;
641     ///
642     /// let mut feed = Feed::default();
643     /// feed.set_namespaces(BTreeMap::new());
644     /// ```
set_namespaces<V>(&mut self, namespaces: V) where V: Into<BTreeMap<String, String>>,645     pub fn set_namespaces<V>(&mut self, namespaces: V)
646     where
647         V: Into<BTreeMap<String, String>>,
648     {
649         self.namespaces = namespaces.into()
650     }
651 }
652 
653 impl FromXml for Feed {
from_xml<B: BufRead>(reader: &mut Reader<B>, _: Attributes<'_>) -> Result<Self, Error>654     fn from_xml<B: BufRead>(reader: &mut Reader<B>, _: Attributes<'_>) -> Result<Self, Error> {
655         let mut feed = Feed::default();
656         let mut buf = Vec::new();
657 
658         loop {
659             match reader.read_event(&mut buf)? {
660                 Event::Start(element) => match element.name() {
661                     b"title" => feed.title = Text::from_xml(reader, element.attributes())?,
662                     b"id" => feed.id = atom_text(reader)?.unwrap_or_default(),
663                     b"updated" => {
664                         feed.updated = atom_datetime(reader)?.unwrap_or_else(default_fixed_datetime)
665                     }
666                     b"author" => feed
667                         .authors
668                         .push(Person::from_xml(reader, element.attributes())?),
669                     b"category" => feed
670                         .categories
671                         .push(Category::from_xml(reader, element.attributes())?),
672                     b"contributor" => feed
673                         .contributors
674                         .push(Person::from_xml(reader, element.attributes())?),
675                     b"generator" => {
676                         feed.generator = Some(Generator::from_xml(reader, element.attributes())?)
677                     }
678                     b"icon" => feed.icon = atom_text(reader)?,
679                     b"link" => feed
680                         .links
681                         .push(Link::from_xml(reader, element.attributes())?),
682                     b"logo" => feed.logo = atom_text(reader)?,
683                     b"rights" => feed.rights = Some(Text::from_xml(reader, element.attributes())?),
684                     b"subtitle" => {
685                         feed.subtitle = Some(Text::from_xml(reader, element.attributes())?)
686                     }
687                     b"entry" => feed
688                         .entries
689                         .push(Entry::from_xml(reader, element.attributes())?),
690                     n => {
691                         if let Some((ns, name)) = extension_name(element.name()) {
692                             parse_extension(
693                                 reader,
694                                 element.attributes(),
695                                 ns,
696                                 name,
697                                 &mut feed.extensions,
698                             )?;
699                         } else {
700                             reader.read_to_end(n, &mut Vec::new())?;
701                         }
702                     }
703                 },
704                 Event::End(_) => break,
705                 Event::Eof => return Err(Error::Eof),
706                 _ => {}
707             }
708 
709             buf.clear();
710         }
711 
712         Ok(feed)
713     }
714 }
715 
716 impl ToXml for Feed {
to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError>717     fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
718         let name = b"feed";
719         let mut element = BytesStart::borrowed(name, name.len());
720         element.push_attribute(("xmlns", "http://www.w3.org/2005/Atom"));
721 
722         for (ns, uri) in &self.namespaces {
723             element.push_attribute((format!("xmlns:{}", ns).as_bytes(), uri.as_bytes()));
724         }
725 
726         writer.write_event(Event::Start(element))?;
727         writer.write_object_named(&self.title, b"title")?;
728         writer.write_text_element(b"id", &*self.id)?;
729         writer.write_text_element(b"updated", &*self.updated.to_rfc3339())?;
730         writer.write_objects_named(&self.authors, "author")?;
731         writer.write_objects(&self.categories)?;
732         writer.write_objects_named(&self.contributors, "contributor")?;
733 
734         if let Some(ref generator) = self.generator {
735             writer.write_object(generator)?;
736         }
737 
738         if let Some(ref icon) = self.icon {
739             writer.write_text_element(b"icon", &**icon)?;
740         }
741 
742         writer.write_objects(&self.links)?;
743 
744         if let Some(ref logo) = self.logo {
745             writer.write_text_element(b"logo", &**logo)?;
746         }
747 
748         if let Some(ref rights) = self.rights {
749             writer.write_object_named(rights, b"rights")?;
750         }
751 
752         if let Some(ref subtitle) = self.subtitle {
753             writer.write_object_named(subtitle, b"subtitle")?;
754         }
755 
756         writer.write_objects(&self.entries)?;
757 
758         for map in self.extensions.values() {
759             for extensions in map.values() {
760                 writer.write_objects(extensions)?;
761             }
762         }
763 
764         writer.write_event(Event::End(BytesEnd::borrowed(name)))?;
765 
766         Ok(())
767     }
768 }
769 
770 impl FromStr for Feed {
771     type Err = Error;
772 
from_str(s: &str) -> Result<Self, Error>773     fn from_str(s: &str) -> Result<Self, Error> {
774         Feed::read_from(s.as_bytes())
775     }
776 }
777 
778 impl ToString for Feed {
to_string(&self) -> String779     fn to_string(&self) -> String {
780         let buf = self.write_to(Vec::new()).unwrap_or_default();
781         // this unwrap should be safe since the bytes written from the Feed are all valid utf8
782         String::from_utf8(buf).unwrap()
783     }
784 }
785 
786 impl Default for Feed {
default() -> Self787     fn default() -> Self {
788         Feed {
789             title: Text::default(),
790             id: String::new(),
791             updated: default_fixed_datetime(),
792             authors: Vec::new(),
793             categories: Vec::new(),
794             contributors: Vec::new(),
795             generator: None,
796             icon: None,
797             links: Vec::new(),
798             logo: None,
799             rights: None,
800             subtitle: None,
801             entries: Vec::new(),
802             extensions: ExtensionMap::default(),
803             namespaces: BTreeMap::default(),
804         }
805     }
806 }
807 
808 #[cfg(feature = "builders")]
809 impl FeedBuilder {
810     /// Builds a new `Feed`.
build(&self) -> Feed811     pub fn build(&self) -> Feed {
812         self.build_impl().unwrap()
813     }
814 }
815