diff --git a/crates/doc/src/assets.rs b/crates/doc/src/assets.rs index 824f3ebea..f17783b1e 100644 --- a/crates/doc/src/assets.rs +++ b/crates/doc/src/assets.rs @@ -34,7 +34,16 @@ impl Document { /// The name of the next unnamed asset: one more than the highest `Asset N` so far. pub fn next_asset_name(&self) -> String { - let n = self.assets.iter().filter_map(|a| a.name.strip_prefix(ASSET)?.parse::().ok()).max().unwrap_or(0); + let n = self + .assets + .iter() + .filter_map(|a| { + let digits = a.name.strip_prefix(ASSET)?; + // `parse` alone would accept a leading `+`. + digits.bytes().all(|b| b.is_ascii_digit()).then(|| digits.parse::().ok()).flatten() + }) + .max() + .unwrap_or(0); format!("{ASSET}{}", n.saturating_add(1)) } @@ -61,3 +70,23 @@ impl Document { v.into_iter().map(|(_, id)| id).collect() } } + +#[cfg(test)] +mod tests { + use super::*; + + fn name_after(names: &[&str]) -> String { + let mut doc = Document::new(100.0, 100.0); + for (i, n) in names.iter().enumerate() { + doc.assets.push(ExportAsset { id: i as u64 + 1, name: (*n).to_string(), nodes: vec![], format_3d: None }); + } + doc.next_asset_name() + } + + #[test] + fn next_asset_name_ignores_non_digit_suffixes() { + assert_eq!(name_after(&["Asset +5"]), "Asset 1"); + assert_eq!(name_after(&["Asset ", "Asset 5x"]), "Asset 1"); + assert_eq!(name_after(&["Asset 5"]), "Asset 6"); + } +}