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.

114 lines
4.0KB

  1. use std::collections::HashMap;
  2. use unicode_segmentation::UnicodeSegmentation;
  3. use errors::Result;
  4. /// Get word count and estimated reading time
  5. pub fn get_reading_analytics(content: &str) -> (usize, usize) {
  6. let word_count: usize = content.unicode_words().count();
  7. // https://help.medium.com/hc/en-us/articles/214991667-Read-time
  8. // 275 seems a bit too high though
  9. (word_count, ((word_count + 199) / 200))
  10. }
  11. #[derive(Debug, PartialEq, Clone)]
  12. pub struct ResolvedInternalLink {
  13. pub permalink: String,
  14. // The 2 fields below are only set when there is an anchor
  15. // as we will need that to check if it exists after the markdown rendering is done
  16. pub md_path: Option<String>,
  17. pub anchor: Option<String>,
  18. }
  19. /// Resolves an internal link (of the `@/posts/something.md#hey` sort) to its absolute link and
  20. /// returns the path + anchor as well
  21. pub fn resolve_internal_link(
  22. link: &str,
  23. permalinks: &HashMap<String, String>,
  24. ) -> Result<ResolvedInternalLink> {
  25. // First we remove the ./ since that's zola specific
  26. let clean_link = link.replacen("@/", "", 1);
  27. // Then we remove any potential anchor
  28. // parts[0] will be the file path and parts[1] the anchor if present
  29. let parts = clean_link.split('#').collect::<Vec<_>>();
  30. match permalinks.get(parts[0]) {
  31. Some(p) => {
  32. if parts.len() > 1 {
  33. Ok(ResolvedInternalLink {
  34. permalink: format!("{}#{}", p, parts[1]),
  35. md_path: Some(parts[0].to_string()),
  36. anchor: Some(parts[1].to_string()),
  37. })
  38. } else {
  39. Ok(ResolvedInternalLink { permalink: p.to_string(), md_path: None, anchor: None })
  40. }
  41. }
  42. None => bail!(format!("Relative link {} not found.", link)),
  43. }
  44. }
  45. #[cfg(test)]
  46. mod tests {
  47. use std::collections::HashMap;
  48. use super::{get_reading_analytics, resolve_internal_link};
  49. #[test]
  50. fn can_resolve_valid_internal_link() {
  51. let mut permalinks = HashMap::new();
  52. permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
  53. let res = resolve_internal_link("@/pages/about.md", &permalinks).unwrap();
  54. assert_eq!(res.permalink, "https://vincent.is/about");
  55. }
  56. #[test]
  57. fn can_resolve_valid_root_internal_link() {
  58. let mut permalinks = HashMap::new();
  59. permalinks.insert("about.md".to_string(), "https://vincent.is/about".to_string());
  60. let res = resolve_internal_link("@/about.md", &permalinks).unwrap();
  61. assert_eq!(res.permalink, "https://vincent.is/about");
  62. }
  63. #[test]
  64. fn can_resolve_internal_links_with_anchors() {
  65. let mut permalinks = HashMap::new();
  66. permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
  67. let res = resolve_internal_link("@/pages/about.md#hello", &permalinks).unwrap();
  68. assert_eq!(res.permalink, "https://vincent.is/about#hello");
  69. assert_eq!(res.md_path, Some("pages/about.md".to_string()));
  70. assert_eq!(res.anchor, Some("hello".to_string()));
  71. }
  72. #[test]
  73. fn errors_resolve_inexistant_internal_link() {
  74. let res = resolve_internal_link("@/pages/about.md#hello", &HashMap::new());
  75. assert!(res.is_err());
  76. }
  77. #[test]
  78. fn reading_analytics_empty_text() {
  79. let (word_count, reading_time) = get_reading_analytics(" ");
  80. assert_eq!(word_count, 0);
  81. assert_eq!(reading_time, 0);
  82. }
  83. #[test]
  84. fn reading_analytics_short_text() {
  85. let (word_count, reading_time) = get_reading_analytics("Hello World");
  86. assert_eq!(word_count, 2);
  87. assert_eq!(reading_time, 1);
  88. }
  89. #[test]
  90. fn reading_analytics_long_text() {
  91. let mut content = String::new();
  92. for _ in 0..1000 {
  93. content.push_str(" Hello world");
  94. }
  95. let (word_count, reading_time) = get_reading_analytics(&content);
  96. assert_eq!(word_count, 2000);
  97. assert_eq!(reading_time, 10);
  98. }
  99. }