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