// Copyright © 2018 Teddy Wing
//
// This file is part of Meetup Find Events RSS.
//
// Meetup Find Events RSS is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// Meetup Find Events RSS is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Meetup Find Events RSS. If not, see
// .
use rss::{Channel, ChannelBuilder, Item};
use std::io;
use errors::*;
use meetup::event::Event;
pub fn generate(events: &Vec) -> Result {
let items: Vec- = events.into_iter().map(|event| {
let mut item = Item::default();
item.set_title(event.name.clone());
item.set_link(event.link.clone());
item.set_description(
format!(
"
{}
{}",
description_header(&event),
event.description.clone().unwrap_or("".to_owned()),
)
);
item
}).collect();
Ok(
ChannelBuilder::default()
.title("Meetup Events")
.description("Upcoming meetups")
.items(items)
.build()?
)
}
/// Writes the channel to standard output.
pub fn write(channel: Channel) -> Result<()> {
let stdout = io::stdout();
let handle = stdout.lock();
channel.write_to(handle)?;
Ok(())
}
/// Generates a string containing the date and venue of an `Event` for inclusion
/// in the description field of an `rss::Item`.
///
/// Looks like:
/// ``` text
/// When: 2018-04-15 19:00
/// Where: Passage, 99 Passage des Panoramas, Paris, France
/// ```
fn description_header(event: &Event) -> String {
let when = if event.local_date.is_some() &&
event.local_time.is_some() {
format!(
"When: {} {}",
event.local_date.clone().unwrap_or("".to_owned()),
event.local_time.clone().unwrap_or("".to_owned()),
)
} else {
"".to_owned()
};
let place = if let Some(venue) = event.venue.clone() {
format!(
"Where: {}, {}, {}, {}",
venue.name,
venue.address_1,
venue.city,
venue.localized_country_name,
)
} else {
"".to_owned()
};
join_nonempty(&[when, place], "
")
}
/// Joins a slice of `String`s with `separator`, filtering out empty strings.
fn join_nonempty(strings: &[String], separator: &str) -> String {
strings
.iter()
.filter(|s| !s.is_empty())
.map(|s| s.to_owned())
.collect::>()
.join(separator)
}
#[cfg(test)]
mod tests {
use super::*;
use meetup::event::Venue;
#[test]
fn test_generate_builds_a_channel_of_events() {
let events = vec![
Event {
name: "Summer Sun Celebration".to_owned(),
description: Some("Description".to_owned()),
link: "http://example.com".to_owned(),
local_date: Some("2018-04-13".to_owned()),
local_time: Some("18:30".to_owned()),
venue: None,
}
];
let event = &events[0];
let channel = generate(&events).unwrap();
let item = channel.items().first().unwrap();
assert_eq!(event.name, item.title().unwrap());
assert_eq!(event.link, item.link().unwrap());
assert_eq!(
format!(
"When: {} {}
{}",
event.local_date.clone().unwrap(),
event.local_time.clone().unwrap(),
event.description.clone().unwrap(),
),
item.description().unwrap()
);
}
#[test]
fn description_header_makes_a_string_of_time_and_venue() {
let event = Event {
name: "Fairies Story 3 Release Party".to_owned(),
description: Some("Empty".to_owned()),
link: "http://example.com".to_owned(),
local_date: Some("2018-04-15".to_owned()),
local_time: Some("19:00".to_owned()),
venue: Some(Venue {
name: "Passage".to_owned(),
address_1: "99 Passage des Panoramas".to_owned(),
city: "Paris".to_owned(),
localized_country_name: "France".to_owned(),
}),
};
let header = description_header(&event);
assert_eq!(
"When: 2018-04-15 19:00
Where: Passage, 99 Passage des Panoramas, Paris, France",
header
);
}
#[test]
fn description_header_excludes_when_if_no_date_present() {
let header = description_header(&Event {
name: "Fairies Story 3 Release Party".to_owned(),
description: Some("Empty".to_owned()),
link: "http://example.com".to_owned(),
local_date: None,
local_time: Some("19:00".to_owned()),
venue: Some(Venue {
name: "Passage".to_owned(),
address_1: "99 Passage des Panoramas".to_owned(),
city: "Paris".to_owned(),
localized_country_name: "France".to_owned(),
}),
});
assert_eq!(
"Where: Passage, 99 Passage des Panoramas, Paris, France",
header
);
}
#[test]
fn description_header_excludes_when_if_no_time_present() {
let header = description_header(&Event {
name: "Fairies Story 3 Release Party".to_owned(),
description: Some("Empty".to_owned()),
link: "http://example.com".to_owned(),
local_date: Some("2018-04-15".to_owned()),
local_time: None,
venue: Some(Venue {
name: "Passage".to_owned(),
address_1: "99 Passage des Panoramas".to_owned(),
city: "Paris".to_owned(),
localized_country_name: "France".to_owned(),
}),
});
assert_eq!(
"Where: Passage, 99 Passage des Panoramas, Paris, France",
header
);
}
#[test]
fn description_header_excludes_where_if_no_venue_present() {
let header = description_header(&Event {
name: "Fairies Story 3 Release Party".to_owned(),
description: Some("Empty".to_owned()),
link: "http://example.com".to_owned(),
local_date: Some("2018-04-15".to_owned()),
local_time: Some("19:00".to_owned()),
venue: None,
});
assert_eq!(
"When: 2018-04-15 19:00",
header
);
}
}