diff --git a/crates/catalog/src/folders.rs b/crates/catalog/src/folders.rs index e8637ceca..e2d3b312d 100644 --- a/crates/catalog/src/folders.rs +++ b/crates/catalog/src/folders.rs @@ -223,10 +223,15 @@ impl Catalog { if root == dest { return Vec::new(); } + // a folder located below itself: what is already at or below `to` belongs to the folders + // of the library there, not to the folder located, so it is neither carried nor cleared + // (the record of `from` itself still lands at `to`, replacing the one there) + let nested = !clear_destination && key_within(&dest, &root); + let carried = |k: &str| key_within(k, &root) && !(nested && key_within(k, &dest)); let moved: Vec<(String, FolderRecord)> = self .folder_records .iter() - .filter(|(k, _)| key_within(k, &root)) + .filter(|(k, _)| carried(k)) .filter_map(|(k, r)| { let rest = folder_rest(k, &root)?; let base = dest.trim_end_matches('/'); @@ -236,7 +241,7 @@ impl Catalog { .collect(); // what was known at the old place goes, and so does what is left at the new one (also // when the moved folder carries nothing: the folder now there is not the one it was about) - let stale = self.folder_records.keys().filter(|k| key_within(k, &root) || (clear_destination && key_within(k, &dest))); + let stale = self.folder_records.keys().filter(|k| carried(k) || (clear_destination && key_within(k, &dest))); let mut ops: Vec = stale.map(|k| Op::SetFolderRecord { folder: k.clone(), record: None }).collect(); ops.extend(moved.into_iter().map(|(k, r)| Op::SetFolderRecord { folder: k, record: Some(r) })); ops diff --git a/crates/catalog/src/tests_folder_records.rs b/crates/catalog/src/tests_folder_records.rs index eb2215ba6..a572db995 100644 --- a/crates/catalog/src/tests_folder_records.rs +++ b/crates/catalog/src/tests_folder_records.rs @@ -191,6 +191,25 @@ fn a_located_folder_with_labels_inside_lands_them_at_the_same_places_below() { assert_eq!(c.folder_color_label("/mnt/nas/trip/day1"), Some(ColorLabel::Green)); } +#[test] +fn a_folder_located_below_itself_leaves_the_records_of_the_folders_already_there_in_place() { + let mut c = library(); + label(&mut c, "/pics/trip", Some(ColorLabel::Red)); + label(&mut c, "/pics/trip/day1", Some(ColorLabel::Green)); + // another folder of the library already sits at the place the folder is said to be at + label(&mut c, "/pics/trip/day1/raw", Some(ColorLabel::Blue)); + let before = c.to_snapshot(); + let ops = c.folder_records_located("/pics/trip", "/pics/trip/day1"); + let undo = c.apply(Op::Batch { ops }).unwrap(); + assert_eq!(c.folder_record("/pics/trip"), None, "what was known at the old place goes"); + assert_eq!(c.folder_color_label("/pics/trip/day1"), Some(ColorLabel::Red), "a record carried over replaces the one at the same place"); + assert_eq!(c.folder_color_label("/pics/trip/day1/raw"), Some(ColorLabel::Blue), "a folder below the destination keeps its own"); + assert_eq!(c.folder_record("/pics/trip/day1/day1"), None, "the record at the destination is not carried below itself"); + assert_eq!(c.folder_record("/pics/trip/day1/day1/raw"), None); + c.apply(undo).unwrap(); + assert_eq!(c.to_snapshot(), before); +} + #[test] fn a_folder_located_where_it_is_or_without_labels_gives_no_ops() { let mut c = library();