You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lib.rs 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #[macro_use]
  2. extern crate lazy_static;
  3. #[macro_use]
  4. extern crate serde_derive;
  5. extern crate serde;
  6. extern crate toml;
  7. extern crate regex;
  8. extern crate tera;
  9. extern crate chrono;
  10. #[macro_use]
  11. extern crate errors;
  12. use std::path::Path;
  13. use regex::Regex;
  14. use errors::{Result, ResultExt};
  15. mod page;
  16. mod section;
  17. pub use page::PageFrontMatter;
  18. pub use section::SectionFrontMatter;
  19. lazy_static! {
  20. static ref PAGE_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap();
  21. }
  22. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
  23. #[serde(rename_all = "lowercase")]
  24. pub enum SortBy {
  25. /// Most recent to oldest
  26. Date,
  27. /// Lower weight comes first
  28. Weight,
  29. /// No sorting
  30. None,
  31. }
  32. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
  33. #[serde(rename_all = "lowercase")]
  34. pub enum InsertAnchor {
  35. Left,
  36. Right,
  37. None,
  38. }
  39. /// Split a file between the front matter and its content
  40. /// Will return an error if the front matter wasn't found
  41. fn split_content(file_path: &Path, content: &str) -> Result<(String, String)> {
  42. if !PAGE_RE.is_match(content) {
  43. bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", file_path.to_string_lossy());
  44. }
  45. // 2. extract the front matter and the content
  46. let caps = PAGE_RE.captures(content).unwrap();
  47. // caps[0] is the full match
  48. // caps[1] => front matter
  49. // caps[2] => content
  50. Ok((caps[1].to_string(), caps[2].to_string()))
  51. }
  52. /// Split a file between the front matter and its content.
  53. /// Returns a parsed `SectionFrontMatter` and the rest of the content
  54. pub fn split_section_content(file_path: &Path, content: &str) -> Result<(SectionFrontMatter, String)> {
  55. let (front_matter, content) = split_content(file_path, content)?;
  56. let meta = SectionFrontMatter::parse(&front_matter)
  57. .chain_err(|| format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()))?;
  58. Ok((meta, content))
  59. }
  60. /// Split a file between the front matter and its content
  61. /// Returns a parsed `PageFrontMatter` and the rest of the content
  62. pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> {
  63. let (front_matter, content) = split_content(file_path, content)?;
  64. let meta = PageFrontMatter::parse(&front_matter)
  65. .chain_err(|| format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()))?;
  66. Ok((meta, content))
  67. }
  68. #[cfg(test)]
  69. mod tests {
  70. use std::path::Path;
  71. use super::{split_section_content, split_page_content};
  72. #[test]
  73. fn can_split_page_content_valid() {
  74. let content = r#"
  75. +++
  76. title = "Title"
  77. description = "hey there"
  78. date = 2002-10-12
  79. +++
  80. Hello
  81. "#;
  82. let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
  83. assert_eq!(content, "Hello\n");
  84. assert_eq!(front_matter.title.unwrap(), "Title");
  85. }
  86. #[test]
  87. fn can_split_section_content_valid() {
  88. let content = r#"
  89. +++
  90. paginate_by = 10
  91. +++
  92. Hello
  93. "#;
  94. let (front_matter, content) = split_section_content(Path::new(""), content).unwrap();
  95. assert_eq!(content, "Hello\n");
  96. assert!(front_matter.is_paginated());
  97. }
  98. #[test]
  99. fn can_split_content_with_only_frontmatter_valid() {
  100. let content = r#"
  101. +++
  102. title = "Title"
  103. description = "hey there"
  104. date = 2002-10-12
  105. +++"#;
  106. let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
  107. assert_eq!(content, "");
  108. assert_eq!(front_matter.title.unwrap(), "Title");
  109. }
  110. #[test]
  111. fn can_split_content_lazily() {
  112. let content = r#"
  113. +++
  114. title = "Title"
  115. description = "hey there"
  116. date = 2002-10-02T15:00:00Z
  117. +++
  118. +++"#;
  119. let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
  120. assert_eq!(content, "+++");
  121. assert_eq!(front_matter.title.unwrap(), "Title");
  122. }
  123. #[test]
  124. fn errors_if_cannot_locate_frontmatter() {
  125. let content = r#"
  126. +++
  127. title = "Title"
  128. description = "hey there"
  129. date = 2002-10-12"#;
  130. let res = split_page_content(Path::new(""), content);
  131. assert!(res.is_err());
  132. }
  133. }