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}