aboutsummaryrefslogtreecommitdiff
path: root/lib/jsonfeed/src
diff options
context:
space:
mode:
authorXe Iaso <me@christine.website>2023-09-30 10:36:37 -0400
committerGitHub <noreply@github.com>2023-09-30 10:36:37 -0400
commitac6a3df0d18cc73524c0096d954a57d24cad5669 (patch)
tree81474177d730440657f490ae29892d62392251ea /lib/jsonfeed/src
parentcbdea8ba3fca9a663778af71f8df5965aeb6c090 (diff)
downloadxesite-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.rs211
-rw-r--r--lib/jsonfeed/src/errors.rs6
-rw-r--r--lib/jsonfeed/src/feed.rs286
-rw-r--r--lib/jsonfeed/src/item.rs529
-rw-r--r--lib/jsonfeed/src/lib.rs230
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![]),
-