rss/
channel.rs

1use std::str::FromStr;
2
3use quick_xml::{XmlReader, Event, Element};
4
5use fromxml::{self, FromXml};
6use error::Error;
7use category::Category;
8use cloud::Cloud;
9use image::Image;
10use textinput::TextInput;
11use item::Item;
12use extension::ExtensionMap;
13use extension::itunes::ITunesChannelExtension;
14use extension::dublincore::DublinCoreExtension;
15
16/// A representation of the `<channel>` element.
17#[derive(Debug, Default, Clone, PartialEq)]
18pub struct Channel {
19    /// The name of the channel.
20    pub title: String,
21    /// The URL for the website corresponding to the channel.
22    pub link: String,
23    /// A description of the channel.
24    pub description: String,
25    /// The language of the channel.
26    pub language: Option<String>,
27    /// The copyright notice for the channel.
28    pub copyright: Option<String>,
29    /// The email address for the managing editor.
30    pub managing_editor: Option<String>,
31    /// The email address for the webmaster.
32    pub webmaster: Option<String>,
33    /// The publication date for the content of the channel.
34    pub pub_date: Option<String>,
35    /// The date that the contents of the channel last changed.
36    pub last_build_date: Option<String>,
37    /// The categories the channel belongs to.
38    pub categories: Vec<Category>,
39    /// The program used to generate the channel.
40    pub generator: Option<String>,
41    /// A URL that points to the documentation for the RSS format.
42    pub docs: Option<String>,
43    /// The cloud to register with to be notified of updates to the channel.
44    pub cloud: Option<Cloud>,
45    /// The number of minutes the channel can be cached before refreshing.
46    pub ttl: Option<String>,
47    /// An image that can be displayed with the channel.
48    pub image: Option<Image>,
49    /// A text input box that can be displayed with the channel.
50    pub text_input: Option<TextInput>,
51    /// A hint to tell the aggregator which hours it can skip.
52    pub skip_hours: Vec<String>,
53    /// A hint to tell the aggregator which days it can skip.
54    pub skip_days: Vec<String>,
55    /// The items in the channel.
56    pub items: Vec<Item>,
57    /// The extensions for the channel.
58    pub extensions: ExtensionMap,
59    /// The iTunes extension for the channel.
60    pub itunes_ext: Option<ITunesChannelExtension>,
61    /// The Dublin Core extension for the channel.
62    pub dublin_core_ext: Option<DublinCoreExtension>,
63}
64
65impl Channel {
66    #[inline]
67    /// Attempt to read the RSS channel from the speficied reader.
68    ///
69    /// # Example
70    ///
71    /// ```rust,ignore
72    /// let reader: BufRead = ...;
73    /// let channel = Channel::read_from(reader).unwrap();
74    /// ```
75    pub fn read_from<R: ::std::io::BufRead>(reader: R) -> Result<Channel, Error> {
76        ::parser::parse(::quick_xml::XmlReader::from_reader(reader))
77    }
78}
79
80impl FromXml for Channel {
81    fn from_xml<R: ::std::io::BufRead>(mut reader: XmlReader<R>,
82                                       _: Element)
83                                       -> Result<(Self, XmlReader<R>), Error> {
84        let mut channel = Channel::default();
85
86        while let Some(e) = reader.next() {
87            match e {
88                Ok(Event::Start(element)) => {
89                    match element.name() {
90                        b"category" => {
91                            let (category, reader_) = try!(Category::from_xml(reader, element));
92                            reader = reader_;
93                            channel.categories.push(category);
94                        }
95                        b"cloud" => {
96                            let (cloud, reader_) = try!(Cloud::from_xml(reader, element));
97                            reader = reader_;
98                            channel.cloud = Some(cloud);
99                        }
100                        b"image" => {
101                            let (image, reader_) = try!(Image::from_xml(reader, element));
102                            reader = reader_;
103                            channel.image = Some(image);
104                        }
105                        b"textInput" => {
106                            let (text_input, reader_) = try!(TextInput::from_xml(reader, element));
107                            reader = reader_;
108                            channel.text_input = Some(text_input);
109                        }
110                        b"item" => {
111                            let (item, reader_) = try!(Item::from_xml(reader, element));
112                            reader = reader_;
113                            channel.items.push(item);
114                        }
115                        b"title" => {
116                            if let Some(content) = element_text!(reader) {
117                                channel.title = content;
118                            }
119                        }
120                        b"link" => {
121                            if let Some(content) = element_text!(reader) {
122                                channel.link = content;
123                            }
124                        }
125                        b"description" => {
126                            if let Some(content) = element_text!(reader) {
127                                channel.description = content;
128                            }
129                        }
130                        b"language" => channel.language = element_text!(reader),
131                        b"copyright" => channel.copyright = element_text!(reader),
132                        b"managingEditor" => {
133                            channel.managing_editor = element_text!(reader);
134                        }
135                        b"webMaster" => channel.webmaster = element_text!(reader),
136                        b"pubDate" => channel.pub_date = element_text!(reader),
137                        b"lastBuildDate" => {
138                            channel.last_build_date = element_text!(reader);
139                        }
140                        b"generator" => channel.generator = element_text!(reader),
141                        b"docs" => channel.docs = element_text!(reader),
142                        b"ttl" => channel.ttl = element_text!(reader),
143                        b"skipHours" => {
144                            while let Some(e) = reader.next() {
145                                match e {
146                                    Ok(Event::Start(element)) => {
147                                        if element.name() == b"hour" {
148                                            if let Some(content) = element_text!(reader) {
149                                                channel.skip_hours.push(content);
150                                            }
151                                        } else {
152                                            skip_element!(reader);
153                                        }
154                                    }
155                                    Ok(Event::End(_)) => {
156                                        break;
157                                    }
158                                    Err(err) => return Err(err.into()),
159                                    _ => {}
160                                }
161                            }
162                        }
163                        b"skipDays" => {
164                            while let Some(e) = reader.next() {
165                                match e {
166                                    Ok(Event::Start(element)) => {
167                                        if element.name() == b"day" {
168                                            if let Some(content) = element_text!(reader) {
169                                                channel.skip_days.push(content);
170                                            }
171                                        } else {
172                                            skip_element!(reader);
173                                        }
174                                    }
175                                    Ok(Event::End(_)) => {
176                                        break;
177                                    }
178                                    Err(err) => return Err(err.into()),
179                                    _ => {}
180                                }
181                            }
182                        }
183                        _ => {
184                            if let Some((ns, name)) = fromxml::extension_name(&element) {
185                                parse_extension!(reader, element, ns, name, channel.extensions);
186                            } else {
187                                skip_element!(reader);
188                            }
189                        }
190                    }
191                }
192                Ok(Event::End(_)) => {
193                    if !channel.extensions.is_empty() {
194                        if let Some(map) = channel.extensions.remove("itunes") {
195                            channel.itunes_ext = Some(ITunesChannelExtension::from_map(map));
196                        }
197
198                        if let Some(map) = channel.extensions.remove("dc") {
199                            channel.dublin_core_ext = Some(DublinCoreExtension::from_map(map));
200                        }
201                    }
202
203                    return Ok((channel, reader));
204                }
205                Err(err) => return Err(err.into()),
206                _ => {}
207            }
208        }
209
210        Err(Error::EOF)
211    }
212}
213
214impl FromStr for Channel {
215    type Err = Error;
216    #[inline]
217    /// Attempt to read the RSS channel from the speficied str.
218    fn from_str(s: &str) -> Result<Channel, Error> {
219        Channel::read_from(s.as_bytes())
220    }
221}