atom_syndication/
source.rs

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