Skip to content

Commit bbf50f9

Browse files
committed
Add conversions between our Entry and RSS's Entry
Add the Image and TextInput types to support the change.
1 parent 3ffcfea commit bbf50f9

File tree

6 files changed

+178
-14
lines changed

6 files changed

+178
-14
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
target
22
Cargo.lock
3+
.DS_Store
4+
*.bk

src/entry.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rss;
33

44
use std::str::FromStr;
55
use chrono::{DateTime, UTC};
6+
67
use category::Category;
78
use link::Link;
89
use person::Person;
@@ -101,7 +102,7 @@ impl From<rss::Item> for Entry {
101102
source_data: Some(EntryData::Rss(entry_clone)),
102103
id: entry.guid.map(|id| id.into()),
103104
title: entry.title,
104-
updated: date.clone().unwrap_or_else(UTC::now),
105+
updated: date.unwrap_or_else(UTC::now),
105106
published: date,
106107
summary: None,
107108
content: entry.description,

src/feed.rs

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ use rss;
33

44
use std::str::FromStr;
55
use chrono::{DateTime, UTC};
6+
67
use link::Link;
78
use person::Person;
89
use generator::Generator;
9-
1010
use category::Category;
1111
use entry::Entry;
12+
use image::Image;
13+
use text_input::TextInput;
1214

1315
enum FeedData {
1416
Atom(atom::Feed),
15-
RSS(rss::Channel),
17+
Rss(rss::Channel),
1618
}
1719

1820
// A helpful table of approximately equivalent elements can be found here:
@@ -36,29 +38,47 @@ pub struct Feed {
3638
// `icon` in Atom, not present in RSS
3739
pub icon: Option<String>,
3840
// `logo` in Atom, and `image` in RSS
39-
pub image: Option<String>,
41+
pub image: Option<Image>,
4042

4143
// `generator` in both Atom and RSS
4244
pub generator: Option<Generator>,
4345
// `links` in Atom, and `link` in RSS (produces a 1 item Vec)
4446
pub links: Vec<Link>,
4547
// `categories` in both Atom and RSS
4648
pub categories: Vec<Category>,
47-
// TODO: Should the `web_master` be in `contributors`, `authors`, or at all?
4849
// `authors` in Atom, `managing_editor` in RSS (produces 1 item Vec)
50+
// TODO: Should the `web_master` be in `contributors`, `authors`, or at all?
4951
pub authors: Vec<Person>,
5052
// `contributors` in Atom, `web_master` in RSS (produces a 1 item Vec)
5153
pub contributors: Vec<Person>,
5254
// `entries` in Atom, and `items` in RSS
53-
// TODO: Add more fields that are necessary for RSS
5455
// TODO: Fancy translation, e.g. Atom <link rel="via"> = RSS `source`
5556
pub entries: Vec<Entry>,
57+
58+
// TODO: Add more fields that are necessary for RSS
59+
// `ttl` in RSS, not present in Atom
60+
pub ttl: Option<String>,
61+
// `skip_hours` in RSS, not present in Atom
62+
pub skip_hours: Option<String>,
63+
// `skip_days` in RSS, not present in Atom
64+
pub skip_days: Option<String>,
65+
// `text_input` in RSS, not present in Atom
66+
pub text_input: Option<TextInput>,
67+
// `language` in RSS, not present in Atom
68+
pub language: Option<String>,
69+
// `docs` in RSS, not present in Atom
70+
pub docs: Option<String>,
71+
// `rating` in RSS, not present in Atom
72+
pub rating: Option<String>,
5673
}
5774

5875
impl From<atom::Feed> for Feed {
5976
fn from(feed: atom::Feed) -> Self {
77+
let feed_clone = feed.clone();
78+
let title = feed.title.clone();
79+
let link = feed.links.first().map_or_else(|| "".into(), |link| link.href.clone());
6080
Feed {
61-
source_data: Some(FeedData::Atom(feed.clone())),
81+
source_data: Some(FeedData::Atom(feed_clone)),
6282
id: Some(feed.id),
6383
title: feed.title,
6484
description: feed.subtitle,
@@ -67,7 +87,17 @@ impl From<atom::Feed> for Feed {
6787
.map(|date| date.with_timezone(&UTC)),
6888
copyright: feed.rights,
6989
icon: feed.icon,
70-
image: feed.logo,
90+
// (Note, in practice the image <title> and <link> should have the same value as the
91+
// channel's <title> and <link>.)
92+
image: feed.logo.map(|url| {
93+
Image {
94+
url: url,
95+
title: title,
96+
link: link,
97+
width: None,
98+
height: None,
99+
}
100+
}),
71101
generator: feed.generator.map(|generator| generator.into()),
72102
links: feed.links.into_iter().map(|link| link.into()).collect(),
73103
categories: feed.categories.into_iter().map(|person| person.into()).collect(),
@@ -77,6 +107,13 @@ impl From<atom::Feed> for Feed {
77107
.into_iter()
78108
.map(|entry| entry.into())
79109
.collect::<Vec<_>>(),
110+
ttl: None,
111+
skip_hours: None,
112+
skip_days: None,
113+
text_input: None,
114+
language: None,
115+
docs: None,
116+
rating: None,
80117
}
81118
}
82119
}
@@ -97,7 +134,7 @@ impl From<Feed> for atom::Feed {
97134
updated: feed.updated.unwrap_or_else(UTC::now).to_rfc3339(),
98135
rights: feed.copyright,
99136
icon: feed.icon,
100-
logo: feed.image,
137+
logo: feed.image.map(|image| image.url),
101138
generator: None,
102139
links: feed.links.into_iter().map(|link| link.into()).collect(),
103140
categories: feed.categories.into_iter().map(|category| category.into()).collect(),
@@ -112,14 +149,71 @@ impl From<Feed> for atom::Feed {
112149
}
113150
}
114151

152+
impl From<rss::Channel> for Feed {
153+
fn from(feed: rss::Channel) -> Self {
154+
Feed {
155+
source_data: Some(FeedData::Rss(feed.clone())),
156+
id: None,
157+
title: feed.title,
158+
description: Some(feed.description),
159+
updated: None,
160+
copyright: feed.copyright,
161+
icon: None,
162+
image: feed.image.map(|image| image.into()),
163+
generator: feed.generator.map(Generator::from_name),
164+
links: vec![Link::from_href(feed.link)],
165+
categories: feed.categories.into_iter().map(|person| person.into()).collect(),
166+
authors: feed.managing_editor.into_iter().map(Person::from_name).collect(),
167+
contributors: feed.web_master.into_iter().map(Person::from_name).collect(),
168+
entries: feed.items.into_iter().map(|entry| entry.into()).collect(),
169+
ttl: feed.ttl,
170+
skip_hours: feed.skip_hours,
171+
skip_days: feed.skip_days,
172+
text_input: feed.text_input.map(|input| input.into()),
173+
rating: feed.rating,
174+
language: feed.language,
175+
docs: feed.docs,
176+
}
177+
}
178+
}
179+
180+
impl From<Feed> for rss::Channel {
181+
fn from(feed: Feed) -> rss::Channel {
182+
if let Some(FeedData::Rss(feed)) = feed.source_data {
183+
feed
184+
} else {
185+
rss::Channel {
186+
title: feed.title,
187+
description: feed.description.unwrap_or("".into()),
188+
pub_date: None,
189+
last_build_date: feed.updated.map(|date| date.to_rfc2822()),
190+
link: feed.links.into_iter().next().map_or_else(|| "".into(), |link| link.href),
191+
items: feed.entries.into_iter().map(|entry| entry.into()).collect(),
192+
categories: feed.categories.into_iter().map(|category| category.into()).collect(),
193+
image: feed.image.map(|image| image.into()),
194+
generator: feed.generator.map(|generator| generator.name),
195+
managing_editor: feed.authors.into_iter().next().map(|person| person.name),
196+
web_master: feed.contributors.into_iter().next().map(|person| person.name),
197+
copyright: feed.copyright,
198+
ttl: feed.ttl,
199+
skip_hours: feed.skip_hours,
200+
skip_days: feed.skip_days,
201+
text_input: feed.text_input.map(|input| input.into()),
202+
rating: feed.rating,
203+
language: feed.language,
204+
docs: feed.docs,
205+
}
206+
}
207+
}
208+
}
209+
115210
impl FromStr for Feed {
116211
type Err = &'static str;
117212

118213
fn from_str(s: &str) -> Result<Self, Self::Err> {
119214
match s.parse::<FeedData>() {
120215
Ok(FeedData::Atom(feed)) => Ok(feed.into()),
121-
// TODO: Implement the RSS conversions
122-
Ok(FeedData::RSS(_)) => Err("RSS Unimplemented"),
216+
Ok(FeedData::Rss(feed)) => Ok(feed.into()),
123217
Err(e) => Err(e),
124218
}
125219
}
@@ -133,7 +227,7 @@ impl FromStr for FeedData {
133227
Ok(feed) => Ok(FeedData::Atom(feed)),
134228
_ => {
135229
match s.parse::<rss::Rss>() {
136-
Ok(rss::Rss(channel)) => Ok(FeedData::RSS(channel)),
230+
Ok(rss::Rss(channel)) => Ok(FeedData::Rss(channel)),
137231
_ => Err("Could not parse XML as Atom or RSS from input"),
138232
}
139233
}
@@ -145,7 +239,7 @@ impl ToString for FeedData {
145239
fn to_string(&self) -> String {
146240
match *self {
147241
FeedData::Atom(ref atom_feed) => atom_feed.to_string(),
148-
FeedData::RSS(ref rss_channel) => rss::Rss(rss_channel.clone()).to_string(),
242+
FeedData::Rss(ref rss_channel) => rss::Rss(rss_channel.clone()).to_string(),
149243
}
150244
}
151245
}
@@ -226,7 +320,7 @@ mod test {
226320
..Default::default()
227321
};
228322

229-
let rss = FeedData::RSS(channel);
323+
let rss = FeedData::Rss(channel);
230324
assert_eq!(rss.to_string(),
231325
"<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss \
232326
version=\'2.0\'><channel><title>My \

src/image.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use rss;
2+
3+
pub struct Image {
4+
pub url: String,
5+
pub title: String,
6+
pub link: String,
7+
pub width: Option<u32>,
8+
pub height: Option<u32>,
9+
}
10+
11+
impl From<rss::Image> for Image {
12+
fn from(image: rss::Image) -> Image {
13+
Image {
14+
url: image.url,
15+
title: image.title,
16+
link: image.link,
17+
width: image.width,
18+
height: image.height,
19+
}
20+
}
21+
}
22+
23+
impl From<Image> for rss::Image {
24+
fn from(image: Image) -> rss::Image {
25+
rss::Image {
26+
url: image.url,
27+
title: image.title,
28+
link: image.link,
29+
width: image.width,
30+
height: image.height,
31+
}
32+
}
33+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ mod feed;
99
mod link;
1010
mod generator;
1111
mod guid;
12+
mod image;
13+
mod text_input;
1214

1315
pub use ::category::Category;
1416
pub use ::person::Person;
@@ -17,3 +19,5 @@ pub use ::feed::Feed;
1719
pub use ::link::Link;
1820
pub use ::generator::Generator;
1921
pub use ::guid::Guid;
22+
pub use ::image::Image;
23+
pub use ::text_input::TextInput;

src/text_input.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use rss;
2+
3+
pub struct TextInput {
4+
pub title: String,
5+
pub description: String,
6+
pub name: String,
7+
pub link: String,
8+
}
9+
10+
impl From<rss::TextInput> for TextInput {
11+
fn from(input: rss::TextInput) -> TextInput {
12+
TextInput {
13+
title: input.title,
14+
description: input.description,
15+
name: input.name,
16+
link: input.link,
17+
}
18+
}
19+
}
20+
21+
impl From<TextInput> for rss::TextInput {
22+
fn from(input: TextInput) -> rss::TextInput {
23+
rss::TextInput {
24+
title: input.title,
25+
description: input.description,
26+
name: input.name,
27+
link: input.link,
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)