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.

234 lines
8.2KB

  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_trans(config: Config) -> GlobalFn {
  20. let translations_config = config.translations.unwrap();
  21. let default_lang = to_value(config.default_language.unwrap()).unwrap();
  22. Box::new(move |args| -> Result<Value> {
  23. let key = required_string_arg!(args.get("key"), "`trans` requires a `key` argument.");
  24. let lang_arg = args.get("lang").unwrap_or(&default_lang).clone();
  25. let lang = from_value::<String>(lang_arg).unwrap();
  26. let translations = &translations_config[lang.as_str()];
  27. Ok(to_value(&translations[key.as_str()]).unwrap())
  28. })
  29. }
  30. pub fn make_get_page(all_pages: &HashMap<PathBuf, Page>) -> GlobalFn {
  31. let mut pages = HashMap::new();
  32. for page in all_pages.values() {
  33. pages.insert(page.file.relative.clone(), page.clone());
  34. }
  35. Box::new(move |args| -> Result<Value> {
  36. let path = required_string_arg!(args.get("path"), "`get_page` requires a `path` argument with a string value");
  37. match pages.get(&path) {
  38. Some(p) => Ok(to_value(p).unwrap()),
  39. None => Err(format!("Page `{}` not found.", path).into())
  40. }
  41. })
  42. }
  43. pub fn make_get_section(all_sections: &HashMap<PathBuf, Section>) -> GlobalFn {
  44. let mut sections = HashMap::new();
  45. for section in all_sections.values() {
  46. sections.insert(section.file.relative.clone(), section.clone());
  47. }
  48. Box::new(move |args| -> Result<Value> {
  49. let path = required_string_arg!(args.get("path"), "`get_section` requires a `path` argument with a string value");
  50. match sections.get(&path) {
  51. Some(p) => Ok(to_value(p).unwrap()),
  52. None => Err(format!("Section `{}` not found.", path).into())
  53. }
  54. })
  55. }
  56. pub fn make_get_url(permalinks: HashMap<String, String>, config: Config) -> GlobalFn {
  57. Box::new(move |args| -> Result<Value> {
  58. let cachebust = args
  59. .get("cachebust")
  60. .map_or(false, |c| {
  61. from_value::<bool>(c.clone()).unwrap_or(false)
  62. });
  63. let trailing_slash = args
  64. .get("trailing_slash")
  65. .map_or(true, |c| {
  66. from_value::<bool>(c.clone()).unwrap_or(true)
  67. });
  68. let path = required_string_arg!(args.get("path"), "`get_url` requires a `path` argument with a string value");
  69. if path.starts_with("./") {
  70. match resolve_internal_link(&path, &permalinks) {
  71. Ok(url) => Ok(to_value(url).unwrap()),
  72. Err(_) => Err(format!("Could not resolve URL for link `{}` not found.", path).into())
  73. }
  74. } else {
  75. // anything else
  76. let mut permalink = config.make_permalink(&path);
  77. if !trailing_slash && permalink.ends_with("/") {
  78. permalink.pop(); // Removes the slash
  79. }
  80. if cachebust {
  81. permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap());
  82. }
  83. Ok(to_value(permalink).unwrap())
  84. }
  85. })
  86. }
  87. pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy>) -> GlobalFn {
  88. Box::new(move |args| -> Result<Value> {
  89. let kind = required_string_arg!(args.get("kind"), "`get_taxonomy_url` requires a `kind` argument with a string value");
  90. let name = required_string_arg!(args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value");
  91. let container = match kind.as_ref() {
  92. "tag" => &tags,
  93. "category" => &categories,
  94. _ => return Err("`get_taxonomy_url` can only get `tag` or `category` for the `kind` argument".into()),
  95. };
  96. if let Some(ref c) = *container {
  97. for item in &c.items {
  98. if item.name == name {
  99. return Ok(to_value(item.permalink.clone()).unwrap());
  100. }
  101. }
  102. bail!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind);
  103. } else {
  104. bail!("`get_taxonomy_url` tried to get a taxonomy of kind `{}` but there isn't any", kind);
  105. }
  106. })
  107. }
  108. #[cfg(test)]
  109. mod tests {
  110. use super::{make_get_url, make_get_taxonomy_url, make_trans};
  111. use std::collections::HashMap;
  112. use tera::to_value;
  113. use config::Config;
  114. use taxonomies::{Taxonomy, TaxonomyKind, TaxonomyItem};
  115. #[test]
  116. fn can_add_cachebust_to_url() {
  117. let config = Config::default();
  118. let static_fn = make_get_url(HashMap::new(), config);
  119. let mut args = HashMap::new();
  120. args.insert("path".to_string(), to_value("app.css").unwrap());
  121. args.insert("cachebust".to_string(), to_value(true).unwrap());
  122. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/?t=1");
  123. }
  124. #[test]
  125. fn can_remove_trailing_slashes() {
  126. let config = Config::default();
  127. let static_fn = make_get_url(HashMap::new(), config);
  128. let mut args = HashMap::new();
  129. args.insert("path".to_string(), to_value("app.css").unwrap());
  130. args.insert("trailing_slash".to_string(), to_value(false).unwrap());
  131. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css");
  132. }
  133. #[test]
  134. fn can_remove_slashes_and_cachebust() {
  135. let config = Config::default();
  136. let static_fn = make_get_url(HashMap::new(), config);
  137. let mut args = HashMap::new();
  138. args.insert("path".to_string(), to_value("app.css").unwrap());
  139. args.insert("trailing_slash".to_string(), to_value(false).unwrap());
  140. args.insert("cachebust".to_string(), to_value(true).unwrap());
  141. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css?t=1");
  142. }
  143. #[test]
  144. fn can_link_to_some_static_file() {
  145. let config = Config::default();
  146. let static_fn = make_get_url(HashMap::new(), config);
  147. let mut args = HashMap::new();
  148. args.insert("path".to_string(), to_value("app.css").unwrap());
  149. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/");
  150. }
  151. #[test]
  152. fn can_get_tag_url() {
  153. let tag = TaxonomyItem::new(
  154. "Prog amming",
  155. TaxonomyKind::Tags,
  156. &Config::default(),
  157. vec![],
  158. );
  159. let tags = Taxonomy {
  160. kind: TaxonomyKind::Tags,
  161. items: vec![tag],
  162. };
  163. let static_fn = make_get_taxonomy_url(Some(tags), None);
  164. // can find it correctly
  165. let mut args = HashMap::new();
  166. args.insert("kind".to_string(), to_value("tag").unwrap());
  167. args.insert("name".to_string(), to_value("Prog amming").unwrap());
  168. assert_eq!(static_fn(args).unwrap(), "http://a-website.com/tags/prog-amming/");
  169. // and errors if it can't find it
  170. let mut args = HashMap::new();
  171. args.insert("kind".to_string(), to_value("tag").unwrap());
  172. args.insert("name".to_string(), to_value("random").unwrap());
  173. assert!(static_fn(args).is_err());
  174. }
  175. #[test]
  176. fn can_translate_a_string() {
  177. let trans_config = r#"
  178. base_url = "https://remplace-par-ton-url.fr"
  179. default_language = "fr"
  180. [translations]
  181. [translations.fr]
  182. title = "Un titre"
  183. [translations.en]
  184. title = "A title"
  185. "#;
  186. let config = Config::parse(trans_config).unwrap();
  187. let static_fn = make_trans(config);
  188. let mut args = HashMap::new();
  189. args.insert("key".to_string(), to_value("title").unwrap());
  190. assert_eq!(static_fn(args.clone()).unwrap(), "Un titre");
  191. args.insert("lang".to_string(), to_value("en").unwrap());
  192. assert_eq!(static_fn(args.clone()).unwrap(), "A title");
  193. args.insert("lang".to_string(), to_value("fr").unwrap());
  194. assert_eq!(static_fn(args.clone()).unwrap(), "Un titre");
  195. }
  196. }