use std::path::Path; use std::path::PathBuf; use tokio::task::JoinHandle; use walkdir::WalkDir; use crate::error::CustomError; #[derive(Debug)] pub(crate) struct BlogPost { pub(crate) id: String, } impl BlogPost { pub(crate) async fn load_blog_post, R: AsRef>( root_dir: R, post_dir: P, ) -> Result { async fn inner(root_dir: &Path, post_dir: &Path) -> Result { let post_id = post_dir .file_name() .expect("The post directory should have a name."); let org_files = { let mut ret = Vec::new(); let org_files_iter = get_org_files(post_dir)?; for entry in org_files_iter { ret.push(entry.await??); } ret }; let parsed_org_files = { let mut ret = Vec::new(); for (path, contents) in org_files.iter() { let parsed = organic::parser::parse_file(contents.as_str(), Some(path)) .map_err(|_| CustomError::Static("Failed to parse org-mode document."))?; ret.push((path, contents, parsed)); } ret }; Ok(BlogPost { id: post_id.to_string_lossy().into_owned(), }) } inner(root_dir.as_ref(), post_dir.as_ref()).await } } async fn read_file(path: PathBuf) -> std::io::Result<(PathBuf, String)> { let contents = tokio::fs::read_to_string(&path).await?; Ok((path, contents)) } fn get_org_files>( root_dir: P, ) -> Result>>, walkdir::Error> { let org_files = WalkDir::new(root_dir) .into_iter() .filter(|e| match e { Ok(dir_entry) => { dir_entry.file_type().is_file() && Path::new(dir_entry.file_name()) .extension() .map(|ext| ext.to_ascii_lowercase() == "org") .unwrap_or(false) } Err(_) => true, }) .collect::, _>>()?; let org_files = org_files .into_iter() .map(walkdir::DirEntry::into_path) .map(|path| tokio::spawn(read_file(path))); Ok(org_files) }