use std::fs::read_dir; use std::path::{Path, PathBuf}; /// Looks into the current folder for the path and see if there's anything that is not a .md /// file. Those will be copied next to the rendered .html file pub fn find_related_assets(path: &Path) -> Vec { let mut assets = vec![]; for entry in read_dir(path).unwrap().filter_map(|e| e.ok()) { let entry_path = entry.path(); if entry_path.is_file() { match entry_path.extension() { Some(e) => match e.to_str() { Some("md") => continue, _ => assets.push(entry_path.to_path_buf()), }, None => continue, } } } assets } /// Get word count and estimated reading time pub fn get_reading_analytics(content: &str) -> (usize, usize) { // Only works for latin language but good enough for a start let word_count: usize = content.split_whitespace().count(); // https://help.medium.com/hc/en-us/articles/214991667-Read-time // 275 seems a bit too high though (word_count, (word_count / 200)) } /// Takes a full path to a .md and returns only the components after the first `content` directory /// Will not return the filename as last component pub fn find_content_components>(path: P) -> Vec { let path = path.as_ref(); let mut is_in_content = false; let mut components = vec![]; for section in path.parent().unwrap().components() { let component = section.as_ref().to_string_lossy(); if is_in_content { components.push(component.to_string()); continue; } if component == "content" { is_in_content = true; } } components } #[cfg(test)] mod tests { use std::fs::File; use tempdir::TempDir; use super::{find_related_assets, find_content_components, get_reading_analytics}; #[test] fn can_find_related_assets() { let tmp_dir = TempDir::new("example").expect("create temp dir"); File::create(tmp_dir.path().join("index.md")).unwrap(); File::create(tmp_dir.path().join("example.js")).unwrap(); File::create(tmp_dir.path().join("graph.jpg")).unwrap(); File::create(tmp_dir.path().join("fail.png")).unwrap(); let assets = find_related_assets(tmp_dir.path()); assert_eq!(assets.len(), 3); assert_eq!(assets.iter().filter(|p| p.extension().unwrap() != "md").count(), 3); assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "example.js").count(), 1); assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "graph.jpg").count(), 1); assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1); } #[test] fn reading_analytics_short_text() { let (word_count, reading_time) = get_reading_analytics("Hello World"); assert_eq!(word_count, 2); assert_eq!(reading_time, 0); } #[test] fn reading_analytics_long_text() { let mut content = String::new(); for _ in 0..1000 { content.push_str(" Hello world"); } let (word_count, reading_time) = get_reading_analytics(&content); assert_eq!(word_count, 2000); assert_eq!(reading_time, 10); } #[test] fn can_find_content_components() { let res = find_content_components("/home/vincent/code/site/content/posts/tutorials/python.md"); assert_eq!(res, ["posts".to_string(), "tutorials".to_string()]); } }