diff options
Diffstat (limited to 'lib/jsonfeed/src/feed.rs')
| -rw-r--r-- | lib/jsonfeed/src/feed.rs | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/lib/jsonfeed/src/feed.rs b/lib/jsonfeed/src/feed.rs new file mode 100644 index 0000000..8b5b5ce --- /dev/null +++ b/lib/jsonfeed/src/feed.rs @@ -0,0 +1,296 @@ +use std::default::Default; + +use item::Item; +use builder::Builder; + +const VERSION_1: &'static str = "https://jsonfeed.org/version/1"; + +/// Represents a single feed +/// +/// # Examples +/// +/// ```rust +/// // Serialize a feed object to a JSON string +/// +/// # extern crate jsonfeed; +/// # use std::default::Default; +/// # use jsonfeed::Feed; +/// # fn main() { +/// let feed: Feed = Feed::default(); +/// assert_eq!( +/// jsonfeed::to_string(&feed).unwrap(), +/// "{\"version\":\"https://jsonfeed.org/version/1\",\"title\":\"\",\"items\":[]}" +/// ); +/// # } +/// ``` +/// +/// ```rust +/// // Deserialize a feed objects from a JSON String +/// +/// # extern crate jsonfeed; +/// # use jsonfeed::Feed; +/// # fn main() { +/// let json = "{\"version\":\"https://jsonfeed.org/version/1\",\"title\":\"\",\"items\":[]}"; +/// let feed: Feed = jsonfeed::from_str(&json).unwrap(); +/// assert_eq!( +/// feed, +/// Feed::default() +/// ); +/// # } +/// ``` +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct Feed { + pub version: String, + pub title: String, + pub items: Vec<Item>, + #[serde(skip_serializing_if = "Option::is_none")] + pub home_page_url: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub feed_url: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_comment: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub next_url: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub icon: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub favicon: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option<Author>, + #[serde(skip_serializing_if = "Option::is_none")] + pub expired: Option<bool>, + #[serde(skip_serializing_if = "Option::is_none")] + pub hubs: Option<Vec<Hub>>, +} + +impl Feed { + /// Used to construct a Feed object + pub fn builder() -> Builder { + Builder::new() + } +} + +impl Default for Feed { + fn default() -> Feed { + Feed { + version: VERSION_1.to_string(), + title: "".to_string(), + items: vec![], + home_page_url: None, + feed_url: None, + description: None, + user_comment: None, + next_url: None, + icon: None, + favicon: None, + author: None, + expired: None, + hubs: None, + } + } +} + +/// Represents an `attachment` for an item +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct Attachment { + url: String, + mime_type: String, + title: Option<String>, + size_in_bytes: Option<u64>, + duration_in_seconds: Option<u64>, +} + +/// Represents an `author` in both a feed and a feed item +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct Author { + name: Option<String>, + url: Option<String>, + avatar: Option<String>, +} + +impl Author { + pub fn new() -> Author { + Author { + name: None, + url: None, + avatar: None, + } + } + + pub fn name<I: Into<String>>(mut self, name: I) -> Self { + self.name = Some(name.into()); + self + } + + pub fn url<I: Into<String>>(mut self, url: I) -> Self { + self.url = Some(url.into()); + self + } + + pub fn avatar<I: Into<String>>(mut self, avatar: I) -> Self { + self.avatar = Some(avatar.into()); + self + } +} + +/// Represents a `hub` for a feed +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct Hub { + #[serde(rename = "type")] + type_: String, + url: String, +} + +#[cfg(test)] +mod tests { + use serde_json; + use std::default::Default; + use super::*; + + #[test] + fn serialize_feed() { + let feed = Feed { + version: "https://jsonfeed.org/version/1".to_string(), + title: "some title".to_string(), + items: vec![], + home_page_url: None, + description: None, + expired: Some(true), + ..Default::default() + }; + assert_eq!( + serde_json::to_string(&feed).unwrap(), + r#"{"version":"https://jsonfeed.org/version/1","title":"some title","items":[],"expired":true}"# + ); + } + + #[test] + fn deserialize_feed() { + let json = r#"{"version":"https://jsonfeed.org/version/1","title":"some title","items":[]}"#; + let feed: Feed = serde_json::from_str(&json).unwrap(); + let expected = Feed { + version: "https://jsonfeed.org/version/1".to_string(), + title: "some title".to_string(), + items: vec![], + ..Default::default() + }; + assert_eq!( + feed, + expected + ); + } + + #[test] + fn serialize_attachment() { + let attachment = Attachment { + url: "http://example.com".to_string(), + mime_type: "application/json".to_string(), + title: Some("some title".to_string()), + size_in_bytes: Some(1), + duration_in_seconds: Some(1), + }; + assert_eq!( + serde_json::to_string(&attachment).unwrap(), + r#"{"url":"http://example.com","mime_type":"application/json","title":"some title","size_in_bytes":1,"duration_in_seconds":1}"# + ); + } + + #[test] + fn deserialize_attachment() { + let json = r#"{"url":"http://example.com","mime_type":"application/json","title":"some title","size_in_bytes":1,"duration_in_seconds":1}"#; + let attachment: Attachment = serde_json::from_str(&json).unwrap(); + let expected = Attachment { + url: "http://example.com".to_string(), + mime_type: "application/json".to_string(), + title: Some("some title".to_string()), + size_in_bytes: Some(1), + duration_in_seconds: Some(1), + }; + assert_eq!( + attachment, + expected + ); + } + + #[test] + fn serialize_author() { + let author = Author { + name: Some("bob jones".to_string()), + url: Some("http://example.com".to_string()), + avatar: Some("http://img.com/blah".to_string()), + }; + assert_eq!( + serde_json::to_string(&author).unwrap(), + r#"{"name":"bob jones","url":"http://example.com","avatar":"http://img.com/blah"}"# + ); + } + + #[test] + fn deserialize_author() { + let json = r#"{"name":"bob jones","url":"http://example.com","avatar":"http://img.com/blah"}"#; + let author: Author = serde_json::from_str(&json).unwrap(); + let expected = Author { + name: Some("bob jones".to_string()), + url: Some("http://example.com".to_string()), + avatar: Some("http://img.com/blah".to_string()), + }; + assert_eq!( + author, + expected + ); + } + + #[test] + fn serialize_hub() { + let hub = Hub { + type_: "some-type".to_string(), + url: "http://example.com".to_string(), + }; + assert_eq!( + serde_json::to_string(&hub).unwrap(), + r#"{"type":"some-type","url":"http://example.com"}"# + ) + } + + #[test] + fn deserialize_hub() { + let json = r#"{"type":"some-type","url":"http://example.com"}"#; + let hub: Hub = serde_json::from_str(&json).unwrap(); + let expected = Hub { + type_: "some-type".to_string(), + url: "http://example.com".to_string(), + }; + assert_eq!( + hub, + expected + ); + } + + #[test] + fn deser_podcast() { + let json = r#"{ + "version": "https://jsonfeed.org/version/1", + "title": "Timetable", + "home_page_url": "http://timetable.manton.org/", + "items": [ + { + "id": "http://timetable.manton.org/2017/04/episode-45-launch-week/", + "url": "http://timetable.manton.org/2017/04/episode-45-launch-week/", + "title": "Episode 45: Launch week", + "content_html": "I’m rolling out early access to Micro.blog this week. I talk about how the first 2 days have gone, mistakes with TestFlight, and what to do next.", + "date_published": "2017-04-26T01:09:45+00:00", + "attachments": [ + { + "url": "http://timetable.manton.org/podcast-download/139/episode-45-launch-week.mp3", + "mime_type": "audio/mpeg", + "size_in_bytes": 5236920 + } + ] + } + ] +}"#; + serde_json::from_str::<Feed>(&json).expect("Failed to deserialize podcast feed"); + } +} |
