mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 18:12:11 +00:00
Don't replace an .ase file whose owner isn't known
After a restart an .ase file called COM¹- could have been saved as COM¹ or as COM¹-, and the same goes for its numbered files. Such a file is kept until it is saved here, so a library named like that gets a new file, once per run. Other names go on replacing their own file.
This commit is contained in:
committed by
bflatastic
parent
a17efec894
commit
850fd620f9
@@ -222,14 +222,23 @@ impl<L: LibraryFile> Libraries<L> {
|
||||
let device = crate::file_access::is_device_name;
|
||||
let id = format!("user/{file}").to_lowercase();
|
||||
let fallback = |other: &str| library_file_name(other, ext).to_lowercase() != file.to_lowercase();
|
||||
let owner = |e: &Extra<L>| self.saved_as.get(&e.info.id.to_lowercase()).map_or(e.lib.name(), String::as_str).to_string();
|
||||
// A format with no title reads its name from the file name, so a file a device name could
|
||||
// have made has no known owner until it is saved here.
|
||||
let untitled = L::read(bytes, "\0").is_ok_and(|lib| lib.name() == "\0");
|
||||
let owner = |e: &Extra<L>| match self.saved_as.get(&e.info.id.to_lowercase()) {
|
||||
Some(saved) => Some(saved.clone()),
|
||||
None if untitled && device_file_name(e.lib.name()) => None,
|
||||
None => Some(e.lib.name().to_string()),
|
||||
};
|
||||
let held = self.extra.iter().any(|e| {
|
||||
let other = owner(e);
|
||||
e.info.id.to_lowercase() == id && other != name && (device(name) || device(&other) || fallback(&other))
|
||||
e.info.id.to_lowercase() == id && owner(e).is_none_or(|other| other != name && (device(name) || device(&other) || fallback(&other)))
|
||||
});
|
||||
if held {
|
||||
// Saved again, it goes back to the file it was given the first time.
|
||||
let own = self.extra.iter().find(|e| e.info.category == "user" && owner(e) == name && extension(&e.info.id) == extension(&file));
|
||||
let own = self
|
||||
.extra
|
||||
.iter()
|
||||
.find(|e| e.info.category == "user" && owner(e).as_deref() == Some(name) && extension(&e.info.id) == extension(&file));
|
||||
file = match own.and_then(|e| e.info.id.strip_prefix("user/")) {
|
||||
Some(own) => own.to_string(),
|
||||
None => {
|
||||
@@ -379,6 +388,13 @@ pub fn library_file_name(name: &str, ext: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is `stem` a file name [`library_file_name`] gives a device name (`COM¹-`, `COM¹-.x`), or one of
|
||||
/// those with the number [`free_name`] adds (`COM¹- 2`)?
|
||||
fn device_file_name(stem: &str) -> bool {
|
||||
let base = stem.rsplit_once(' ').filter(|(_, n)| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit())).map_or(stem, |(base, _)| base);
|
||||
base.split('.').next().and_then(|first| first.strip_suffix('-')).is_some_and(crate::file_access::is_device_name)
|
||||
}
|
||||
|
||||
/// The library files with extensions `exts` in folder `dir`, sorted by name.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn library_files(dir: &str, exts: &[&str]) -> Vec<String> {
|
||||
|
||||
@@ -794,7 +794,7 @@ fn a_library_named_like_a_device_keeps_the_library_with_its_file_name() {
|
||||
|
||||
/// `.ase` keeps no title, so a library's name comes back from its file name. Whichever is saved
|
||||
/// first, `COM¹` and `COM¹-` keep a file each, and saving either again replaces only itself. After
|
||||
/// a restart `COM¹` can't be told from `COM¹-`, and gets a new file, not the other's.
|
||||
/// a restart neither can be told from the other, so each gets a new file and replaces neither.
|
||||
#[test]
|
||||
fn device_named_libraries_without_a_title_keep_their_own_files() {
|
||||
for (first, second) in [("COM¹", "COM¹-"), ("COM¹-", "COM¹")] {
|
||||
@@ -813,9 +813,21 @@ fn device_named_libraries_without_a_title_keep_their_own_files() {
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 2);
|
||||
// The next time the app starts.
|
||||
s.swatch_libraries.set_user_dir(Some(dir.to_string_lossy().to_string()));
|
||||
let again = save(&mut s, "COM¹");
|
||||
assert!(again != a && again != b, "{again}");
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 3);
|
||||
for (name, count) in [(first, 3), (second, 4)] {
|
||||
let again = save(&mut s, name);
|
||||
assert!(again != a && again != b, "{name}: {again}");
|
||||
assert_eq!(save(&mut s, name), again, "saving {name} again replaces {name}");
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), count);
|
||||
}
|
||||
// The numbered file is as unknown as the first.
|
||||
s.swatch_libraries.set_user_dir(Some(dir.to_string_lossy().to_string()));
|
||||
let numbered = b.strip_prefix("user/").unwrap().strip_suffix(".ase").unwrap().to_string();
|
||||
assert_ne!(save(&mut s, &numbered), b);
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 5);
|
||||
// Any other name goes on replacing its own file after a restart.
|
||||
let other = save(&mut s, "Greys");
|
||||
s.swatch_libraries.set_user_dir(Some(dir.to_string_lossy().to_string()));
|
||||
assert_eq!(save(&mut s, "Greys"), other);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user