mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-29 13:16:50 -06:00
Engine: a macro renamed on a name clash keeps its name within the length limit
When a file held two macros with the same name, the second was renamed "name (2)" without room for the suffix. Two macros with the same 255-character name both opened, but the second name grew to 259 characters, was saved that way, and the next open dropped that macro as over the limits. unique_name now shortens the name to make room for the suffix, and load checks the macro again after renaming it.
This commit is contained in:
@@ -128,12 +128,14 @@ pub fn load(wb: &mut Workbook) -> Vec<String> {
|
||||
bad += 1;
|
||||
continue;
|
||||
}
|
||||
match m.check_limits().and_then(|()| check_size(&m)) {
|
||||
Ok(()) => {
|
||||
let mut m = m;
|
||||
m.name = unique_name(wb, m.name.trim());
|
||||
wb.macros.push(m);
|
||||
}
|
||||
let mut m = m;
|
||||
let checked = m.check_limits().and_then(|()| {
|
||||
m.name = unique_name(wb, m.name.trim());
|
||||
// The macro as it is kept, renamed, must still pass, or the next save would lose it.
|
||||
m.check_limits().and_then(|()| check_size(&m))
|
||||
});
|
||||
match checked {
|
||||
Ok(()) => wb.macros.push(m),
|
||||
Err(_) => bad += 1,
|
||||
}
|
||||
}
|
||||
@@ -150,13 +152,22 @@ pub fn load(wb: &mut Workbook) -> Vec<String> {
|
||||
warnings
|
||||
}
|
||||
|
||||
/// `name`, or `name (2)`, `name (3)`… if a macro already has it.
|
||||
/// `name`, or `name (2)`, `name (3)`… if a macro already has it. A long name is shortened to make
|
||||
/// room for the suffix, so the result stays within [`gridcraft_model::MAX_MACRO_NAME`] characters.
|
||||
pub fn unique_name(wb: &Workbook, name: &str) -> String {
|
||||
let name = if name.is_empty() { "Macro" } else { name };
|
||||
if find(wb, name).is_none() {
|
||||
return name.to_string();
|
||||
}
|
||||
(2..).map(|n| format!("{name} ({n})")).find(|n| find(wb, n).is_none()).unwrap_or_else(|| name.to_string())
|
||||
(2..)
|
||||
.map(|n| {
|
||||
let suffix = format!(" ({n})");
|
||||
let keep = gridcraft_model::MAX_MACRO_NAME.saturating_sub(suffix.chars().count());
|
||||
let base: String = name.chars().take(keep).collect();
|
||||
format!("{}{suffix}", base.trim_end())
|
||||
})
|
||||
.find(|n| find(wb, n).is_none())
|
||||
.unwrap_or_else(|| name.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -190,6 +190,13 @@ fn names() {
|
||||
assert_eq!(unique_name(&wb, "macro1"), "macro1 (2)");
|
||||
assert_eq!(unique_name(&wb, "Other"), "Other");
|
||||
assert_eq!(unique_name(&wb, ""), "Macro");
|
||||
// A long name is shortened to make room for the suffix.
|
||||
let long = "y".repeat(MAX_MACRO_NAME);
|
||||
wb.macros.push(Macro { name: long.clone(), ..Macro::default() });
|
||||
let renamed = unique_name(&wb, &long);
|
||||
assert_eq!(renamed, format!("{} (2)", "y".repeat(MAX_MACRO_NAME - 4)));
|
||||
wb.macros.push(Macro { name: renamed, ..Macro::default() });
|
||||
assert_eq!(unique_name(&wb, &long).chars().count(), MAX_MACRO_NAME);
|
||||
assert_eq!(find(&wb, " MACRO1 "), Some(0));
|
||||
assert_eq!(find(&wb, "Macro2"), None);
|
||||
}
|
||||
@@ -206,3 +213,24 @@ fn json_field_macros_get_the_name_check_too() {
|
||||
assert_eq!(names, ["Fine"]);
|
||||
assert!(warnings.len() == 1 && warnings[0].starts_with("1 macro "), "{warnings:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_names_at_the_length_limit_survive_save_and_reopen() {
|
||||
// Two macros with the same 255-character name: the second is renamed within the limit, so a
|
||||
// save and a second open keep both.
|
||||
let long = "é".repeat(MAX_MACRO_NAME);
|
||||
for file in ["dup.json", "dup.xlsx"] {
|
||||
let mut wb = Workbook::new();
|
||||
wb.macros.push(Macro { name: long.clone(), ..Macro::default() });
|
||||
wb.macros.push(Macro { name: long.clone(), description: "second".into(), ..Macro::default() });
|
||||
let (first, warnings) = crate::io::open_bytes(file, &crate::io::save_bytes(&wb, file).unwrap()).unwrap();
|
||||
assert!(warnings.is_empty(), "{file}: {warnings:?}");
|
||||
assert_eq!(first.macros.len(), 2, "{file}");
|
||||
let renamed = &first.macros[1].name;
|
||||
assert_eq!(renamed.chars().count(), MAX_MACRO_NAME, "{file}");
|
||||
assert!(renamed.ends_with(" (2)") && renamed.starts_with("éé"), "{file}: {renamed}");
|
||||
let (again, warnings) = crate::io::open_bytes(file, &crate::io::save_bytes(&first, file).unwrap()).unwrap();
|
||||
assert!(warnings.is_empty(), "{file}: {warnings:?}");
|
||||
assert_eq!(again.macros, first.macros, "{file}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user