diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bibiman/bibisetup.rs | 98 | ||||
| -rw-r--r-- | src/bibiman/citekeys.rs | 85 | ||||
| -rw-r--r-- | src/bibiman/citekeys/citekey_utils.rs | 17 |
3 files changed, 145 insertions, 55 deletions
diff --git a/src/bibiman/bibisetup.rs b/src/bibiman/bibisetup.rs index a83a507..a817236 100644 --- a/src/bibiman/bibisetup.rs +++ b/src/bibiman/bibisetup.rs @@ -22,6 +22,7 @@ use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; +use std::path::Path; use std::{fs, path::PathBuf}; use walkdir::WalkDir; @@ -318,23 +319,27 @@ impl BibiSetup { cfg: &BibiConfig, ) -> Vec<BibiData> { let mut pdf_files = if cfg.general.pdf_path.is_some() { - collect_file_paths(cfg.general.pdf_path.as_ref().unwrap(), &Some(vec!["pdf"])) + collect_file_paths( + cfg.general.pdf_path.as_ref().unwrap(), + Some(vec!["pdf".into()].as_slice()), + ) + } else { + None + }; + let ext = if let Some(ext) = &cfg.general.note_extensions + && cfg.general.note_path.is_some() + { + // let mut ext: Vec<&str> = Vec::new(); + // for e in cfg.general.note_extensions.as_ref().unwrap().iter() { + // ext.push(e); + // } + Some(ext.as_slice()) } else { None }; - let ext: Option<Vec<&str>> = - if cfg.general.note_path.is_some() && cfg.general.note_extensions.is_some() { - let mut ext: Vec<&str> = Vec::new(); - for e in cfg.general.note_extensions.as_ref().unwrap().iter() { - ext.push(e); - } - Some(ext) - } else { - None - }; let mut note_files = if cfg.general.note_path.is_some() && cfg.general.note_extensions.is_some() { - collect_file_paths(cfg.general.note_path.as_ref().unwrap(), &ext) + collect_file_paths(cfg.general.note_path.as_ref().unwrap(), ext.clone()) } else { None }; @@ -369,7 +374,7 @@ impl BibiSetup { file_field: filepaths.1, subtitle: Self::get_subtitle(k, bibliography), notes: if note_files.is_some() { - Self::get_notepath(k, &mut note_files, &ext) + Self::get_notepath(k, &mut note_files, ext) } else { None }, @@ -575,18 +580,18 @@ impl BibiSetup { ) -> (Option<Vec<OsString>>, bool) { if biblio.get(citekey).unwrap().file().is_ok() { ( - Some(vec![biblio - .get(citekey) - .unwrap() - .file() - .unwrap() - .trim() - .into()]), + Some(vec![ + biblio.get(citekey).unwrap().file().unwrap().trim().into(), + ]), true, ) } else if pdf_files.is_some() { ( - Self::merge_filepath_or_none_two(&citekey, pdf_files, vec!["pdf"]), + Self::merge_filepath_or_none_two( + &citekey, + pdf_files, + vec!["pdf".into()].as_slice(), + ), false, ) } else { @@ -597,10 +602,10 @@ impl BibiSetup { pub fn get_notepath( citekey: &str, note_files: &mut Option<HashMap<String, Vec<PathBuf>>>, - ext: &Option<Vec<&str>>, + ext: Option<&[String]>, ) -> Option<Vec<OsString>> { if let Some(e) = ext { - Self::merge_filepath_or_none_two(citekey, note_files, e.to_vec()) + Self::merge_filepath_or_none_two(citekey, note_files, e) } else { None } @@ -627,7 +632,7 @@ impl BibiSetup { fn merge_filepath_or_none_two( citekey: &str, files: &mut Option<HashMap<String, Vec<PathBuf>>>, - extensions: Vec<&str>, + extensions: &[String], ) -> Option<Vec<OsString>> { let mut file = Vec::new(); @@ -645,11 +650,7 @@ impl BibiSetup { } } - if file.is_empty() { - None - } else { - Some(file) - } + if file.is_empty() { None } else { Some(file) } } } @@ -663,15 +664,17 @@ impl BibiSetup { /// /// Passing [`None`] as argument for extensions will result in collecting all files /// from the given directory and its subdirectories! -pub fn collect_file_paths( - file_dir: &PathBuf, - extensions: &Option<Vec<&str>>, +pub fn collect_file_paths<P: AsRef<Path>>( + file_dir: P, + extensions: Option<&[String]>, ) -> Option<HashMap<String, Vec<PathBuf>>> { let mut files: HashMap<String, Vec<PathBuf>> = HashMap::new(); + let file_dir = file_dir.as_ref(); + // Expand tilde to /home/user let file_dir = if file_dir.starts_with("~") { - &app::expand_home(&file_dir) + &app::expand_home(&file_dir.to_path_buf()) } else { file_dir }; @@ -682,13 +685,13 @@ pub fn collect_file_paths( let f = file.unwrap().into_path(); if f.is_file() && f.extension().is_some() - && extensions.as_ref().is_some_and(|v| { + && extensions.is_some_and(|v| { v.contains( &f.extension() .unwrap_or_default() .to_ascii_lowercase() - .to_str() - .unwrap_or_default(), + .to_string_lossy() + .to_string(), ) }) { @@ -721,11 +724,7 @@ pub fn collect_file_paths( } } - if files.is_empty() { - None - } else { - Some(files) - } + if files.is_empty() { None } else { Some(files) } } #[cfg(test)] @@ -759,8 +758,11 @@ mod tests { ], ); - let matches = - BibiSetup::merge_filepath_or_none_two("citekey", &mut Some(files), vec!["md", "pdf"]); + let matches = BibiSetup::merge_filepath_or_none_two( + "citekey", + &mut Some(files), + vec!["md".into(), "pdf".into()].as_slice(), + ); assert_eq!( matches.clone().unwrap().iter().next().unwrap().to_owned(), @@ -770,9 +772,11 @@ mod tests { matches.clone().unwrap().last().unwrap().to_owned(), OsString::from("/one/other/citekey.pdf") ); - assert!(!matches - .clone() - .unwrap() - .contains(&OsString::from("/one/other/citekey2.pdf"))); + assert!( + !matches + .clone() + .unwrap() + .contains(&OsString::from("/one/other/citekey2.pdf")) + ); } } diff --git a/src/bibiman/citekeys.rs b/src/bibiman/citekeys.rs index 4516b28..8f70ab0 100644 --- a/src/bibiman/citekeys.rs +++ b/src/bibiman/citekeys.rs @@ -16,6 +16,7 @@ ///// use std::{ + ffi::OsStr, fs::OpenOptions, io::Write, path::{Path, PathBuf}, @@ -28,7 +29,10 @@ use owo_colors::OwoColorize; use serde::{Deserialize, Serialize}; use crate::{ - bibiman::citekeys::citekey_utils::{SKIPPED_ENTRIES, build_citekey, formatting_help}, + bibiman::{ + bibisetup::collect_file_paths, + citekeys::citekey_utils::{SKIPPED_ENTRIES, build_citekey, formatting_help}, + }, config::{BibiConfig, IGNORED_SPECIAL_CHARS, IGNORED_WORDS}, }; @@ -72,6 +76,7 @@ impl<'a> CitekeyFormatting<'a> { let mut formatter = CitekeyFormatting::default(); let mut source_file = PathBuf::new(); let mut target_file: Option<PathBuf> = None; + let mut update_files = false; formatter.fields = cfg.citekey_formatter.fields.clone().ok_or_eyre(format!( "Need to define {} correctly in config file", @@ -101,6 +106,7 @@ impl<'a> CitekeyFormatting<'a> { Short('t') | Short('o') | Long("target") | Long("output") => { target_file = Some(parser.value()?.into()) } + Short('u') | Long("update-attachments") => update_files = true, _ => return Err(arg.unexpected().into()), } } @@ -122,10 +128,13 @@ impl<'a> CitekeyFormatting<'a> { &*IGNORED_WORDS.as_slice() }; - formatter - .do_formatting() - .rev_sort_new_keys_by_len() - .update_file(source_file, target_file)?; + let mut updated_formatter = formatter.do_formatting().rev_sort_new_keys_by_len(); + + updated_formatter.update_file(source_file, target_file)?; + + if update_files { + updated_formatter.update_notes_pdfs(cfg)?; + } Ok(()) } @@ -194,7 +203,13 @@ impl<'a> CitekeyFormatting<'a> { target_file: Option<P>, ) -> color_eyre::Result<()> { if self.dry_run { - println!("Following citekeys would be formatted: old => new\n"); + println!( + "{}\n", + "Following citekeys would be formatted: old => new" + .bold() + .underline() + .white() + ); self.old_new_keys_map.sort_by(|a, b| a.0.cmp(&b.0)); for (old, new) in &self.old_new_keys_map { println!("{} => {}", old.italic(), new.bold()) @@ -235,6 +250,64 @@ impl<'a> CitekeyFormatting<'a> { self } + pub fn update_notes_pdfs(&self, cfg: &BibiConfig) -> color_eyre::Result<()> { + if let Some(pdf_path) = &cfg.general.pdf_path { + self.update_files_by_citekey_basename(pdf_path, vec!["pdf".into()].as_slice())?; + } + if let Some(note_path) = &cfg.general.note_path + && let Some(ext) = &cfg.general.note_extensions + { + self.update_files_by_citekey_basename(note_path, ext.as_slice())?; + } + Ok(()) + } + + fn update_files_by_citekey_basename<P: AsRef<Path>>( + &self, + path: P, + ext: &[String], + ) -> color_eyre::Result<()> { + let files = collect_file_paths(path.as_ref(), Some(ext)); + if self.dry_run { + println!( + "\n{}\n", + "Following paths would be updated:" + .underline() + .bold() + .white() + ) + } + if let Some(mut f) = files { + for (old_key, new_key) in self.old_new_keys_map.iter() { + for e in ext { + let old_basename = old_key.to_owned() + "." + e; + if let Some(item) = f.get_mut(&old_basename) { + for p in item { + let ext = p.extension(); + let basename = new_key.to_owned() + + "." + + ext.unwrap_or(OsStr::new("")).to_str().unwrap_or(""); + let new_name = p + .parent() + .expect("parent expected") + .join(Path::new(&basename)); + if !self.dry_run { + std::fs::rename(p, new_name)?; + } else { + println!( + "{} => {}", + p.display().to_string().italic().dimmed(), + new_name.display().to_string().bold() + ) + } + } + } + } + } + } + Ok(()) + } + /// Update the `Bibliography` of the `CitekeyFormatting` struct and return /// it as `String`. pub fn print_updated_bib_as_string(&mut self) -> String { diff --git a/src/bibiman/citekeys/citekey_utils.rs b/src/bibiman/citekeys/citekey_utils.rs index 58a8274..61a1804 100644 --- a/src/bibiman/citekeys/citekey_utils.rs +++ b/src/bibiman/citekeys/citekey_utils.rs @@ -79,7 +79,7 @@ pub(super) fn formatting_help() { \t{} \tThe bibfile for which the citekey formatting should be processed. \tTakes a path as argument. - ", "-s, -f, --source=, --file=".fg::<White>().bold()}, + ", "-s, -f, --source=<PATH>, --file=<PATH>".fg::<White>().bold()}, formatdoc!( " \t{} @@ -88,9 +88,22 @@ pub(super) fn formatting_help() { \tcreated. \tIf the argument isn't used, the original file will be {}! ", - "-t, -o, --target=, --output=".fg::<White>().bold(), + "-t, -o, --target=<PATH>, --output=<PATH>" + .fg::<White>() + .bold(), "overwritten".italic(), ), + formatdoc!( + " + \t{} + \tWhen formatting citekeys also rename all PDFs and notefiles + \tfollowing the bibiman citekey-basename scheme at the locations + \tset in the config file. This option can break file paths. Try + \twith {} first! + ", + "-u, --update-attachments".fg::<White>().bold(), + "--dry-run".bold() + ), ]; let help = help.join("\n"); println!("{}", help); |
