aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bibiman/bibisetup.rs98
-rw-r--r--src/bibiman/citekeys.rs85
-rw-r--r--src/bibiman/citekeys/citekey_utils.rs17
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);