parent
bb3cba1ad5
commit
299c3c8b22
|
@ -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 {
|
|
||||||
/// <title> of the page
|
|
||||||
pub title: Option<String>,
|
|
||||||
/// Description in <meta> that appears when linked, e.g. on twitter
|
|
||||||
pub description: Option<String>,
|
|
||||||
/// Date if we want to order pages (ie blog post)
|
|
||||||
pub date: Option<String>,
|
|
||||||
/// The page slug. Will be used instead of the filename if present
|
|
||||||
/// Can't be an empty string if present
|
|
||||||
pub slug: Option<String>,
|
|
||||||
/// 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<String>,
|
|
||||||
/// Tags, not to be confused with categories
|
|
||||||
pub tags: Option<Vec<String>>,
|
|
||||||
/// Whether this page is a draft and should be published or not
|
|
||||||
pub draft: Option<bool>,
|
|
||||||
/// Only one category allowed
|
|
||||||
pub category: Option<String>,
|
|
||||||
/// Whether to sort by "date", "order" or "none". Defaults to `none`.
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub sort_by: Option<SortBy>,
|
|
||||||
/// Integer to use to order content. Lowest is at the bottom, highest first
|
|
||||||
pub order: Option<usize>,
|
|
||||||
/// Optional template, if we want to specify which template to render for that page
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub template: Option<String>,
|
|
||||||
/// 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<usize>,
|
|
||||||
/// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub paginate_path: Option<String>,
|
|
||||||
/// Whether to render that page/section or not. Defaults to `true`.
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub render: Option<bool>,
|
|
||||||
/// Any extra parameter present in the front matter
|
|
||||||
pub extra: Option<HashMap<String, Value>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FrontMatter {
|
|
||||||
pub fn parse(toml: &str) -> Result<FrontMatter> {
|
|
||||||
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<NaiveDateTime> {
|
|
||||||
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()))
|
|
||||||
}
|
|
122
src/front_matter/mod.rs
Normal file
122
src/front_matter/mod.rs
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
206
src/front_matter/page.rs
Normal file
206
src/front_matter/page.rs
Normal file
|
@ -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 {
|
||||||
|
/// <title> of the page
|
||||||
|
pub title: Option<String>,
|
||||||
|
/// Description in <meta> that appears when linked, e.g. on twitter
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Date if we want to order pages (ie blog post)
|
||||||
|
pub date: Option<String>,
|
||||||
|
/// The page slug. Will be used instead of the filename if present
|
||||||
|
/// Can't be an empty string if present
|
||||||
|
pub slug: Option<String>,
|
||||||
|
/// 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<String>,
|
||||||
|
/// Tags, not to be confused with categories
|
||||||
|
pub tags: Option<Vec<String>>,
|
||||||
|
/// Whether this page is a draft and should be published or not
|
||||||
|
pub draft: Option<bool>,
|
||||||
|
/// Only one category allowed
|
||||||
|
pub category: Option<String>,
|
||||||
|
/// Integer to use to order content. Lowest is at the bottom, highest first
|
||||||
|
pub order: Option<usize>,
|
||||||
|
/// Optional template, if we want to specify which template to render for that page
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub template: Option<String>,
|
||||||
|
/// Any extra parameter present in the front matter
|
||||||
|
pub extra: Option<HashMap<String, Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageFrontMatter {
|
||||||
|
pub fn parse(toml: &str) -> Result<PageFrontMatter> {
|
||||||
|
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<NaiveDateTime> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
99
src/front_matter/section.rs
Normal file
99
src/front_matter/section.rs
Normal file
|
@ -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 {
|
||||||
|
/// <title> of the page
|
||||||
|
pub title: Option<String>,
|
||||||
|
/// Description in <meta> that appears when linked, e.g. on twitter
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Whether to sort by "date", "order" or "none". Defaults to `none`.
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub sort_by: Option<SortBy>,
|
||||||
|
/// Optional template, if we want to specify which template to render for that page
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub template: Option<String>,
|
||||||
|
/// 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<usize>,
|
||||||
|
/// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub paginate_path: Option<String>,
|
||||||
|
/// 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<bool>,
|
||||||
|
/// Any extra parameter present in the front matter
|
||||||
|
pub extra: Option<HashMap<String, Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SectionFrontMatter {
|
||||||
|
pub fn parse(toml: &str) -> Result<SectionFrontMatter> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ mod templates;
|
||||||
|
|
||||||
pub use site::{Site};
|
pub use site::{Site};
|
||||||
pub use config::{Config, get_config};
|
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 page::{Page, populate_previous_and_next_pages};
|
||||||
pub use section::{Section};
|
pub use section::{Section};
|
||||||
pub use utils::{create_file};
|
pub use utils::{create_file};
|
||||||
|
|
40
src/page.rs
40
src/page.rs
|
@ -11,7 +11,7 @@ use slug::slugify;
|
||||||
|
|
||||||
use errors::{Result, ResultExt};
|
use errors::{Result, ResultExt};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use front_matter::{FrontMatter, SortBy, split_content};
|
use front_matter::{PageFrontMatter, SortBy, split_page_content};
|
||||||
use markdown::markdown_to_html;
|
use markdown::markdown_to_html;
|
||||||
use utils::{read_file, find_content_components};
|
use utils::{read_file, find_content_components};
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ fn find_related_assets(path: &Path) -> Vec<PathBuf> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
|
/// The front matter meta-data
|
||||||
|
pub meta: PageFrontMatter,
|
||||||
/// The .md path
|
/// The .md path
|
||||||
pub file_path: PathBuf,
|
pub file_path: PathBuf,
|
||||||
/// The .md path, starting from the content directory, with / slashes
|
/// The .md path, starting from the content directory, with / slashes
|
||||||
|
@ -60,8 +62,6 @@ pub struct Page {
|
||||||
pub assets: Vec<PathBuf>,
|
pub assets: Vec<PathBuf>,
|
||||||
/// The HTML rendered of the page
|
/// The HTML rendered of the page
|
||||||
pub content: String,
|
pub content: String,
|
||||||
/// The front matter meta-data
|
|
||||||
pub meta: FrontMatter,
|
|
||||||
|
|
||||||
/// The slug of that page.
|
/// The slug of that page.
|
||||||
/// First tries to find the slug in the meta and defaults to filename otherwise
|
/// First tries to find the slug in the meta and defaults to filename otherwise
|
||||||
|
@ -83,8 +83,9 @@ pub struct Page {
|
||||||
|
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub fn new(meta: FrontMatter) -> Page {
|
pub fn new(meta: PageFrontMatter) -> Page {
|
||||||
Page {
|
Page {
|
||||||
|
meta: meta,
|
||||||
file_path: PathBuf::new(),
|
file_path: PathBuf::new(),
|
||||||
relative_path: String::new(),
|
relative_path: String::new(),
|
||||||
parent_path: PathBuf::new(),
|
parent_path: PathBuf::new(),
|
||||||
|
@ -97,7 +98,6 @@ impl Page {
|
||||||
path: "".to_string(),
|
path: "".to_string(),
|
||||||
permalink: "".to_string(),
|
permalink: "".to_string(),
|
||||||
summary: None,
|
summary: None,
|
||||||
meta: meta,
|
|
||||||
previous: None,
|
previous: None,
|
||||||
next: None,
|
next: None,
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ impl Page {
|
||||||
/// erroneous
|
/// erroneous
|
||||||
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Page> {
|
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Page> {
|
||||||
// 1. separate front matter from content
|
// 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);
|
let mut page = Page::new(meta);
|
||||||
page.file_path = file_path.to_path_buf();
|
page.file_path = file_path.to_path_buf();
|
||||||
page.parent_path = page.file_path.parent().unwrap().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 {
|
impl ser::Serialize for Page {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
|
||||||
let mut state = serializer.serialize_struct("page", 16)?;
|
let mut state = serializer.serialize_struct("page", 16)?;
|
||||||
|
@ -318,17 +340,17 @@ mod tests {
|
||||||
|
|
||||||
use std::fs::File;
|
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};
|
use super::{Page, find_related_assets, sort_pages, populate_previous_and_next_pages};
|
||||||
|
|
||||||
fn create_page_with_date(date: &str) -> Page {
|
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());
|
front_matter.date = Some(date.to_string());
|
||||||
Page::new(front_matter)
|
Page::new(front_matter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_page_with_order(order: usize) -> Page {
|
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);
|
front_matter.order = Some(order);
|
||||||
Page::new(front_matter)
|
Page::new(front_matter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,14 +154,14 @@ impl<'a> Paginator<'a> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use tera::{to_value};
|
use tera::{to_value};
|
||||||
|
|
||||||
use front_matter::FrontMatter;
|
use front_matter::SectionFrontMatter;
|
||||||
use page::Page;
|
use page::Page;
|
||||||
use section::Section;
|
use section::Section;
|
||||||
|
|
||||||
use super::{Paginator};
|
use super::{Paginator};
|
||||||
|
|
||||||
fn create_section(is_index: bool) -> Section {
|
fn create_section(is_index: bool) -> Section {
|
||||||
let mut f = FrontMatter::default();
|
let mut f = SectionFrontMatter::default();
|
||||||
f.paginate_by = Some(2);
|
f.paginate_by = Some(2);
|
||||||
f.paginate_path = Some("page".to_string());
|
f.paginate_path = Some("page".to_string());
|
||||||
let mut s = Section::new("content/_index.md", f);
|
let mut s = Section::new("content/_index.md", f);
|
||||||
|
@ -178,9 +178,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_create_paginator() {
|
fn test_can_create_paginator() {
|
||||||
let pages = vec![
|
let pages = vec![
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
];
|
];
|
||||||
let section = create_section(false);
|
let section = create_section(false);
|
||||||
let paginator = Paginator::new(pages.as_slice(), §ion);
|
let paginator = Paginator::new(pages.as_slice(), §ion);
|
||||||
|
@ -200,9 +200,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_create_paginator_for_index() {
|
fn test_can_create_paginator_for_index() {
|
||||||
let pages = vec![
|
let pages = vec![
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
];
|
];
|
||||||
let section = create_section(true);
|
let section = create_section(true);
|
||||||
let paginator = Paginator::new(pages.as_slice(), §ion);
|
let paginator = Paginator::new(pages.as_slice(), §ion);
|
||||||
|
@ -222,9 +222,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_build_paginator_context() {
|
fn test_can_build_paginator_context() {
|
||||||
let pages = vec![
|
let pages = vec![
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
Page::new(FrontMatter::default()),
|
Page::default(),
|
||||||
];
|
];
|
||||||
let section = create_section(false);
|
let section = create_section(false);
|
||||||
let paginator = Paginator::new(pages.as_slice(), §ion);
|
let paginator = Paginator::new(pages.as_slice(), §ion);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use tera::{Tera, Context};
|
||||||
use serde::ser::{SerializeStruct, self};
|
use serde::ser::{SerializeStruct, self};
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use front_matter::{FrontMatter, split_content};
|
use front_matter::{SectionFrontMatter, split_section_content};
|
||||||
use errors::{Result, ResultExt};
|
use errors::{Result, ResultExt};
|
||||||
use utils::{read_file, find_content_components};
|
use utils::{read_file, find_content_components};
|
||||||
use markdown::markdown_to_html;
|
use markdown::markdown_to_html;
|
||||||
|
@ -15,6 +15,8 @@ use page::{Page};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Section {
|
pub struct Section {
|
||||||
|
/// The front matter meta-data
|
||||||
|
pub meta: SectionFrontMatter,
|
||||||
/// The _index.md full path
|
/// The _index.md full path
|
||||||
pub file_path: PathBuf,
|
pub file_path: PathBuf,
|
||||||
/// The .md path, starting from the content directory, with / slashes
|
/// The .md path, starting from the content directory, with / slashes
|
||||||
|
@ -31,8 +33,6 @@ pub struct Section {
|
||||||
pub raw_content: String,
|
pub raw_content: String,
|
||||||
/// The HTML rendered of the page
|
/// The HTML rendered of the page
|
||||||
pub content: String,
|
pub content: String,
|
||||||
/// The front matter meta-data
|
|
||||||
pub meta: FrontMatter,
|
|
||||||
/// All direct pages of that section
|
/// All direct pages of that section
|
||||||
pub pages: Vec<Page>,
|
pub pages: Vec<Page>,
|
||||||
/// All pages that cannot be sorted in this section
|
/// All pages that cannot be sorted in this section
|
||||||
|
@ -42,10 +42,11 @@ pub struct Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Section {
|
impl Section {
|
||||||
pub fn new<P: AsRef<Path>>(file_path: P, meta: FrontMatter) -> Section {
|
pub fn new<P: AsRef<Path>>(file_path: P, meta: SectionFrontMatter) -> Section {
|
||||||
let file_path = file_path.as_ref();
|
let file_path = file_path.as_ref();
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
meta: meta,
|
||||||
file_path: file_path.to_path_buf(),
|
file_path: file_path.to_path_buf(),
|
||||||
relative_path: "".to_string(),
|
relative_path: "".to_string(),
|
||||||
parent_path: file_path.parent().unwrap().to_path_buf(),
|
parent_path: file_path.parent().unwrap().to_path_buf(),
|
||||||
|
@ -54,7 +55,6 @@ impl Section {
|
||||||
permalink: "".to_string(),
|
permalink: "".to_string(),
|
||||||
raw_content: "".to_string(),
|
raw_content: "".to_string(),
|
||||||
content: "".to_string(),
|
content: "".to_string(),
|
||||||
meta: meta,
|
|
||||||
pages: vec![],
|
pages: vec![],
|
||||||
ignored_pages: vec![],
|
ignored_pages: vec![],
|
||||||
subsections: vec![],
|
subsections: vec![],
|
||||||
|
@ -62,7 +62,7 @@ impl Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
|
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
|
||||||
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);
|
let mut section = Section::new(file_path, meta);
|
||||||
section.raw_content = content.clone();
|
section.raw_content = content.clone();
|
||||||
section.components = find_content_components(§ion.file_path);
|
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
|
/// Used to create a default index section if there is no _index.md in the root content directory
|
||||||
fn default() -> Section {
|
fn default() -> Section {
|
||||||
Section {
|
Section {
|
||||||
|
meta: SectionFrontMatter::default(),
|
||||||
file_path: PathBuf::new(),
|
file_path: PathBuf::new(),
|
||||||
relative_path: "".to_string(),
|
relative_path: "".to_string(),
|
||||||
parent_path: PathBuf::new(),
|
parent_path: PathBuf::new(),
|
||||||
|
@ -162,7 +163,6 @@ impl Default for Section {
|
||||||
permalink: "".to_string(),
|
permalink: "".to_string(),
|
||||||
raw_content: "".to_string(),
|
raw_content: "".to_string(),
|
||||||
content: "".to_string(),
|
content: "".to_string(),
|
||||||
meta: FrontMatter::default(),
|
|
||||||
pages: vec![],
|
pages: vec![],
|
||||||
ignored_pages: vec![],
|
ignored_pages: vec![],
|
||||||
subsections: vec![],
|
subsections: vec![],
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
Loading…
Reference in a new issue