From 1e251178879940f10e17d910d3f65efd986ce055 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Sun, 25 Feb 2018 19:25:15 +0100 Subject: [PATCH] Fix date serializing in extra section of pages Closes #238 --- CHANGELOG.md | 2 + Cargo.lock | 22 +++--- components/content/src/page.rs | 7 +- components/content/src/sorting.rs | 5 +- components/front_matter/src/page.rs | 108 +++++++++++++++++++++++++--- 5 files changed, 113 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b9676ff..4498f506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## 0.3.2 (unreleased) - Fix `serve` command trying to read all files as markdown +- Add many syntax highlighting themes +- Fix date being serialised incorrectly in page `extra` section of front-matter ## 0.3.1 (2018-02-15) diff --git a/Cargo.lock b/Cargo.lock index d4b61d73..f6feb5ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,7 +192,7 @@ dependencies = [ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -272,7 +272,7 @@ name = "errors" version = "0.1.0" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -310,7 +310,7 @@ dependencies = [ "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -794,7 +794,7 @@ dependencies = [ "front_matter 0.1.0", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -1000,7 +1000,7 @@ dependencies = [ "slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "templates 0.1.0", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -1123,7 +1123,7 @@ dependencies = [ "taxonomies 0.1.0", "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "templates 0.1.0", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1211,7 +1211,7 @@ dependencies = [ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -1235,13 +1235,13 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "taxonomies 0.1.0", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] [[package]] name = "tera" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1411,7 +1411,7 @@ version = "0.1.0" dependencies = [ "errors 0.1.0", "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1638,7 +1638,7 @@ dependencies = [ "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum syntect 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9fffcb25a761118df53811bd1cfcd54cf57fcbc51e1ea3167ae263477129ad" "checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e" -"checksum tera 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d5fcce006fbde2ac587dc056940d2c329ee49f2e4de7d9ddfdbbc60efb9f9207" +"checksum tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1a35d04c2444875b1319293fbc72c00215ae6220f8c70f9f14fefa5eaae0c6" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum term-painter 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "dcaa948f0e3e38470cd8dc8dcfe561a75c9e43f28075bb183845be2b9b3c08cf" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" diff --git a/components/content/src/page.rs b/components/content/src/page.rs index b49235cf..220a0198 100644 --- a/components/content/src/page.rs +++ b/components/content/src/page.rs @@ -223,12 +223,7 @@ impl ser::Serialize for Page { state.serialize_field("content", &self.content)?; state.serialize_field("title", &self.meta.title)?; state.serialize_field("description", &self.meta.description)?; - // From a TOML datetime to a String first - let date = match self.meta.date { - Some(ref d) => Some(d.to_string()), - None => None, - }; - state.serialize_field("date", &date)?; + state.serialize_field("date", &self.meta.date)?; state.serialize_field("slug", &self.slug)?; state.serialize_field("path", &self.path)?; state.serialize_field("components", &self.components)?; diff --git a/components/content/src/sorting.rs b/components/content/src/sorting.rs index eec12044..95ea20b0 100644 --- a/components/content/src/sorting.rs +++ b/components/content/src/sorting.rs @@ -98,16 +98,13 @@ pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec { #[cfg(test)] mod tests { - use std::str::FromStr; - use toml::value::Datetime; - use front_matter::{PageFrontMatter, SortBy}; use page::Page; use super::{sort_pages, populate_previous_and_next_pages}; fn create_page_with_date(date: &str) -> Page { let mut front_matter = PageFrontMatter::default(); - front_matter.date = Some(Datetime::from_str(date).unwrap()); + front_matter.date = Some(date.to_string()); Page::new("content/hello.md", front_matter) } diff --git a/components/front_matter/src/page.rs b/components/front_matter/src/page.rs index 26b0d4a9..ca2c95ac 100644 --- a/components/front_matter/src/page.rs +++ b/components/front_matter/src/page.rs @@ -1,12 +1,65 @@ -use std::collections::HashMap; +use std::result::{Result as StdResult}; use chrono::prelude::*; -use tera::Value; +use tera::{Map, Value}; +use serde::{Deserialize, Deserializer}; use toml; use errors::Result; +fn from_toml_datetime<'de, D>(deserializer: D) -> StdResult, D::Error> + where + D: Deserializer<'de>, +{ + toml::value::Datetime::deserialize(deserializer) + .map(|s| Some(s.to_string())) +} + +/// Returns key/value for a converted date from TOML. +/// If the table itself is the TOML struct, only return its value without the key +fn convert_toml_date(table: Map) -> Value { + let mut new = Map::new(); + + for (k, v) in table.into_iter() { + if k == "$__toml_private_datetime" { + return v; + } + + match v { + Value::Object(mut o) => { + // that was a toml datetime object, just return the date + if let Some(toml_date) = o.remove("$__toml_private_datetime") { + new.insert(k, toml_date); + return Value::Object(new); + } + new.insert(k, convert_toml_date(o)); + }, + _ => { new.insert(k, v); } + } + } + + Value::Object(new) +} + +/// TOML datetimes will be serialized as a struct but we want the +/// stringified version for json, otherwise they are going to be weird +fn fix_toml_dates(table: Map) -> Value { + let mut new = Map::new(); + + for (key, value) in table { + match value { + Value::Object(mut o) => { + new.insert(key, convert_toml_date(o)); + }, + _ => { new.insert(key, value); }, + } + } + + Value::Object(new) +} + + /// The front matter of every page #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PageFrontMatter { @@ -15,7 +68,8 @@ pub struct PageFrontMatter { /// Description in that appears when linked, e.g. on twitter pub description: Option, /// Date if we want to order pages (ie blog post) - pub date: Option, + #[serde(default, deserialize_with = "from_toml_datetime")] + pub date: Option, /// Whether this page is a draft and should be ignored for pagination etc pub draft: Option, /// The page slug. Will be used instead of the filename if present @@ -41,12 +95,13 @@ pub struct PageFrontMatter { #[serde(skip_serializing)] pub template: Option, /// Any extra parameter present in the front matter - pub extra: Option>, + #[serde(default)] + pub extra: Map, } impl PageFrontMatter { pub fn parse(toml: &str) -> Result { - let f: PageFrontMatter = match toml::from_str(toml) { + let mut f: PageFrontMatter = match toml::from_str(toml) { Ok(d) => d, Err(e) => bail!(e), }; @@ -69,17 +124,20 @@ impl PageFrontMatter { } } + f.extra = match fix_toml_dates(f.extra) { + Value::Object(o) => o, + _ => unreachable!("Got something other than a table in page extra"), + }; Ok(f) } /// Converts the TOML datetime to a Chrono naive datetime pub fn date(&self) -> Option { if let Some(ref d) = self.date { - let d2 = d.to_string(); - if d2.contains('T') { - DateTime::parse_from_rfc3339(&d2).ok().and_then(|s| Some(s.naive_local())) + if d.contains('T') { + DateTime::parse_from_rfc3339(&d).ok().and_then(|s| Some(s.naive_local())) } else { - NaiveDate::parse_from_str(&d2, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0, 0, 0))) + NaiveDate::parse_from_str(&d, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0, 0, 0))) } } else { None @@ -117,7 +175,7 @@ impl Default for PageFrontMatter { weight: None, aliases: None, template: None, - extra: None, + extra: Map::new(), } } } @@ -125,12 +183,14 @@ impl Default for PageFrontMatter { #[cfg(test)] mod tests { + use tera::to_value; use super::PageFrontMatter; #[test] fn can_have_empty_front_matter() { let content = r#" "#; let res = PageFrontMatter::parse(content); + println!("{:?}", res); assert!(res.is_ok()); } @@ -251,4 +311,32 @@ mod tests { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } + + #[test] + fn can_parse_dates_in_extra() { + let content = r#" + title = "Hello" + description = "hey there" + + [extra] + some-date = 2002-14-01"#; + let res = PageFrontMatter::parse(content); + println!("{:?}", res); + assert!(res.is_ok()); + assert_eq!(res.unwrap().extra["some-date"], to_value("2002-14-01").unwrap()); + } + + #[test] + fn can_parse_nested_dates_in_extra() { + let content = r#" + title = "Hello" + description = "hey there" + + [extra.something] + some-date = 2002-14-01"#; + let res = PageFrontMatter::parse(content); + println!("{:?}", res); + assert!(res.is_ok()); + assert_eq!(res.unwrap().extra["something"]["some-date"], to_value("2002-14-01").unwrap()); + } }