use std::path::{Path, PathBuf};
use par_term_config::Config;
pub fn migrate_legacy_config_dir() {
let canonical = Config::config_dir();
for legacy in legacy_config_dirs(&canonical) {
let moved = migrate_between(&legacy, &canonical);
if moved > 0 {
log::info!(
"Migrated {moved} legacy config file(s)/dir(s) from {legacy:?} to {canonical:?}"
);
}
let _ = std::fs::remove_dir(&legacy);
}
}
fn legacy_config_dirs(canonical: &Path) -> Vec<PathBuf> {
let candidates = [
dirs::config_dir().map(|dir| dir.join("par-term")),
dirs::home_dir().map(|home| home.join(".config").join("par-term")),
];
let mut out: Vec<PathBuf> = Vec::new();
for candidate in candidates.into_iter().flatten() {
if candidate != canonical && !out.contains(&candidate) {
out.push(candidate);
}
}
out
}
fn migrate_between(legacy: &Path, canonical: &Path) -> usize {
let entries = match std::fs::read_dir(legacy) {
Ok(e) => e,
Err(_) => return 0,
};
let _ = std::fs::create_dir_all(canonical);
let mut moved = 0;
for entry in entries.flatten() {
let dest = canonical.join(entry.file_name());
if dest.exists() {
continue; }
match std::fs::rename(entry.path(), &dest) {
Ok(()) => moved += 1,
Err(e) => log::warn!(
"Could not migrate legacy config entry {:?} -> {:?}: {e} (left in place)",
entry.path(),
dest,
),
}
}
moved
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn canonical_is_never_a_migration_source() {
let canonical = Config::config_dir();
let legacy = legacy_config_dirs(&canonical);
assert!(
!legacy.contains(&canonical),
"migrating a directory into itself would be a no-op at best: {legacy:?}"
);
let mut unique = legacy.clone();
unique.sort();
unique.dedup();
assert_eq!(
unique.len(),
legacy.len(),
"duplicate legacy roots: {legacy:?}"
);
}
#[test]
fn dot_config_is_a_legacy_source_when_canonical_moves_away() {
let canonical = PathBuf::from("/an/xdg/root/par-term");
let legacy = legacy_config_dirs(&canonical);
if let Some(home) = dirs::home_dir() {
assert!(
legacy.contains(&home.join(".config").join("par-term")),
"~/.config/par-term must be migrated when XDG_CONFIG_HOME points elsewhere: {legacy:?}"
);
}
}
#[test]
fn moves_all_when_canonical_empty() {
let legacy = tempdir().unwrap();
let canonical = tempdir().unwrap();
fs::write(legacy.path().join("profiles.yaml"), "profiles").unwrap();
fs::write(legacy.path().join("arrangements.yaml"), "arrangements").unwrap();
fs::create_dir_all(legacy.path().join("sounds").join("sub")).unwrap();
fs::write(legacy.path().join("sounds").join("a.wav"), "audio").unwrap();
let moved = migrate_between(legacy.path(), canonical.path());
assert_eq!(moved, 3); assert!(canonical.path().join("profiles.yaml").exists());
assert!(canonical.path().join("arrangements.yaml").exists());
assert!(canonical.path().join("sounds").join("a.wav").exists());
assert!(canonical.path().join("sounds").join("sub").exists());
assert!(legacy.path().read_dir().unwrap().next().is_none());
}
#[test]
fn does_not_overwrite_existing_canonical() {
let legacy = tempdir().unwrap();
let canonical = tempdir().unwrap();
fs::write(legacy.path().join("profiles.yaml"), "stale-profiles").unwrap();
fs::create_dir(legacy.path().join("sounds")).unwrap();
fs::write(canonical.path().join("profiles.yaml"), "current-profiles").unwrap();
let moved = migrate_between(legacy.path(), canonical.path());
assert_eq!(moved, 1); assert_eq!(
fs::read_to_string(canonical.path().join("profiles.yaml")).unwrap(),
"current-profiles",
);
assert!(canonical.path().join("sounds").exists());
assert!(legacy.path().join("profiles.yaml").exists());
assert!(!legacy.path().join("sounds").exists());
}
#[test]
fn missing_legacy_is_noop() {
let legacy = tempdir().unwrap();
let canonical = tempdir().unwrap();
let absent = legacy.path().join("does-not-exist");
assert!(!absent.exists());
assert_eq!(migrate_between(&absent, canonical.path()), 0);
}
#[test]
fn empty_legacy_moves_nothing() {
let legacy = tempdir().unwrap();
let canonical = tempdir().unwrap();
assert_eq!(migrate_between(legacy.path(), canonical.path()), 0);
assert!(canonical.path().read_dir().unwrap().next().is_none());
}
#[test]
fn creates_canonical_dir_if_missing() {
let legacy = tempdir().unwrap();
fs::write(legacy.path().join("arrangements.yaml"), "x").unwrap();
let canonical = legacy.path().join("sibling").join("canonical");
assert!(!canonical.exists());
let moved = migrate_between(legacy.path(), &canonical);
assert_eq!(moved, 1);
assert!(canonical.join("arrangements.yaml").exists());
}
}