1 use std::io::{BufRead, Write}; 2 3 use quick_xml::events::attributes::Attributes; 4 use quick_xml::events::{BytesEnd, BytesStart, Event}; 5 use quick_xml::Error as XmlError; 6 use quick_xml::Reader; 7 use quick_xml::Writer; 8 9 use crate::category::Category; 10 use crate::content::Content; 11 use crate::error::Error; 12 use crate::extension::util::{extension_name, parse_extension}; 13 use crate::extension::ExtensionMap; 14 use crate::fromxml::FromXml; 15 use crate::link::Link; 16 use crate::person::Person; 17 use crate::source::Source; 18 use crate::text::Text; 19 use crate::toxml::{ToXml, WriterExt}; 20 use crate::util::{atom_datetime, atom_text, default_fixed_datetime, FixedDateTime}; 21 22 /// Represents an entry in an Atom feed 23 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] 24 #[derive(Debug, Clone, PartialEq)] 25 #[cfg_attr(feature = "builders", derive(Builder))] 26 #[cfg_attr( 27 feature = "builders", 28 builder( 29 setter(into), 30 default, 31 build_fn(name = "build_impl", private, error = "never::Never") 32 ) 33 )] 34 pub struct Entry { 35 /// A human-readable title for the entry. 36 pub title: Text, 37 /// A universally unique and permanent URI. 38 pub id: String, 39 /// The last time the entry was modified. 40 pub updated: FixedDateTime, 41 /// The authors of the feed. 42 #[cfg_attr(feature = "builders", builder(setter(each = "author")))] 43 pub authors: Vec<Person>, 44 /// The categories that the entry belongs to. 45 #[cfg_attr(feature = "builders", builder(setter(each = "category")))] 46 pub categories: Vec<Category>, 47 /// The contributors to the entry. 48 #[cfg_attr(feature = "builders", builder(setter(each = "contributor")))] 49 pub contributors: Vec<Person>, 50 /// The Web pages related to the entry. 51 #[cfg_attr(feature = "builders", builder(setter(each = "link")))] 52 pub links: Vec<Link>, 53 /// The time of the initial creation or first availability of the entry. 54 pub published: Option<FixedDateTime>, 55 /// Information about rights held in and over the entry. 56 pub rights: Option<Text>, 57 /// The source information if an entry is copied from one feed into another feed. 58 pub source: Option<Source>, 59 /// A short summary, abstract, or excerpt of the entry. 60 pub summary: Option<Text>, 61 /// Contains or links to the complete content of the entry. 62 pub content: Option<Content>, 63 /// The extensions for this entry. 64 #[cfg_attr(feature = "builders", builder(setter(each = "extension")))] 65 pub extensions: ExtensionMap, 66 } 67 68 impl Entry { 69 /// Return the title of this entry. 70 /// 71 /// # Examples 72 /// 73 /// ``` 74 /// use atom_syndication::Entry; 75 /// 76 /// let mut entry = Entry::default(); 77 /// entry.set_title("Entry Title"); 78 /// assert_eq!(entry.title(), "Entry Title"); 79 /// ``` title(&self) -> &Text80 pub fn title(&self) -> &Text { 81 &self.title 82 } 83 84 /// Set the title of this entry. 85 /// 86 /// # Examples 87 /// 88 /// ``` 89 /// use atom_syndication::Entry; 90 /// 91 /// let mut entry = Entry::default(); 92 /// entry.set_title("Entry Title"); 93 /// ``` set_title<V>(&mut self, title: V) where V: Into<Text>,94 pub fn set_title<V>(&mut self, title: V) 95 where 96 V: Into<Text>, 97 { 98 self.title = title.into(); 99 } 100 101 /// Return the unique URI of this entry. 102 /// 103 /// # Examples 104 /// 105 /// ``` 106 /// use atom_syndication::Entry; 107 /// 108 /// let mut entry = Entry::default(); 109 /// entry.set_id("urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6"); 110 /// assert_eq!(entry.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6"); 111 /// ``` id(&self) -> &str112 pub fn id(&self) -> &str { 113 self.id.as_str() 114 } 115 116 /// Set the unique URI of this entry. 117 /// 118 /// # Examples 119 /// 120 /// ``` 121 /// use atom_syndication::Entry; 122 /// 123 /// let mut entry = Entry::default(); 124 /// entry.set_id("urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6"); 125 /// ``` set_id<V>(&mut self, id: V) where V: Into<String>,126 pub fn set_id<V>(&mut self, id: V) 127 where 128 V: Into<String>, 129 { 130 self.id = id.into(); 131 } 132 133 /// Return the last time that this entry was modified. 134 /// 135 /// # Examples 136 /// 137 /// ``` 138 /// use atom_syndication::Entry; 139 /// use atom_syndication::FixedDateTime; 140 /// use std::str::FromStr; 141 /// 142 /// let mut entry = Entry::default(); 143 /// entry.set_updated(FixedDateTime::from_str("2017-06-03T15:15:44-05:00").unwrap()); 144 /// assert_eq!(entry.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00"); 145 /// ``` updated(&self) -> &FixedDateTime146 pub fn updated(&self) -> &FixedDateTime { 147 &self.updated 148 } 149 150 /// Set the last time that this entry was modified. 151 /// 152 /// # Examples 153 /// 154 /// ``` 155 /// use atom_syndication::Entry; 156 /// use atom_syndication::FixedDateTime; 157 /// use std::str::FromStr; 158 /// 159 /// let mut entry = Entry::default(); 160 /// entry.set_updated(FixedDateTime::from_str("2017-06-03T15:15:44-05:00").unwrap()); 161 /// ``` set_updated<V>(&mut self, updated: V) where V: Into<FixedDateTime>,162 pub fn set_updated<V>(&mut self, updated: V) 163 where 164 V: Into<FixedDateTime>, 165 { 166 self.updated = updated.into(); 167 } 168 169 /// Return the authors of this entry. 170 /// 171 /// # Examples 172 /// 173 /// ``` 174 /// use atom_syndication::{Entry, Person}; 175 /// 176 /// let mut entry = Entry::default(); 177 /// entry.set_authors(vec![Person::default()]); 178 /// assert_eq!(entry.authors().len(), 1); 179 /// ``` authors(&self) -> &[Person]180 pub fn authors(&self) -> &[Person] { 181 self.authors.as_slice() 182 } 183 184 /// Set the authors of this entry. 185 /// 186 /// # Examples 187 /// 188 /// ``` 189 /// use atom_syndication::{Entry, Person}; 190 /// 191 /// let mut entry = Entry::default(); 192 /// entry.set_authors(vec![Person::default()]); 193 /// ``` set_authors<V>(&mut self, authors: V) where V: Into<Vec<Person>>,194 pub fn set_authors<V>(&mut self, authors: V) 195 where 196 V: Into<Vec<Person>>, 197 { 198 self.authors = authors.into(); 199 } 200 201 /// Return the categories this entry belongs to. 202 /// 203 /// # Examples 204 /// 205 /// ``` 206 /// use atom_syndication::{Entry, Category}; 207 /// 208 /// let mut entry = Entry::default(); 209 /// entry.set_categories(vec![Category::default()]); 210 /// assert_eq!(entry.categories().len(), 1); 211 /// ``` categories(&self) -> &[Category]212 pub fn categories(&self) -> &[Category] { 213 self.categories.as_slice() 214 } 215 216 /// Set the categories this entry belongs to. 217 /// 218 /// # Examples 219 /// 220 /// ``` 221 /// use atom_syndication::{Entry, Category}; 222 /// 223 /// let mut entry = Entry::default(); 224 /// entry.set_categories(vec![Category::default()]); 225 /// ``` set_categories<V>(&mut self, categories: V) where V: Into<Vec<Category>>,226 pub fn set_categories<V>(&mut self, categories: V) 227 where 228 V: Into<Vec<Category>>, 229 { 230 self.categories = categories.into(); 231 } 232 233 /// Return the contributors to this entry. 234 /// 235 /// # Examples 236 /// 237 /// ``` 238 /// use atom_syndication::{Entry, Person}; 239 /// 240 /// let mut entry = Entry::default(); 241 /// entry.set_contributors(vec![Person::default()]); 242 /// assert_eq!(entry.contributors().len(), 1); 243 /// ``` contributors(&self) -> &[Person]244 pub fn contributors(&self) -> &[Person] { 245 self.contributors.as_slice() 246 } 247 248 /// Set the contributors to this entry. 249 /// 250 /// # Examples 251 /// 252 /// ``` 253 /// use atom_syndication::{Entry, Person}; 254 /// 255 /// let mut entry = Entry::default(); 256 /// entry.set_contributors(vec![Person::default()]); 257 /// ``` set_contributors<V>(&mut self, contributors: V) where V: Into<Vec<Person>>,258 pub fn set_contributors<V>(&mut self, contributors: V) 259 where 260 V: Into<Vec<Person>>, 261 { 262 self.contributors = contributors.into(); 263 } 264 265 /// Return the links for this entry. 266 /// 267 /// # Examples 268 /// 269 /// ``` 270 /// use atom_syndication::{Entry, Link}; 271 /// 272 /// let mut entry = Entry::default(); 273 /// entry.set_links(vec![Link::default()]); 274 /// assert_eq!(entry.links().len(), 1); 275 /// ``` links(&self) -> &[Link]276 pub fn links(&self) -> &[Link] { 277 self.links.as_slice() 278 } 279 280 /// Set the links for this entry. 281 /// 282 /// # Examples 283 /// 284 /// ``` 285 /// use atom_syndication::{Entry, Link}; 286 /// 287 /// let mut entry = Entry::default(); 288 /// entry.set_links(vec![Link::default()]); 289 /// ``` set_links<V>(&mut self, links: V) where V: Into<Vec<Link>>,290 pub fn set_links<V>(&mut self, links: V) 291 where 292 V: Into<Vec<Link>>, 293 { 294 self.links = links.into(); 295 } 296 297 /// Return the time that this entry was initially created or first made available. 298 /// 299 /// # Examples 300 /// 301 /// ``` 302 /// use atom_syndication::Entry; 303 /// use atom_syndication::FixedDateTime; 304 /// use std::str::FromStr; 305 /// 306 /// let mut entry = Entry::default(); 307 /// entry.set_published(FixedDateTime::from_str("2017-06-01T15:15:44-05:00").unwrap()); 308 /// assert_eq!(entry.published().map(|x|x.to_rfc3339()), Some("2017-06-01T15:15:44-05:00".to_string())); 309 /// ``` published(&self) -> Option<&FixedDateTime>310 pub fn published(&self) -> Option<&FixedDateTime> { 311 self.published.as_ref() 312 } 313 314 /// Set the time that this entry was initially created or first made available. 315 /// 316 /// # Examples 317 /// 318 /// ``` 319 /// use atom_syndication::Entry; 320 /// use atom_syndication::FixedDateTime; 321 /// use std::str::FromStr; 322 /// 323 /// let mut entry = Entry::default(); 324 /// entry.set_published(FixedDateTime::from_str("2017-06-01T15:15:44-05:00").unwrap()); 325 /// ``` set_published<V>(&mut self, published: V) where V: Into<Option<FixedDateTime>>,326 pub fn set_published<V>(&mut self, published: V) 327 where 328 V: Into<Option<FixedDateTime>>, 329 { 330 self.published = published.into(); 331 } 332 333 /// Return the information about the rights held in and over this entry. 334 /// 335 /// # Examples 336 /// 337 /// ``` 338 /// use atom_syndication::{Entry, Text}; 339 /// 340 /// let mut entry = Entry::default(); 341 /// entry.set_rights(Text::from("© 2017 John Doe")); 342 /// assert_eq!(entry.rights().map(Text::as_str), Some("© 2017 John Doe")); 343 /// ``` rights(&self) -> Option<&Text>344 pub fn rights(&self) -> Option<&Text> { 345 self.rights.as_ref() 346 } 347 348 /// Set the information about the rights held in and over this entry. 349 /// 350 /// # Examples 351 /// 352 /// ``` 353 /// use atom_syndication::{Entry, Text}; 354 /// 355 /// let mut entry = Entry::default(); 356 /// entry.set_rights(Text::from("© 2017 John Doe")); 357 /// ``` set_rights<V>(&mut self, rights: V) where V: Into<Option<Text>>,358 pub fn set_rights<V>(&mut self, rights: V) 359 where 360 V: Into<Option<Text>>, 361 { 362 self.rights = rights.into(); 363 } 364 365 /// Return the source of this entry if it was copied from another feed. 366 /// 367 /// # Examples 368 /// 369 /// ``` 370 /// use atom_syndication::{Entry, Source}; 371 /// 372 /// let mut entry = Entry::default(); 373 /// entry.set_source(Source::default()); 374 /// assert!(entry.source().is_some()); 375 /// ``` source(&self) -> Option<&Source>376 pub fn source(&self) -> Option<&Source> { 377 self.source.as_ref() 378 } 379 380 /// Set the source of this entry if it was copied from another feed. 381 /// 382 /// # Examples 383 /// 384 /// ``` 385 /// use atom_syndication::{Entry, Source}; 386 /// 387 /// let mut entry = Entry::default(); 388 /// entry.set_source(Source::default()); 389 /// ``` set_source<V>(&mut self, source: V) where V: Into<Option<Source>>,390 pub fn set_source<V>(&mut self, source: V) 391 where 392 V: Into<Option<Source>>, 393 { 394 self.source = source.into() 395 } 396 397 /// Return the summary of this entry. 398 /// 399 /// # Examples 400 /// 401 /// ``` 402 /// use atom_syndication::{Entry, Text}; 403 /// 404 /// let mut entry = Entry::default(); 405 /// entry.set_summary(Text::from("Entry summary.")); 406 /// assert_eq!(entry.summary().map(Text::as_str), Some("Entry summary.")); 407 /// ``` summary(&self) -> Option<&Text>408 pub fn summary(&self) -> Option<&Text> { 409 self.summary.as_ref() 410 } 411 412 /// Set the summary of this entry. 413 /// 414 /// # Examples 415 /// 416 /// ``` 417 /// use atom_syndication::{Entry, Text}; 418 /// 419 /// let mut entry = Entry::default(); 420 /// entry.set_summary(Text::from("Entry summary.")); 421 /// ``` set_summary<V>(&mut self, summary: V) where V: Into<Option<Text>>,422 pub fn set_summary<V>(&mut self, summary: V) 423 where 424 V: Into<Option<Text>>, 425 { 426 self.summary = summary.into(); 427 } 428 429 /// Return the content of this entry. 430 /// 431 /// # Examples 432 /// 433 /// ``` 434 /// use atom_syndication::{Entry, Content}; 435 /// 436 /// let mut entry = Entry::default(); 437 /// entry.set_content(Content::default()); 438 /// assert!(entry.content().is_some()); 439 /// ``` content(&self) -> Option<&Content>440 pub fn content(&self) -> Option<&Content> { 441 self.content.as_ref() 442 } 443 444 /// Set the content of this entry. 445 /// 446 /// # Examples 447 /// 448 /// ``` 449 /// use atom_syndication::{Entry, Content}; 450 /// 451 /// let mut entry = Entry::default(); 452 /// entry.set_content(Content::default()); 453 /// assert!(entry.content().is_some()); 454 /// ``` set_content<V>(&mut self, content: V) where V: Into<Option<Content>>,455 pub fn set_content<V>(&mut self, content: V) 456 where 457 V: Into<Option<Content>>, 458 { 459 self.content = content.into(); 460 } 461 462 /// Return the extensions for this entry. 463 /// 464 /// # Examples 465 /// 466 /// ``` 467 /// use std::collections::BTreeMap; 468 /// use atom_syndication::Entry; 469 /// use atom_syndication::extension::{ExtensionMap, Extension}; 470 /// 471 /// let extension = Extension::default(); 472 /// 473 /// let mut item_map = BTreeMap::<String, Vec<Extension>>::new(); 474 /// item_map.insert("ext:name".to_string(), vec![extension]); 475 /// 476 /// let mut extension_map = ExtensionMap::default(); 477 /// extension_map.insert("ext".to_string(), item_map); 478 /// 479 /// let mut entry = Entry::default(); 480 /// entry.set_extensions(extension_map); 481 /// assert_eq!(entry.extensions() 482 /// .get("ext") 483 /// .and_then(|m| m.get("ext:name")) 484 /// .map(|v| v.len()), 485 /// Some(1)); 486 /// ``` extensions(&self) -> &ExtensionMap487 pub fn extensions(&self) -> &ExtensionMap { 488 &self.extensions 489 } 490 491 /// Set the extensions for this entry. 492 /// 493 /// # Examples 494 /// 495 /// ``` 496 /// use atom_syndication::Entry; 497 /// use atom_syndication::extension::ExtensionMap; 498 /// 499 /// let mut entry = Entry::default(); 500 /// entry.set_extensions(ExtensionMap::default()); 501 /// ``` set_extensions<V>(&mut self, extensions: V) where V: Into<ExtensionMap>,502 pub fn set_extensions<V>(&mut self, extensions: V) 503 where 504 V: Into<ExtensionMap>, 505 { 506 self.extensions = extensions.into() 507 } 508 } 509 510 impl FromXml for Entry { from_xml<B: BufRead>(reader: &mut Reader<B>, _: Attributes<'_>) -> Result<Self, Error>511 fn from_xml<B: BufRead>(reader: &mut Reader<B>, _: Attributes<'_>) -> Result<Self, Error> { 512 let mut entry = Entry::default(); 513 let mut buf = Vec::new(); 514 515 loop { 516 match reader.read_event(&mut buf)? { 517 Event::Start(element) => match element.name() { 518 b"id" => entry.id = atom_text(reader)?.unwrap_or_default(), 519 b"title" => entry.title = Text::from_xml(reader, element.attributes())?, 520 b"updated" => { 521 entry.updated = 522 atom_datetime(reader)?.unwrap_or_else(default_fixed_datetime) 523 } 524 b"author" => entry 525 .authors 526 .push(Person::from_xml(reader, element.attributes())?), 527 b"category" => entry 528 .categories 529 .push(Category::from_xml(reader, element.attributes())?), 530 b"contributor" => entry 531 .contributors 532 .push(Person::from_xml(reader, element.attributes())?), 533 b"link" => entry 534 .links 535 .push(Link::from_xml(reader, element.attributes())?), 536 b"published" => entry.published = atom_datetime(reader)?, 537 b"rights" => entry.rights = Some(Text::from_xml(reader, element.attributes())?), 538 b"source" => { 539 entry.source = Some(Source::from_xml(reader, element.attributes())?) 540 } 541 b"summary" => { 542 entry.summary = Some(Text::from_xml(reader, element.attributes())?) 543 } 544 b"content" => { 545 entry.content = Some(Content::from_xml(reader, element.attributes())?) 546 } 547 n => { 548 if let Some((ns, name)) = extension_name(element.name()) { 549 parse_extension( 550 reader, 551 element.attributes(), 552 ns, 553 name, 554 &mut entry.extensions, 555 )?; 556 } else { 557 reader.read_to_end(n, &mut Vec::new())?; 558 } 559 } 560 }, 561 Event::End(_) => break, 562 Event::Eof => return Err(Error::Eof), 563 _ => {} 564 } 565 566 buf.clear(); 567 } 568 569 Ok(entry) 570 } 571 } 572 573 impl ToXml for Entry { to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError>574 fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> { 575 let name = b"entry"; 576 writer.write_event(Event::Start(BytesStart::borrowed(name, name.len())))?; 577 writer.write_object_named(&self.title, b"title")?; 578 writer.write_text_element(b"id", &*self.id)?; 579 writer.write_text_element(b"updated", &*self.updated.to_rfc3339())?; 580 writer.write_objects_named(&self.authors, "author")?; 581 writer.write_objects(&self.categories)?; 582 writer.write_objects_named(&self.contributors, "contributor")?; 583 writer.write_objects(&self.links)?; 584 585 if let Some(ref published) = self.published { 586 writer.write_text_element(b"published", &published.to_rfc3339())?; 587 } 588 589 if let Some(ref rights) = self.rights { 590 writer.write_object_named(rights, b"rights")?; 591 } 592 593 if let Some(ref source) = self.source { 594 writer.write_object(source)?; 595 } 596 597 if let Some(ref summary) = self.summary { 598 writer.write_object_named(summary, b"summary")?; 599 } 600 601 if let Some(ref content) = self.content { 602 writer.write_object(content)?; 603 } 604 605 for map in self.extensions.values() { 606 for extensions in map.values() { 607 writer.write_objects(extensions)?; 608 } 609 } 610 611 writer.write_event(Event::End(BytesEnd::borrowed(name)))?; 612 613 Ok(()) 614 } 615 } 616 617 impl Default for Entry { default() -> Self618 fn default() -> Self { 619 Entry { 620 title: Text::default(), 621 id: String::new(), 622 updated: default_fixed_datetime(), 623 authors: Vec::new(), 624 categories: Vec::new(), 625 contributors: Vec::new(), 626 links: Vec::new(), 627 published: None, 628 rights: None, 629 source: None, 630 summary: None, 631 content: None, 632 extensions: ExtensionMap::default(), 633 } 634 } 635 } 636 637 #[cfg(feature = "builders")] 638 impl EntryBuilder { 639 /// Builds a new `Entry`. build(&self) -> Entry640 pub fn build(&self) -> Entry { 641 self.build_impl().unwrap() 642 } 643 } 644