1use camino::{Utf8Component, Utf8Path, Utf8PathBuf};
4
5pub fn mirror_into_backup(backup_root: &Utf8Path, abs_target: &Utf8Path) -> Utf8PathBuf {
13 let mut out = backup_root.to_path_buf();
14 for component in abs_target.components() {
15 match component {
16 Utf8Component::Prefix(p) => {
17 let s = p.as_str().trim_end_matches(':');
18 if !s.is_empty() {
19 out.push(s);
20 }
21 }
22 Utf8Component::RootDir | Utf8Component::CurDir => {}
23 Utf8Component::ParentDir => {}
24 Utf8Component::Normal(s) => {
25 out.push(s);
26 }
27 }
28 }
29 out
30}
31
32pub fn append_timestamp(path: &Utf8Path, ts: &str) -> Utf8PathBuf {
40 let parent = path.parent().map(Utf8PathBuf::from).unwrap_or_default();
41 let file_name = path.file_name().unwrap_or("");
42
43 let (stem, ext) = match (path.file_stem(), path.extension()) {
44 (Some(stem), Some(ext)) if !file_name.starts_with('.') => (stem, Some(ext)),
45 _ => (file_name, None),
46 };
47
48 let new_name = match ext {
49 Some(ext) => format!("{stem}_{ts}.{ext}"),
50 None => format!("{stem}_{ts}"),
51 };
52 parent.join(new_name)
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn mirror_unix_absolute() {
61 let r = mirror_into_backup(
62 Utf8Path::new("/dotfiles/.yui/backup"),
63 Utf8Path::new("/home/u/.config/foo.toml"),
64 );
65 assert_eq!(
66 r,
67 Utf8PathBuf::from("/dotfiles/.yui/backup/home/u/.config/foo.toml")
68 );
69 }
70
71 #[test]
72 fn append_with_extension() {
73 let r = append_timestamp(Utf8Path::new("a/b.yml"), "20260429_143022123");
74 assert_eq!(r, Utf8PathBuf::from("a/b_20260429_143022123.yml"));
75 }
76
77 #[test]
78 fn append_no_extension() {
79 let r = append_timestamp(Utf8Path::new("a/b"), "20260429_143022123");
80 assert_eq!(r, Utf8PathBuf::from("a/b_20260429_143022123"));
81 }
82
83 #[test]
84 fn append_dotfile() {
85 let r = append_timestamp(Utf8Path::new(".gitconfig"), "20260429_143022123");
86 assert_eq!(r, Utf8PathBuf::from(".gitconfig_20260429_143022123"));
87 }
88}