diff options
| author | Xe Iaso <me@christine.website> | 2023-09-30 10:36:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-30 10:36:37 -0400 |
| commit | ac6a3df0d18cc73524c0096d954a57d24cad5669 (patch) | |
| tree | 81474177d730440657f490ae29892d62392251ea /lib/jsonfeed/src | |
| parent | cbdea8ba3fca9a663778af71f8df5965aeb6c090 (diff) | |
| download | xesite-ac6a3df0d18cc73524c0096d954a57d24cad5669.tar.xz xesite-ac6a3df0d18cc73524c0096d954a57d24cad5669.zip | |
Xesite V4 (#723)
* scripts/ditherify: fix quoting
Signed-off-by: Xe Iaso <me@xeiaso.net>
* clean up some old files
Signed-off-by: Xe Iaso <me@xeiaso.net>
* import site into lume
Signed-off-by: Xe Iaso <me@xeiaso.net>
* initial go code
Signed-off-by: Xe Iaso <me@xeiaso.net>
* move vods index to top level
Signed-off-by: Xe Iaso <me@xeiaso.net>
* remove the ads
Signed-off-by: Xe Iaso <me@xeiaso.net>
* internal/lume: metrics
Signed-off-by: Xe Iaso <me@xeiaso.net>
* delete old code
Signed-off-by: Xe Iaso <me@xeiaso.net>
* load config into memory
Signed-off-by: Xe Iaso <me@xeiaso.net>
* autogenerate data from dhall config
Signed-off-by: Xe Iaso <me@xeiaso.net>
* various cleanups, import clackset logic
Signed-off-by: Xe Iaso <me@xeiaso.net>
* Update signalboost.dhall (#722)
Added myself, and also fixed someone’s typo
* Add Connor Edwards to signal boost (#721)
* add cache headers
Signed-off-by: Xe Iaso <me@xeiaso.net>
* move command to xesite folder
Signed-off-by: Xe Iaso <me@xeiaso.net>
* xesite: listen for GitHub webhook push events
Signed-off-by: Xe Iaso <me@xeiaso.net>
* xesite: 5 minute timeout for rebuilding the site
Signed-off-by: Xe Iaso <me@xeiaso.net>
* xesite: add rebuild metrics
Signed-off-by: Xe Iaso <me@xeiaso.net>
* xesite: update default variables
Signed-off-by: Xe Iaso <me@xeiaso.net>
* don't commit binaries oops lol
Signed-off-by: Xe Iaso <me@xeiaso.net>
* lume: make search have a light background
Signed-off-by: Xe Iaso <me@xeiaso.net>
* add a notfound page
Signed-off-by: Xe Iaso <me@xeiaso.net>
* fetch info from patreon API
Signed-off-by: Xe Iaso <me@xeiaso.net>
* create contact page
Signed-off-by: Xe Iaso <me@xeiaso.net>
* Toot embedding
Signed-off-by: Xe Iaso <me@xeiaso.net>
* attempt a docker image
Signed-off-by: Xe Iaso <me@xeiaso.net>
* lume: fix deno lock
Signed-off-by: Xe Iaso <me@xeiaso.net>
* add gokrazy post
Signed-off-by: Xe Iaso <me@xeiaso.net>
* cmd/xesite: go up before trying to connect to the saas proxy
Signed-off-by: Xe Iaso <me@xeiaso.net>
* blog: add Sine post/demo
Signed-off-by: Xe Iaso <me@xeiaso.net>
---------
Signed-off-by: Xe Iaso <me@xeiaso.net>
Co-authored-by: bri <284789+b-@users.noreply.github.com>
Co-authored-by: Connor Edwards <38229097+cedws@users.noreply.github.com>
Diffstat (limited to 'lib/jsonfeed/src')
| -rw-r--r-- | lib/jsonfeed/src/builder.rs | 211 | ||||
| -rw-r--r-- | lib/jsonfeed/src/errors.rs | 6 | ||||
| -rw-r--r-- | lib/jsonfeed/src/feed.rs | 286 | ||||
| -rw-r--r-- | lib/jsonfeed/src/item.rs | 529 | ||||
| -rw-r--r-- | lib/jsonfeed/src/lib.rs | 230 |
5 files changed, 0 insertions, 1262 deletions
diff --git a/lib/jsonfeed/src/builder.rs b/lib/jsonfeed/src/builder.rs deleted file mode 100644 index 4ce47a4..0000000 --- a/lib/jsonfeed/src/builder.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::default::Default; - -use errors::*; -use feed::{Attachment, Author, Feed}; -use item::{Content, Item}; - -/// Feed Builder -/// -/// This is used to programmatically build up a Feed object, -/// which can be serialized later into a JSON string -pub struct Builder(Feed); - -impl Builder { - pub fn new() -> Builder { - Builder(Feed::default()) - } - - pub fn title<I: Into<String>>(mut self, t: I) -> Builder { - self.0.title = t.into(); - self - } - - pub fn home_page_url<I: Into<String>>(mut self, url: I) -> Builder { - self.0.home_page_url = Some(url.into()); - self - } - - pub fn feed_url<I: Into<String>>(mut self, url: I) -> Builder { - self.0.feed_url = Some(url.into()); - self - } - - pub fn description<I: Into<String>>(mut self, desc: I) -> Builder { - self.0.description = Some(desc.into()); - self - } - - pub fn user_comment<I: Into<String>>(mut self, cmt: I) -> Builder { - self.0.user_comment = Some(cmt.into()); - self - } - - pub fn next_url<I: Into<String>>(mut self, url: I) -> Builder { - self.0.next_url = Some(url.into()); - self - } - - pub fn icon<I: Into<String>>(mut self, url: I) -> Builder { - self.0.icon = Some(url.into()); - self - } - - pub fn favicon<I: Into<String>>(mut self, url: I) -> Builder { - self.0.favicon = Some(url.into()); - self - } - - pub fn author(mut self, author: Author) -> Builder { - self.0.author = Some(author); - self - } - - pub fn expired(mut self) -> Builder { - self.0.expired = Some(true); - self - } - - pub fn item(mut self, item: Item) -> Builder { - self.0.items.push(item); - self - } - - pub fn build(self) -> Feed { - self.0 - } -} - -/// Builder object for an item in a feed -pub struct ItemBuilder { - pub id: Option<String>, - pub url: Option<String>, - pub external_url: Option<String>, - pub title: Option<String>, - pub content: Option<Content>, - pub summary: Option<String>, - pub image: Option<String>, - pub banner_image: Option<String>, - pub date_published: Option<String>, - pub date_modified: Option<String>, - pub author: Option<Author>, - pub tags: Option<Vec<String>>, - pub attachments: Option<Vec<Attachment>>, - pub xesite_frontmater: Option<xesite_types::Frontmatter>, -} - -impl ItemBuilder { - pub fn new() -> ItemBuilder { - ItemBuilder { - id: None, - url: None, - external_url: None, - title: None, - content: None, - summary: None, - image: None, - banner_image: None, - date_published: None, - date_modified: None, - author: None, - tags: None, - attachments: None, - xesite_frontmater: None, - } - } - - pub fn title<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.title = Some(i.into()); - self - } - - pub fn image<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.image = Some(i.into()); - self - } - - pub fn id<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.id = Some(i.into()); - self - } - - pub fn url<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.url = Some(i.into()); - self - } - - pub fn external_url<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.external_url = Some(i.into()); - self - } - - pub fn date_modified<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.date_modified = Some(i.into()); - self - } - - pub fn date_published<I: Into<String>>(mut self, i: I) -> ItemBuilder { - self.date_published = Some(i.into()); - self - } - - pub fn tags(mut self, tags: Vec<String>) -> ItemBuilder { - self.tags = Some(tags); - self - } - - pub fn author(mut self, who: Author) -> ItemBuilder { - self.author = Some(who); - self - } - - pub fn content_html<I: Into<String>>(mut self, i: I) -> ItemBuilder { - match self.content { - Some(Content::Text(t)) => { - self.content = Some(Content::Both(i.into(), t)); - } - _ => { - self.content = Some(Content::Html(i.into())); - } - } - self - } - - pub fn content_text<I: Into<String>>(mut self, i: I) -> ItemBuilder { - match self.content { - Some(Content::Html(s)) => { - self.content = Some(Content::Both(s, i.into())); - } - _ => { - self.content = Some(Content::Text(i.into())); - } - } - self - } - - pub fn xesite_frontmatter(mut self, fm: xesite_types::Frontmatter) -> ItemBuilder { - self.xesite_frontmater = Some(fm); - self - } - - pub fn build(self) -> Result<Item> { - if self.id.is_none() || self.content.is_none() { - return Err("missing field 'id' or 'content_*'".into()); - } - Ok(Item { - id: self.id.unwrap(), - url: self.url, - external_url: self.external_url, - title: self.title, - content: self.content.unwrap(), - summary: self.summary, - image: self.image, - banner_image: self.banner_image, - date_published: self.date_published, - date_modified: self.date_modified, - author: self.author, - tags: self.tags, - attachments: self.attachments, - xesite_frontmatter: self.xesite_frontmater, - }) - } -} diff --git a/lib/jsonfeed/src/errors.rs b/lib/jsonfeed/src/errors.rs deleted file mode 100644 index b94779c..0000000 --- a/lib/jsonfeed/src/errors.rs +++ /dev/null @@ -1,6 +0,0 @@ -use serde_json; -error_chain! { - foreign_links { - Serde(serde_json::Error); - } -} diff --git a/lib/jsonfeed/src/feed.rs b/lib/jsonfeed/src/feed.rs deleted file mode 100644 index dc0a2b5..0000000 --- a/lib/jsonfeed/src/feed.rs +++ /dev/null @@ -1,286 +0,0 @@ -use std::default::Default; - -use builder::Builder; -use item::Item; - -const VERSION_1: &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 super::*; - use serde_json; - use std::default::Default; - - #[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"); - } -} diff --git a/lib/jsonfeed/src/item.rs b/lib/jsonfeed/src/item.rs deleted file mode 100644 index 49593ab..0000000 --- a/lib/jsonfeed/src/item.rs +++ /dev/null @@ -1,529 +0,0 @@ -use std::default::Default; -use std::fmt; - -use builder::ItemBuilder; -use feed::{Attachment, Author}; - -use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor}; -use serde::ser::{Serialize, SerializeStruct, Serializer}; - -/// Represents the `content_html` and `content_text` attributes of an item -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub enum Content { - Html(String), - Text(String), - Both(String, String), -} - -/// Represents an item in a feed -#[derive(Debug, Clone, PartialEq)] -pub struct Item { - pub id: String, - pub url: Option<String>, - pub external_url: Option<String>, - pub title: Option<String>, - pub content: Content, - pub summary: Option<String>, - pub image: Option<String>, - pub banner_image: Option<String>, - pub date_published: Option<String>, // todo DateTime objects? - pub date_modified: Option<String>, - pub author: Option<Author>, - pub tags: Option<Vec<String>>, - pub attachments: Option<Vec<Attachment>>, - - // xesite extensions - pub xesite_frontmatter: Option<xesite_types::Frontmatter>, -} - -impl Item { - pub fn builder() -> ItemBuilder { - ItemBuilder::new() - } -} - -impl Default for Item { - fn default() -> Item { - Item { - id: "".to_string(), - url: None, - external_url: None, - title: None, - content: Content::Text("".into()), - summary: None, - image: None, - banner_image: None, - date_published: None, - date_modified: None, - author: None, - tags: None, - attachments: None, - xesite_frontmatter: None, - } - } -} - -impl Serialize for Item { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Item", 14)?; - state.serialize_field("id", &self.id)?; - if self.url.is_some() { - state.serialize_field("url", &self.url)?; - } - if self.external_url.is_some() { - state.serialize_field("external_url", &self.external_url)?; - } - if self.title.is_some() { - state.serialize_field("title", &self.title)?; - } - match self.content { - Content::Html(ref s) => { - state.serialize_field("content_html", s)?; - state.serialize_field("content_text", &None::<Option<&str>>)?; - } - Content::Text(ref s) => { - state.serialize_field("content_html", &None::<Option<&str>>)?; - state.serialize_field("content_text", s)?; - } - Content::Both(ref s, ref t) => { - state.serialize_field("content_html", s)?; - state.serialize_field("content_text", t)?; - } - }; - if self.summary.is_some() { - state.serialize_field("summary", &self.summary)?; - } - if self.image.is_some() { - state.serialize_field("image", &self.image)?; - } - if self.banner_image.is_some() { - state.serialize_field("banner_image", &self.banner_image)?; - } - if self.date_published.is_some() { - state.serialize_field("date_published", &self.date_published)?; - } - if self.date_modified.is_some() { - state.serialize_field("date_modified", &self.date_modified)?; - } - if self.author.is_some() { - state.serialize_field("author", &self.author)?; - } - if self.tags.is_some() { - state.serialize_field("tags", &self.tags)?; - } - if self.attachments.is_some() { - state.serialize_field("attachments", &self.attachments)?; - } - if self.xesite_frontmatter.is_some() { - state.serialize_field("_xesite_frontmatter", &self.xesite_frontmatter)?; - } - state.end() - } -} - -impl<'de> Deserialize<'de> for Item { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - enum Field { - Id, - Url, - ExternalUrl, - Title, - ContentHtml, - ContentText, - Summary, - Image, - BannerImage, - DatePublished, - DateModified, - Author, - Tags, - Attachments, - } - - impl<'de> Deserialize<'de> for Field { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - struct FieldVisitor; - - impl<'de> Visitor<'de> for FieldVisitor { - type Value = Field; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("non-expected field") - } - - fn visit_str<E>(self, value: &str) -> Result<Field, E> - where - E: de::Error, - { - match value { - "id" => Ok(Field::Id), - "url" => Ok(Field::Url), - "external_url" => Ok(Field::ExternalUrl), - "title" => Ok(Field::Title), - "content_html" => Ok(Field::ContentHtml), - "content_text" => Ok(Field::ContentText), - "summary" => Ok(Field::Summary), - "image" => Ok(Field::Image), - "banner_image" => Ok(Field::BannerImage), - "date_published" => Ok(Field::DatePublished), - "date_modified" => Ok(Field::DateModified), - "author" => Ok(Field::Author), - "tags" => Ok(Field::Tags), - "attachments" => Ok(Field::Attachments), - _ => Err(de::Error::unknown_field(value, FIELDS)), - } - } - } - - deserializer.deserialize_identifier(FieldVisitor) - } - } - - struct ItemVisitor; - - impl<'de> Visitor<'de> for ItemVisitor { - type Value = Item; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("non-expected thing") - } - - fn visit_map<V>(self, mut map: V) -> Result<Item, V::Error> - where - V: MapAccess<'de>, - { - let mut id = None; - let mut url = None; - let mut external_url = None; - let mut title = None; - let mut content_html: Option<String> = None; - let mut content_text: Option<String> = None; - let mut summary = None; - let mut image = None; - let mut banner_image = None; - let mut date_published = None; - let mut date_modified = None; - let mut author = None; - let mut tags = None; - let mut attachments = None; - - while let Some(key) = map.next_key()? { - match key { - Field::Id => { - if id.is_some() { - return Err(de::Error::duplicate_field("id")); - } - id = Some(map.next_value()?); - } - Field::Url => { - if url.is_some() { - return Err(de::Error::duplicate_field("url")); - } - url = map.next_value()?; - } - Field::ExternalUrl => { - if external_url.is_some() { - return Err(de::Error::duplicate_field("external_url")); - } - external_url = map.next_value()?; - } - Field::Title => { - if title.is_some() { - return Err(de::Error::duplicate_field("title")); - } - title = map.next_value()?; - } - Field::ContentHtml => { - if content_html.is_some() { - return Err(de::Error::duplicate_field("content_html")); - } - content_html = map.next_value()?; - } - Field::ContentText => { - if content_text.is_some() { - return Err(de::Error::duplicate_field("content_text")); - } - content_text = map.next_value()?; - } - Field::Summary => { - if summary.is_some() { - return Err(de::Error::duplicate_field("summary")); - } - summary = map.next_value()?; - } - Field::Image => { - if image.is_some() { - return Err(de::Error::duplicate_field("image")); - } - image = map.next_value()?; - } - Field::BannerImage => { - if banner_image.is_some() { - return Err(de::Error::duplicate_field("banner_image")); - } - banner_image = map.next_value()?; - } - Field::DatePublished => { - if date_published.is_some() { - return Err(de::Error::duplicate_field("date_published")); - } - date_published = map.next_value()?; - } - Field::DateModified => { - if date_modified.is_some() { - return Err(de::Error::duplicate_field("date_modified")); - } - date_modified = map.next_value()?; - } - Field::Author => { - if author.is_some() { - return Err(de::Error::duplicate_field("author")); - } - author = map.next_value()?; - } - Field::Tags => { - if tags.is_some() { - return Err(de::Error::duplicate_field("tags")); - } - tags = map.next_value()?; - } - Field::Attachments => { - if attachments.is_some() { - return Err(de::Error::duplicate_field("attachments")); - } - attachments = map.next_value()?; - } - } - } - - let id = id.ok_or_else(|| de::Error::missing_field("id"))?; - let content = match (content_html, content_text) { - (Some(s), Some(t)) => Content::Both(s, t), - (Some(s), _) => Content::Html(s), - (_, Some(t)) => Content::Text(t), - _ => return Err(de::Error::missing_field("content_html or content_text")), - }; - - Ok(Item { - id, - url, - external_url, - title, - content, - summary, - image, - banner_image, - date_published, - date_modified, - author, - tags, - attachments, - xesite_frontmatter: None, - }) - } - } - - const FIELDS: &[&str] = &[ - "id", - "url", - "external_url", - "title", - "content", - "summary", - "image", - "banner_image", - "date_published", - "date_modified", - "author", - "tags", - "attachments", - ]; - deserializer.deserialize_struct("Item", FIELDS, ItemVisitor) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use feed::Author; - use serde_json; - - #[test] - #[allow(non_snake_case)] - fn serialize_item__content_html() { - let item = Item { - id: "1".into(), - url: Some("http://example.com/feed.json".into()), - external_url: Some("http://example.com/feed.json".into()), - title: Some("feed title".into()), - content: Content::Html("<p>content</p>".into()), - summary: Some("feed summary".into()), - image: Some("http://img.com/blah".into()), - banner_image: Some("http://img.com/blah".into()), - date_published: Some("2017-01-01 10:00:00".into()), - date_modified: Some("2017-01-01 10:00:00".into()), - author: Some( - Author::new() - .name("bob jones") - .url("http://example.com") - .avatar("http://img.com/blah"), - ), - tags: Some(vec!["json".into(), "feed".into()]), - attachments: Some(vec![]), - }; - assert_eq!( - serde_json::to_string(&item).unwrap(), - r#"{"id":"1","url":"http://example.com/feed.json","external_url":"http://example.com/feed.json","title":"feed title","content_html":"<p>content</p>","content_text":null,"summary":"feed summary","image":"http://img.com/blah","banner_image":"http://img.com/blah","date_published":"2017-01-01 10:00:00","date_modified":"2017-01-01 10:00:00","author":{"name":"bob jones","url":"http://example.com","avatar":"http://img.com/blah"},"tags":["json","feed"],"attachments":[]}"# - ); - } - - #[test] - #[allow(non_snake_case)] - fn serialize_item__content_text() { - let item = Item { - id: "1".into(), - url: Some("http://example.com/feed.json".into()), - external_url: Some("http://example.com/feed.json".into()), - title: Some("feed title".into()), - content: Content::Text("content".into()), - summary: Some("feed summary".into()), - image: Some("http://img.com/blah".into()), - banner_image: Some("http://img.com/blah".into()), - date_published: Some("2017-01-01 10:00:00".into()), - date_modified: Some("2017-01-01 10:00:00".into()), - author: Some( - Author::new() - .name("bob jones") - .url("http://example.com") - .avatar("http://img.com/blah"), - ), - tags: Some(vec!["json".into(), "feed".into()]), - attachments: Some(vec![]), - |
