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.

190 lines
6.8KB

  1. use std::collections::HashMap;
  2. use std::path::{PathBuf};
  3. use tera::{GlobalFn, Value, from_value, to_value, Result};
  4. use content::{Page, Section};
  5. use config::Config;
  6. use utils::site::resolve_internal_link;
  7. use taxonomies::Taxonomy;
  8. macro_rules! required_string_arg {
  9. ($e: expr, $err: expr) => {
  10. match $e {
  11. Some(v) => match from_value::<String>(v.clone()) {
  12. Ok(u) => u,
  13. Err(_) => return Err($err.into())
  14. },
  15. None => return Err($err.into())
  16. };
  17. };
  18. }
  19. pub fn make_get_page(all_pages: &HashMap<PathBuf, Page>) -> GlobalFn {
  20. let mut pages = HashMap::new();
  21. for page in all_pages.values() {
  22. pages.insert(page.file.relative.clone(), page.clone());
  23. }
  24. Box::new(move |args| -> Result<Value> {
  25. let path = required_string_arg!(args.get("path"), "`get_page` requires a `path` argument with a string value");
  26. match pages.get(&path) {
  27. Some(p) => Ok(to_value(p).unwrap()),
  28. None => Err(format!("Page `{}` not found.", path).into())
  29. }
  30. })
  31. }
  32. pub fn make_get_section(all_sections: &HashMap<PathBuf, Section>) -> GlobalFn {
  33. let mut sections = HashMap::new();
  34. for section in all_sections.values() {
  35. sections.insert(section.file.relative.clone(), section.clone());
  36. }
  37. Box::new(move |args| -> Result<Value> {
  38. let path = required_string_arg!(args.get("path"), "`get_section` requires a `path` argument with a string value");
  39. match sections.get(&path) {
  40. Some(p) => Ok(to_value(p).unwrap()),
  41. None => Err(format!("Section `{}` not found.", path).into())
  42. }
  43. })
  44. }
  45. pub fn make_get_url(permalinks: HashMap<String, String>, config: Config) -> GlobalFn {
  46. Box::new(move |args| -> Result<Value> {
  47. let cachebust = args
  48. .get("cachebust")
  49. .map_or(false, |c| {
  50. from_value::<bool>(c.clone()).unwrap_or(false)
  51. });
  52. let trailing_slash = args
  53. .get("trailing_slash")
  54. .map_or(true, |c| {
  55. from_value::<bool>(c.clone()).unwrap_or(true)
  56. });
  57. let path = required_string_arg!(args.get("path"), "`get_url` requires a `path` argument with a string value");
  58. if path.starts_with("./") {
  59. match resolve_internal_link(&path, &permalinks) {
  60. Ok(url) => Ok(to_value(url).unwrap()),
  61. Err(_) => Err(format!("Could not resolve URL for link `{}` not found.", path).into())
  62. }
  63. } else {
  64. // anything else
  65. let mut permalink = config.make_permalink(&path);
  66. if !trailing_slash && permalink.ends_with("/") {
  67. permalink.pop(); // Removes the slash
  68. }
  69. if cachebust {
  70. permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap());
  71. }
  72. Ok(to_value(permalink).unwrap())
  73. }
  74. })
  75. }
  76. pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy>) -> GlobalFn {
  77. Box::new(move |args| -> Result<Value> {
  78. let kind = required_string_arg!(args.get("kind"), "`get_taxonomy_url` requires a `kind` argument with a string value");
  79. let name = required_string_arg!(args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value");
  80. let container = match kind.as_ref() {
  81. "tag" => &tags,
  82. "category" => &categories,
  83. _ => return Err("`get_taxonomy_url` can only get `tag` or `category` for the `kind` argument".into()),
  84. };
  85. if let Some(ref c) = *container {
  86. for item in &c.items {
  87. if item.name == name {
  88. return Ok(to_value(item.permalink.clone()).unwrap());
  89. }
  90. }
  91. bail!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind);
  92. } else {
  93. bail!("`get_taxonomy_url` tried to get a taxonomy of kind `{}` but there isn't any", kind);
  94. }
  95. })
  96. }
  97. #[cfg(test)]
  98. mod tests {
  99. use super::{make_get_url, make_get_taxonomy_url};
  100. use std::collections::HashMap;
  101. use tera::to_value;
  102. use config::Config;
  103. use taxonomies::{Taxonomy, TaxonomyKind, TaxonomyItem};
  104. #[test]
  105. fn can_add_cachebust_to_url() {
  106. let config = Config::default();
  107. let static_fn = make_get_url(HashMap::new(), config);
  108. let mut args = HashMap::new();
  109. args.insert("path".to_string(), to_value("app.css").unwrap());
  110. args.insert("cachebust".to_string(), to_value(true).unwrap());
  111. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/?t=1");
  112. }
  113. #[test]
  114. fn can_remove_trailing_slashes() {
  115. let config = Config::default();
  116. let static_fn = make_get_url(HashMap::new(), config);
  117. let mut args = HashMap::new();
  118. args.insert("path".to_string(), to_value("app.css").unwrap());
  119. args.insert("trailing_slash".to_string(), to_value(false).unwrap());
  120. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css");
  121. }
  122. #[test]
  123. fn can_remove_slashes_and_cachebust() {
  124. let config = Config::default();
  125. let static_fn = make_get_url(HashMap::new(), config);
  126. let mut args = HashMap::new();
  127. args.insert("path".to_string(), to_value("app.css").unwrap());
  128. args.insert("trailing_slash".to_string(), to_value(false).unwrap());
  129. args.insert("cachebust".to_string(), to_value(true).unwrap());
  130. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css?t=1");
  131. }
  132. #[test]
  133. fn can_link_to_some_static_file() {
  134. let config = Config::default();
  135. let static_fn = make_get_url(HashMap::new(), config);
  136. let mut args = HashMap::new();
  137. args.insert("path".to_string(), to_value("app.css").unwrap());
  138. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/");
  139. }
  140. #[test]
  141. fn can_get_tag_url() {
  142. let tag = TaxonomyItem::new(
  143. "Prog amming",
  144. TaxonomyKind::Tags,
  145. &Config::default(),
  146. vec![],
  147. );
  148. let tags = Taxonomy {
  149. kind: TaxonomyKind::Tags,
  150. items: vec![tag],
  151. };
  152. let static_fn = make_get_taxonomy_url(Some(tags), None);
  153. // can find it correctly
  154. let mut args = HashMap::new();
  155. args.insert("kind".to_string(), to_value("tag").unwrap());
  156. args.insert("name".to_string(), to_value("Prog amming").unwrap());
  157. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/tag/prog-amming/");
  158. // and errors if it can't find it
  159. let mut args = HashMap::new();
  160. args.insert("kind".to_string(), to_value("tag").unwrap());
  161. args.insert("name".to_string(), to_value("random").unwrap());
  162. assert!(static_fn(args).is_err());
  163. }
  164. }