diff --git a/src/front_matter.rs b/src/front_matter.rs
deleted file mode 100644
index 1c3a82d..0000000
--- a/src/front_matter.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-use std::collections::HashMap;
-use std::path::Path;
-
-use toml;
-use tera::Value;
-use chrono::prelude::*;
-use regex::Regex;
-
-
-use errors::{Result, ResultExt};
-
-
-lazy_static! {
- static ref PAGE_RE: Regex = Regex::new(r"^\r?\n?\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap();
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
-#[serde(rename_all = "lowercase")]
-pub enum SortBy {
- Date,
- Order,
- None,
-}
-
-/// The front matter of every page
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct FrontMatter {
- ///
of the page
- pub title: Option,
- /// 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,
- /// The page slug. Will be used instead of the filename if present
- /// Can't be an empty string if present
- pub slug: Option,
- /// The url the page appears at, overrides the slug if set in the front-matter
- /// otherwise is set after parsing front matter and sections
- /// Can't be an empty string if present
- pub url: Option,
- /// Tags, not to be confused with categories
- pub tags: Option>,
- /// Whether this page is a draft and should be published or not
- pub draft: Option,
- /// Only one category allowed
- pub category: Option,
- /// Whether to sort by "date", "order" or "none". Defaults to `none`.
- #[serde(skip_serializing)]
- pub sort_by: Option,
- /// Integer to use to order content. Lowest is at the bottom, highest first
- pub order: Option,
- /// Optional template, if we want to specify which template to render for that page
- #[serde(skip_serializing)]
- pub template: Option,
- /// How many pages to be displayed per paginated page. No pagination will happen if this isn't set
- #[serde(skip_serializing)]
- pub paginate_by: Option,
- /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
- #[serde(skip_serializing)]
- pub paginate_path: Option,
- /// Whether to render that page/section or not. Defaults to `true`.
- #[serde(skip_serializing)]
- pub render: Option,
- /// Any extra parameter present in the front matter
- pub extra: Option>,
-}
-
-impl FrontMatter {
- pub fn parse(toml: &str) -> Result {
- let mut f: FrontMatter = match toml::from_str(toml) {
- Ok(d) => d,
- Err(e) => bail!(e),
- };
-
- if let Some(ref slug) = f.slug {
- if slug == "" {
- bail!("`slug` can't be empty if present")
- }
- }
-
- if let Some(ref url) = f.url {
- if url == "" {
- bail!("`url` can't be empty if present")
- }
- }
-
- if f.paginate_path.is_none() {
- f.paginate_path = Some("page".to_string());
- }
-
- if f.render.is_none() {
- f.render = Some(true);
- }
-
- Ok(f)
- }
-
- /// Converts the date in the front matter, which can be in 2 formats, into a NaiveDateTime
- pub fn date(&self) -> Option {
- match self.date {
- Some(ref d) => {
- if d.contains('T') {
- DateTime::parse_from_rfc3339(d).ok().and_then(|s| Some(s.naive_local()))
- } else {
- NaiveDate::parse_from_str(d, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0,0,0)))
- }
- },
- None => None,
- }
- }
-
- pub fn order(&self) -> usize {
- self.order.unwrap()
- }
-
- /// Returns the current sorting method, defaults to `None` (== no sorting)
- pub fn sort_by(&self) -> SortBy {
- match self.sort_by {
- Some(ref s) => *s,
- None => SortBy::None,
- }
- }
-
- /// Only applies to section, whether it is paginated or not.
- pub fn is_paginated(&self) -> bool {
- match self.paginate_by {
- Some(v) => v > 0,
- None => false
- }
- }
-
- pub fn should_render(&self) -> bool {
- self.render.unwrap()
- }
-}
-
-impl Default for FrontMatter {
- fn default() -> FrontMatter {
- FrontMatter {
- title: None,
- description: None,
- date: None,
- slug: None,
- url: None,
- tags: None,
- draft: None,
- category: None,
- sort_by: None,
- order: None,
- template: None,
- paginate_by: None,
- paginate_path: Some("page".to_string()),
- render: Some(true),
- extra: None,
- }
- }
-}
-
-/// Split a file between the front matter and its content
-/// It will parse the front matter as well and returns any error encountered
-pub fn split_content(file_path: &Path, content: &str) -> Result<(FrontMatter, String)> {
- if !PAGE_RE.is_match(content) {
- bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", file_path.to_string_lossy());
- }
-
- // 2. extract the front matter and the content
- let caps = PAGE_RE.captures(content).unwrap();
- // caps[0] is the full match
- let front_matter = &caps[1];
- let content = &caps[2];
-
- // 3. create our page, parse front matter and assign all of that
- let meta = FrontMatter::parse(front_matter)
- .chain_err(|| format!("Error when parsing front matter of file `{}`", file_path.to_string_lossy()))?;
-
- Ok((meta, content.to_string()))
-}
diff --git a/src/front_matter/mod.rs b/src/front_matter/mod.rs
new file mode 100644
index 0000000..7e20a93
--- /dev/null
+++ b/src/front_matter/mod.rs
@@ -0,0 +1,122 @@
+use std::path::Path;
+
+use regex::Regex;
+
+use errors::{Result, ResultExt};
+
+mod page;
+mod section;
+
+pub use self::page::PageFrontMatter;
+pub use self::section::{SectionFrontMatter, SortBy};
+
+lazy_static! {
+ static ref PAGE_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap();
+}
+
+/// Split a file between the front matter and its content
+/// Will return an error if the front matter wasn't found
+fn split_content(file_path: &Path, content: &str) -> Result<(String, String)> {
+ if !PAGE_RE.is_match(content) {
+ bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", file_path.to_string_lossy());
+ }
+
+ // 2. extract the front matter and the content
+ let caps = PAGE_RE.captures(content).unwrap();
+ // caps[0] is the full match
+ // caps[1] => front matter
+ // caps[2] => content
+ Ok((caps[1].to_string(), caps[2].to_string()))
+}
+
+/// Split a file between the front matter and its content.
+/// Returns a parsed SectionFrontMatter and the rest of the content
+pub fn split_section_content(file_path: &Path, content: &str) -> Result<(SectionFrontMatter, String)> {
+ let (front_matter, content) = split_content(file_path, content)?;
+ let meta = SectionFrontMatter::parse(&front_matter)
+ .chain_err(|| format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()))?;
+ Ok((meta, content))
+}
+
+/// Split a file between the front matter and its content
+/// Returns a parsed PageFrontMatter and the rest of the content
+pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> {
+ let (front_matter, content) = split_content(file_path, content)?;
+ let meta = PageFrontMatter::parse(&front_matter)
+ .chain_err(|| format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()))?;
+ Ok((meta, content))
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::Path;
+
+ use super::{split_section_content, split_page_content};
+
+ #[test]
+ fn can_split_page_content_valid() {
+ let content = r#"
++++
+title = "Title"
+description = "hey there"
+date = "2002/10/12"
++++
+Hello
+"#;
+ let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
+ assert_eq!(content, "Hello\n");
+ assert_eq!(front_matter.title.unwrap(), "Title");
+ }
+
+ #[test]
+ fn can_split_section_content_valid() {
+ let content = r#"
++++
+paginate_by = 10
++++
+Hello
+"#;
+ let (front_matter, content) = split_section_content(Path::new(""), content).unwrap();
+ assert_eq!(content, "Hello\n");
+ assert!(front_matter.is_paginated());
+ }
+
+ #[test]
+ fn can_split_content_with_only_frontmatter_valid() {
+ let content = r#"
++++
+title = "Title"
+description = "hey there"
+date = "2002/10/12"
++++"#;
+ let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
+ assert_eq!(content, "");
+ assert_eq!(front_matter.title.unwrap(), "Title");
+ }
+
+ #[test]
+ fn can_split_content_lazily() {
+ let content = r#"
++++
+title = "Title"
+description = "hey there"
+date = "2002-10-02T15:00:00Z"
++++
++++"#;
+ let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
+ assert_eq!(content, "+++");
+ assert_eq!(front_matter.title.unwrap(), "Title");
+ }
+
+ #[test]
+ fn errors_if_cannot_locate_frontmatter() {
+ let content = r#"
++++
+title = "Title"
+description = "hey there"
+date = "2002/10/12""#;
+ let res = split_page_content(Path::new(""), content);
+ assert!(res.is_err());
+ }
+
+}
diff --git a/src/front_matter/page.rs b/src/front_matter/page.rs
new file mode 100644
index 0000000..3596b42
--- /dev/null
+++ b/src/front_matter/page.rs
@@ -0,0 +1,206 @@
+use std::collections::HashMap;
+
+use chrono::prelude::*;
+use tera::Value;
+use toml;
+
+use errors::{Result};
+
+/// The front matter of every page
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct PageFrontMatter {
+ /// of the page
+ pub title: Option,
+ /// 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,
+ /// The page slug. Will be used instead of the filename if present
+ /// Can't be an empty string if present
+ pub slug: Option,
+ /// The url the page appears at, overrides the slug if set in the front-matter
+ /// otherwise is set after parsing front matter and sections
+ /// Can't be an empty string if present
+ pub url: Option,
+ /// Tags, not to be confused with categories
+ pub tags: Option>,
+ /// Whether this page is a draft and should be published or not
+ pub draft: Option,
+ /// Only one category allowed
+ pub category: Option,
+ /// Integer to use to order content. Lowest is at the bottom, highest first
+ pub order: Option,
+ /// Optional template, if we want to specify which template to render for that page
+ #[serde(skip_serializing)]
+ pub template: Option,
+ /// Any extra parameter present in the front matter
+ pub extra: Option>,
+}
+
+impl PageFrontMatter {
+ pub fn parse(toml: &str) -> Result {
+ let f: PageFrontMatter = match toml::from_str(toml) {
+ Ok(d) => d,
+ Err(e) => bail!(e),
+ };
+
+ if let Some(ref slug) = f.slug {
+ if slug == "" {
+ bail!("`slug` can't be empty if present")
+ }
+ }
+
+ if let Some(ref url) = f.url {
+ if url == "" {
+ bail!("`url` can't be empty if present")
+ }
+ }
+
+ Ok(f)
+ }
+
+ /// Converts the date in the front matter, which can be in 2 formats, into a NaiveDateTime
+ pub fn date(&self) -> Option {
+ match self.date {
+ Some(ref d) => {
+ if d.contains('T') {
+ DateTime::parse_from_rfc3339(d).ok().and_then(|s| Some(s.naive_local()))
+ } else {
+ NaiveDate::parse_from_str(d, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0,0,0)))
+ }
+ },
+ None => None,
+ }
+ }
+
+ pub fn order(&self) -> usize {
+ self.order.unwrap()
+ }
+}
+
+impl Default for PageFrontMatter {
+ fn default() -> PageFrontMatter {
+ PageFrontMatter {
+ title: None,
+ description: None,
+ date: None,
+ slug: None,
+ url: None,
+ tags: None,
+ draft: None,
+ category: None,
+ order: None,
+ template: None,
+ extra: None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::PageFrontMatter;
+
+ #[test]
+ fn can_have_empty_front_matter() {
+ let content = r#" "#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_ok());
+ }
+
+ #[test]
+ fn can_parse_valid_front_matter() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there""#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_ok());
+ let res = res.unwrap();
+ assert_eq!(res.title.unwrap(), "Hello".to_string());
+ assert_eq!(res.description.unwrap(), "hey there".to_string())
+ }
+
+ #[test]
+ fn can_parse_tags() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ slug = "hello-world"
+ tags = ["rust", "html"]"#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_ok());
+ let res = res.unwrap();
+
+ assert_eq!(res.title.unwrap(), "Hello".to_string());
+ assert_eq!(res.slug.unwrap(), "hello-world".to_string());
+ assert_eq!(res.tags.unwrap(), ["rust".to_string(), "html".to_string()]);
+ }
+
+ #[test]
+ fn errors_with_invalid_front_matter() {
+ let content = r#"title = 1\n"#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn errors_on_non_string_tag() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ slug = "hello-world"
+ tags = ["rust", 1]"#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn errors_on_present_but_empty_slug() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ slug = """#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn errors_on_present_but_empty_url() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ url = """#;
+ let res = PageFrontMatter::parse(content);
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn can_parse_date_yyyy_mm_dd() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ date = "2016-10-10""#;
+ let res = PageFrontMatter::parse(content).unwrap();
+ assert!(res.date().is_some());
+ }
+
+ #[test]
+ fn can_parse_date_rfc3339() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ date = "2002-10-02T15:00:00Z""#;
+ let res = PageFrontMatter::parse(content).unwrap();
+ assert!(res.date().is_some());
+ }
+
+ #[test]
+ fn cannot_parse_random_date_format() {
+ let content = r#"
+ title = "Hello"
+ description = "hey there"
+ date = "2002/10/12""#;
+ let res = PageFrontMatter::parse(content).unwrap();
+ assert!(res.date().is_none());
+ }
+
+}
diff --git a/src/front_matter/section.rs b/src/front_matter/section.rs
new file mode 100644
index 0000000..1e4fd13
--- /dev/null
+++ b/src/front_matter/section.rs
@@ -0,0 +1,99 @@
+use std::collections::HashMap;
+
+use tera::Value;
+use toml;
+
+use errors::{Result};
+
+static DEFAULT_PAGINATE_PATH: &'static str = "page";
+
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum SortBy {
+ Date,
+ Order,
+ None,
+}
+
+/// The front matter of every section
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct SectionFrontMatter {
+ /// of the page
+ pub title: Option,
+ /// Description in that appears when linked, e.g. on twitter
+ pub description: Option,
+ /// Whether to sort by "date", "order" or "none". Defaults to `none`.
+ #[serde(skip_serializing)]
+ pub sort_by: Option,
+ /// Optional template, if we want to specify which template to render for that page
+ #[serde(skip_serializing)]
+ pub template: Option,
+ /// How many pages to be displayed per paginated page. No pagination will happen if this isn't set
+ #[serde(skip_serializing)]
+ pub paginate_by: Option,
+ /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
+ #[serde(skip_serializing)]
+ pub paginate_path: Option,
+ /// Whether to render that section or not. Defaults to `true`.
+ /// Useful when the section is only there to organize things but is not meant
+ /// to be used directly, like a posts section in a personal site
+ #[serde(skip_serializing)]
+ pub render: Option,
+ /// Any extra parameter present in the front matter
+ pub extra: Option>,
+}
+
+impl SectionFrontMatter {
+ pub fn parse(toml: &str) -> Result {
+ let mut f: SectionFrontMatter = match toml::from_str(toml) {
+ Ok(d) => d,
+ Err(e) => bail!(e),
+ };
+
+ if f.paginate_path.is_none() {
+ f.paginate_path = Some(DEFAULT_PAGINATE_PATH.to_string());
+ }
+
+ if f.render.is_none() {
+ f.render = Some(true);
+ }
+
+ if f.sort_by.is_none() {
+ f.sort_by = Some(SortBy::None);
+ }
+
+ Ok(f)
+ }
+
+ /// Returns the current sorting method, defaults to `None` (== no sorting)
+ pub fn sort_by(&self) -> SortBy {
+ self.sort_by.unwrap()
+ }
+
+ /// Only applies to section, whether it is paginated or not.
+ pub fn is_paginated(&self) -> bool {
+ match self.paginate_by {
+ Some(v) => v > 0,
+ None => false
+ }
+ }
+
+ pub fn should_render(&self) -> bool {
+ self.render.unwrap()
+ }
+}
+
+impl Default for SectionFrontMatter {
+ fn default() -> SectionFrontMatter {
+ SectionFrontMatter {
+ title: None,
+ description: None,
+ sort_by: None,
+ template: None,
+ paginate_by: None,
+ paginate_path: Some(DEFAULT_PAGINATE_PATH.to_string()),
+ render: Some(true),
+ extra: None,
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index a84a891..80fc6e1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,7 +33,7 @@ mod templates;
pub use site::{Site};
pub use config::{Config, get_config};
-pub use front_matter::{FrontMatter, split_content, SortBy};
+pub use front_matter::{PageFrontMatter, SectionFrontMatter, split_page_content, split_section_content, SortBy};
pub use page::{Page, populate_previous_and_next_pages};
pub use section::{Section};
pub use utils::{create_file};
diff --git a/src/page.rs b/src/page.rs
index c63f63c..49384b3 100644
--- a/src/page.rs
+++ b/src/page.rs
@@ -11,7 +11,7 @@ use slug::slugify;
use errors::{Result, ResultExt};
use config::Config;
-use front_matter::{FrontMatter, SortBy, split_content};
+use front_matter::{PageFrontMatter, SortBy, split_page_content};
use markdown::markdown_to_html;
use utils::{read_file, find_content_components};
@@ -41,6 +41,8 @@ fn find_related_assets(path: &Path) -> Vec {
#[derive(Clone, Debug, PartialEq)]
pub struct Page {
+ /// The front matter meta-data
+ pub meta: PageFrontMatter,
/// The .md path
pub file_path: PathBuf,
/// The .md path, starting from the content directory, with / slashes
@@ -60,8 +62,6 @@ pub struct Page {
pub assets: Vec,
/// The HTML rendered of the page
pub content: String,
- /// The front matter meta-data
- pub meta: FrontMatter,
/// The slug of that page.
/// First tries to find the slug in the meta and defaults to filename otherwise
@@ -83,8 +83,9 @@ pub struct Page {
impl Page {
- pub fn new(meta: FrontMatter) -> Page {
+ pub fn new(meta: PageFrontMatter) -> Page {
Page {
+ meta: meta,
file_path: PathBuf::new(),
relative_path: String::new(),
parent_path: PathBuf::new(),
@@ -97,7 +98,6 @@ impl Page {
path: "".to_string(),
permalink: "".to_string(),
summary: None,
- meta: meta,
previous: None,
next: None,
}
@@ -122,7 +122,7 @@ impl Page {
/// erroneous
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result {
// 1. separate front matter from content
- let (meta, content) = split_content(file_path, content)?;
+ let (meta, content) = split_page_content(file_path, content)?;
let mut page = Page::new(meta);
page.file_path = file_path.to_path_buf();
page.parent_path = page.file_path.parent().unwrap().to_path_buf();
@@ -217,6 +217,28 @@ impl Page {
}
}
+impl Default for Page {
+ fn default() -> Page {
+ Page {
+ meta: PageFrontMatter::default(),
+ file_path: PathBuf::new(),
+ relative_path: String::new(),
+ parent_path: PathBuf::new(),
+ file_name: "".to_string(),
+ components: vec![],
+ raw_content: "".to_string(),
+ assets: vec![],
+ content: "".to_string(),
+ slug: "".to_string(),
+ path: "".to_string(),
+ permalink: "".to_string(),
+ summary: None,
+ previous: None,
+ next: None,
+ }
+ }
+}
+
impl ser::Serialize for Page {
fn serialize(&self, serializer: S) -> StdResult where S: ser::Serializer {
let mut state = serializer.serialize_struct("page", 16)?;
@@ -318,17 +340,17 @@ mod tests {
use std::fs::File;
- use front_matter::{FrontMatter, SortBy};
+ use front_matter::{PageFrontMatter, SortBy};
use super::{Page, find_related_assets, sort_pages, populate_previous_and_next_pages};
fn create_page_with_date(date: &str) -> Page {
- let mut front_matter = FrontMatter::default();
+ let mut front_matter = PageFrontMatter::default();
front_matter.date = Some(date.to_string());
Page::new(front_matter)
}
fn create_page_with_order(order: usize) -> Page {
- let mut front_matter = FrontMatter::default();
+ let mut front_matter = PageFrontMatter::default();
front_matter.order = Some(order);
Page::new(front_matter)
}
diff --git a/src/pagination.rs b/src/pagination.rs
index b348137..483fad8 100644
--- a/src/pagination.rs
+++ b/src/pagination.rs
@@ -154,14 +154,14 @@ impl<'a> Paginator<'a> {
mod tests {
use tera::{to_value};
- use front_matter::FrontMatter;
+ use front_matter::SectionFrontMatter;
use page::Page;
use section::Section;
use super::{Paginator};
fn create_section(is_index: bool) -> Section {
- let mut f = FrontMatter::default();
+ let mut f = SectionFrontMatter::default();
f.paginate_by = Some(2);
f.paginate_path = Some("page".to_string());
let mut s = Section::new("content/_index.md", f);
@@ -178,9 +178,9 @@ mod tests {
#[test]
fn test_can_create_paginator() {
let pages = vec![
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
+ Page::default(),
+ Page::default(),
+ Page::default(),
];
let section = create_section(false);
let paginator = Paginator::new(pages.as_slice(), §ion);
@@ -200,9 +200,9 @@ mod tests {
#[test]
fn test_can_create_paginator_for_index() {
let pages = vec![
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
+ Page::default(),
+ Page::default(),
+ Page::default(),
];
let section = create_section(true);
let paginator = Paginator::new(pages.as_slice(), §ion);
@@ -222,9 +222,9 @@ mod tests {
#[test]
fn test_can_build_paginator_context() {
let pages = vec![
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
- Page::new(FrontMatter::default()),
+ Page::default(),
+ Page::default(),
+ Page::default(),
];
let section = create_section(false);
let paginator = Paginator::new(pages.as_slice(), §ion);
diff --git a/src/section.rs b/src/section.rs
index 1d261c3..e8a408f 100644
--- a/src/section.rs
+++ b/src/section.rs
@@ -6,7 +6,7 @@ use tera::{Tera, Context};
use serde::ser::{SerializeStruct, self};
use config::Config;
-use front_matter::{FrontMatter, split_content};
+use front_matter::{SectionFrontMatter, split_section_content};
use errors::{Result, ResultExt};
use utils::{read_file, find_content_components};
use markdown::markdown_to_html;
@@ -15,6 +15,8 @@ use page::{Page};
#[derive(Clone, Debug, PartialEq)]
pub struct Section {
+ /// The front matter meta-data
+ pub meta: SectionFrontMatter,
/// The _index.md full path
pub file_path: PathBuf,
/// The .md path, starting from the content directory, with / slashes
@@ -31,8 +33,6 @@ pub struct Section {
pub raw_content: String,
/// The HTML rendered of the page
pub content: String,
- /// The front matter meta-data
- pub meta: FrontMatter,
/// All direct pages of that section
pub pages: Vec,
/// All pages that cannot be sorted in this section
@@ -42,10 +42,11 @@ pub struct Section {
}
impl Section {
- pub fn new>(file_path: P, meta: FrontMatter) -> Section {
+ pub fn new>(file_path: P, meta: SectionFrontMatter) -> Section {
let file_path = file_path.as_ref();
Section {
+ meta: meta,
file_path: file_path.to_path_buf(),
relative_path: "".to_string(),
parent_path: file_path.parent().unwrap().to_path_buf(),
@@ -54,7 +55,6 @@ impl Section {
permalink: "".to_string(),
raw_content: "".to_string(),
content: "".to_string(),
- meta: meta,
pages: vec![],
ignored_pages: vec![],
subsections: vec![],
@@ -62,7 +62,7 @@ impl Section {
}
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result {
- let (meta, content) = split_content(file_path, content)?;
+ let (meta, content) = split_section_content(file_path, content)?;
let mut section = Section::new(file_path, meta);
section.raw_content = content.clone();
section.components = find_content_components(§ion.file_path);
@@ -154,6 +154,7 @@ impl Default for Section {
/// Used to create a default index section if there is no _index.md in the root content directory
fn default() -> Section {
Section {
+ meta: SectionFrontMatter::default(),
file_path: PathBuf::new(),
relative_path: "".to_string(),
parent_path: PathBuf::new(),
@@ -162,7 +163,6 @@ impl Default for Section {
permalink: "".to_string(),
raw_content: "".to_string(),
content: "".to_string(),
- meta: FrontMatter::default(),
pages: vec![],
ignored_pages: vec![],
subsections: vec![],
diff --git a/tests/front_matter.rs b/tests/front_matter.rs
deleted file mode 100644
index cb09953..0000000
--- a/tests/front_matter.rs
+++ /dev/null
@@ -1,236 +0,0 @@
-extern crate gutenberg;
-extern crate tera;
-
-use std::path::Path;
-
-use gutenberg::{FrontMatter, split_content, SortBy};
-use tera::to_value;
-
-
-#[test]
-fn test_can_parse_a_valid_front_matter() {
- let content = r#"
-title = "Hello"
-description = "hey there""#;
- let res = FrontMatter::parse(content);
- println!("{:?}", res);
- assert!(res.is_ok());
- let res = res.unwrap();
- assert_eq!(res.title.unwrap(), "Hello".to_string());
- assert_eq!(res.description.unwrap(), "hey there".to_string());
-}
-
-#[test]
-fn test_can_parse_tags() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-slug = "hello-world"
-tags = ["rust", "html"]"#;
- let res = FrontMatter::parse(content);
- assert!(res.is_ok());
- let res = res.unwrap();
-
- assert_eq!(res.title.unwrap(), "Hello".to_string());
- assert_eq!(res.slug.unwrap(), "hello-world".to_string());
- assert_eq!(res.tags.unwrap(), ["rust".to_string(), "html".to_string()]);
-}
-
-#[test]
-fn test_can_parse_extra_attributes_in_frontmatter() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-slug = "hello-world"
-
-[extra]
-language = "en"
-authors = ["Bob", "Alice"]"#;
- let res = FrontMatter::parse(content);
- assert!(res.is_ok());
- let res = res.unwrap();
-
- assert_eq!(res.title.unwrap(), "Hello".to_string());
- assert_eq!(res.slug.unwrap(), "hello-world".to_string());
- let extra = res.extra.unwrap();
- assert_eq!(extra["language"], to_value("en").unwrap());
- assert_eq!(
- extra["authors"],
- to_value(["Bob".to_string(), "Alice".to_string()]).unwrap()
- );
-}
-
-#[test]
-fn test_is_ok_with_url_instead_of_slug() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-url = "hello-world""#;
- let res = FrontMatter::parse(content);
- assert!(res.is_ok());
- let res = res.unwrap();
- assert!(res.slug.is_none());
- assert_eq!(res.url.unwrap(), "hello-world".to_string());
-}
-
-#[test]
-fn test_is_ok_with_empty_front_matter() {
- let content = r#" "#;
- let res = FrontMatter::parse(content);
- assert!(res.is_ok());
-}
-
-#[test]
-fn test_errors_with_invalid_front_matter() {
- let content = r#"title = 1\n"#;
- let res = FrontMatter::parse(content);
- assert!(res.is_err());
-}
-
-#[test]
-fn test_errors_on_non_string_tag() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-slug = "hello-world"
-tags = ["rust", 1]"#;
- let res = FrontMatter::parse(content);
- assert!(res.is_err());
-}
-
-#[test]
-fn test_errors_on_present_but_empty_slug() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-slug = """#;
- let res = FrontMatter::parse(content);
- assert!(res.is_err());
-}
-
-#[test]
-fn test_errors_on_present_but_empty_url() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-url = """#;
- let res = FrontMatter::parse(content);
- assert!(res.is_err());
-}
-
-#[test]
-fn test_parse_date_yyyy_mm_dd() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-date = "2016-10-10""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.date().is_some());
-}
-
-#[test]
-fn test_parse_date_rfc3339() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-date = "2002-10-02T15:00:00Z""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.date().is_some());
-}
-
-#[test]
-fn test_cant_parse_random_date_format() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-date = "2002/10/12""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.date().is_none());
-}
-
-#[test]
-fn test_cant_parse_sort_by_date() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-sort_by = "date""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.sort_by.is_some());
- assert_eq!(res.sort_by.unwrap(), SortBy::Date);
-}
-
-#[test]
-fn test_cant_parse_sort_by_order() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-sort_by = "order""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.sort_by.is_some());
- assert_eq!(res.sort_by.unwrap(), SortBy::Order);
-}
-
-#[test]
-fn test_cant_parse_sort_by_none() {
- let content = r#"
-title = "Hello"
-description = "hey there"
-sort_by = "none""#;
- let res = FrontMatter::parse(content).unwrap();
- assert!(res.sort_by.is_some());
- assert_eq!(res.sort_by.unwrap(), SortBy::None);
-}
-
-#[test]
-fn test_can_split_content_valid() {
- let content = r#"
-+++
-title = "Title"
-description = "hey there"
-date = "2002/10/12"
-+++
-Hello
-"#;
- let (front_matter, content) = split_content(Path::new(""), content).unwrap();
- assert_eq!(content, "Hello\n");
- assert_eq!(front_matter.title.unwrap(), "Title");
-}
-
-#[test]
-fn test_can_split_content_with_only_frontmatter_valid() {
- let content = r#"
-+++
-title = "Title"
-description = "hey there"
-date = "2002/10/12"
-+++"#;
- let (front_matter, content) = split_content(Path::new(""), content).unwrap();
- assert_eq!(content, "");
- assert_eq!(front_matter.title.unwrap(), "Title");
-}
-
-#[test]
-fn test_can_split_content_lazily() {
- let content = r#"
-+++
-title = "Title"
-description = "hey there"
-date = "2002-10-02T15:00:00Z"
-+++
-+++"#;
- let (front_matter, content) = split_content(Path::new(""), content).unwrap();
- assert_eq!(content, "+++");
- assert_eq!(front_matter.title.unwrap(), "Title");
-}
-
-#[test]
-fn test_error_if_cannot_locate_frontmatter() {
- let content = r#"
-+++
-title = "Title"
-description = "hey there"
-date = "2002/10/12"
-"#;
- let res = split_content(Path::new(""), content);
- assert!(res.is_err());
-}