use lazy_static::lazy_static; use serde_derive::{Deserialize, Serialize}; use errors::{bail, Error, Result}; use regex::Regex; use std::path::Path; mod page; mod section; pub use page::PageFrontMatter; pub use section::SectionFrontMatter; lazy_static! { static ref PAGE_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+(\r?\n(?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap(); } #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum SortBy { /// Most recent to oldest Date, /// Lower weight comes first Weight, /// No sorting None, } #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum InsertAnchor { Left, Right, None, } /// Split a file between the front matter and its content /// Will return an error if the front matter wasn't found fn split_content<'c>(file_path: &Path, content: &'c str) -> Result<(&'c str, &'c str)> { 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.get(1).unwrap().as_str(), caps.get(2).unwrap().as_str())) } /// 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<'c>( file_path: &Path, content: &'c str, ) -> Result<(SectionFrontMatter, &'c str)> { let (front_matter, content) = split_content(file_path, content)?; let meta = SectionFrontMatter::parse(&front_matter).map_err(|e| { Error::chain( format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()), e, ) })?; 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<'c>( file_path: &Path, content: &'c str, ) -> Result<(PageFrontMatter, &'c str)> { let (front_matter, content) = split_content(file_path, content)?; let meta = PageFrontMatter::parse(&front_matter).map_err(|e| { Error::chain( format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()), e, ) })?; Ok((meta, content)) } #[cfg(test)] mod tests { use std::path::Path; use super::{split_page_content, split_section_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()); } }