mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 14:01:37 +00:00
Update deduplication logic and documentation for suffix handling
- Removed the `luau` dependency from `rokit.toml`. - Updated deduplication logic to append `~2`, `~3`, etc. instead of `~1`, `~2` for naming collisions in various files, ensuring consistent suffix management. - Adjusted comments and documentation across multiple files to reflect the new suffix handling rules. - Added new test cases to validate the updated deduplication behavior, ensuring accurate naming for instances with collisions. These changes enhance the clarity and functionality of the deduplication process, improving overall consistency in naming conventions.
This commit is contained in:
parent
a59d9d497a
commit
d836ebddc8
35 changed files with 349 additions and 281 deletions
|
|
@ -267,7 +267,7 @@ pub fn slugify_name(name: &str) -> String {
|
|||
result
|
||||
}
|
||||
|
||||
/// Appends ~1, ~2, etc. to the slug to avoid filesystem-level collisions.
|
||||
/// Appends ~2, ~3, etc. to the slug to avoid filesystem-level collisions.
|
||||
/// Returns `(deduped_slug, full_fs_name)`.
|
||||
///
|
||||
/// `extension` is `None` for directory middleware, `Some("server.luau")` etc.
|
||||
|
|
@ -294,7 +294,7 @@ pub fn deduplicate_name_with_ext(
|
|||
if !taken_names.contains(&full_name.to_lowercase()) {
|
||||
return (base.to_string(), full_name);
|
||||
}
|
||||
for i in 1.. {
|
||||
for i in 2.. {
|
||||
let candidate_slug = format!("{base}~{i}");
|
||||
let candidate_full = build_full(&candidate_slug);
|
||||
if !taken_names.contains(&candidate_full.to_lowercase()) {
|
||||
|
|
@ -304,7 +304,7 @@ pub fn deduplicate_name_with_ext(
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
/// Legacy wrapper: appends ~1, ~2, etc. to avoid collisions using bare slug
|
||||
/// Legacy wrapper: appends ~2, ~3, etc. to avoid collisions using bare slug
|
||||
/// as the collision key. Used by callers that don't yet use full filesystem
|
||||
/// name dedup (e.g., change_processor.rs renames).
|
||||
pub fn deduplicate_name(base: &str, taken_names: &std::collections::HashSet<String>) -> String {
|
||||
|
|
@ -312,7 +312,7 @@ pub fn deduplicate_name(base: &str, taken_names: &std::collections::HashSet<Stri
|
|||
if !taken_names.contains(&base_lower) {
|
||||
return base.to_string();
|
||||
}
|
||||
for i in 1.. {
|
||||
for i in 2.. {
|
||||
let candidate = format!("{base}~{i}");
|
||||
if !taken_names.contains(&candidate.to_lowercase()) {
|
||||
return candidate;
|
||||
|
|
@ -569,7 +569,7 @@ mod tests {
|
|||
#[test]
|
||||
fn dedup_single_collision() {
|
||||
let taken: HashSet<String> = ["foo".to_string()].into_iter().collect();
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~1");
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -589,14 +589,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dedup_gap_in_suffixes() {
|
||||
let taken: HashSet<String> = ["foo", "foo~2"].into_iter().map(String::from).collect();
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~1");
|
||||
let taken: HashSet<String> = ["foo", "foo~3"].into_iter().map(String::from).collect();
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dedup_natural_vs_slug_collision() {
|
||||
let taken: HashSet<String> = ["hey_bro".to_string()].into_iter().collect();
|
||||
assert_eq!(deduplicate_name("Hey_Bro", &taken), "Hey_Bro~1");
|
||||
assert_eq!(deduplicate_name("Hey_Bro", &taken), "Hey_Bro~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -609,16 +609,16 @@ mod tests {
|
|||
fn dedup_case_insensitive() {
|
||||
// "foo" is taken, "Foo" should collide (case-insensitive filesystem)
|
||||
let taken: HashSet<String> = ["foo".to_string()].into_iter().collect();
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~1");
|
||||
assert_eq!(deduplicate_name("FOO", &taken), "FOO~1");
|
||||
assert_eq!(deduplicate_name("foo", &taken), "foo~1");
|
||||
assert_eq!(deduplicate_name("Foo", &taken), "Foo~2");
|
||||
assert_eq!(deduplicate_name("FOO", &taken), "FOO~2");
|
||||
assert_eq!(deduplicate_name("foo", &taken), "foo~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dedup_case_only_difference() {
|
||||
// Two instances: "MyScript" and "myscript" - second must get ~1
|
||||
// Two instances: "MyScript" and "myscript" - second must get ~2
|
||||
let taken: HashSet<String> = ["myscript".to_string()].into_iter().collect();
|
||||
assert_eq!(deduplicate_name("MyScript", &taken), "MyScript~1");
|
||||
assert_eq!(deduplicate_name("MyScript", &taken), "MyScript~2");
|
||||
}
|
||||
|
||||
// ── name_needs_slugify ──────────────────────────────────────────────
|
||||
|
|
@ -831,7 +831,7 @@ mod tests {
|
|||
|
||||
let (filename, needs_meta, _dk) =
|
||||
name_for_inst(Middleware::ModuleScript, child, None, &taken).unwrap();
|
||||
assert_eq!(filename.as_ref(), "Foo~1.luau");
|
||||
assert_eq!(filename.as_ref(), "Foo~2.luau");
|
||||
assert!(needs_meta, "deduped name differs from original");
|
||||
}
|
||||
|
||||
|
|
@ -844,7 +844,7 @@ mod tests {
|
|||
|
||||
let (filename, needs_meta, _dk) =
|
||||
name_for_inst(Middleware::Dir, child, None, &taken).unwrap();
|
||||
assert_eq!(filename.as_ref(), "Stuff~1");
|
||||
assert_eq!(filename.as_ref(), "Stuff~2");
|
||||
assert!(needs_meta);
|
||||
}
|
||||
|
||||
|
|
@ -858,7 +858,7 @@ mod tests {
|
|||
|
||||
let (filename, needs_meta, _dk) =
|
||||
name_for_inst(Middleware::ModuleScript, child, None, &taken).unwrap();
|
||||
assert_eq!(filename.as_ref(), "Hey_Bro~1.luau");
|
||||
assert_eq!(filename.as_ref(), "Hey_Bro~2.luau");
|
||||
assert!(needs_meta);
|
||||
}
|
||||
|
||||
|
|
@ -1145,7 +1145,7 @@ mod tests {
|
|||
fn nfi_slug_collides_with_existing_dir() {
|
||||
// "Hey/Bro" slugifies to "Hey_Bro", which is already taken as a dir name
|
||||
let (f, m) = nfi("Hey/Bro", Middleware::Dir, &["hey_bro"]);
|
||||
assert_eq!(f, "Hey_Bro~1");
|
||||
assert_eq!(f, "Hey_Bro~2");
|
||||
assert!(m);
|
||||
}
|
||||
|
||||
|
|
@ -1153,15 +1153,15 @@ mod tests {
|
|||
fn nfi_slug_collides_with_natural_dir() {
|
||||
// Dir "Hey_Bro" exists naturally, then "Hey:Bro" slugifies to same
|
||||
let (f, m) = nfi("Hey:Bro", Middleware::Dir, &["hey_bro"]);
|
||||
assert_eq!(f, "Hey_Bro~1");
|
||||
assert_eq!(f, "Hey_Bro~2");
|
||||
assert!(m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nfi_two_slugs_collide_dir() {
|
||||
// Both "A/B" and "A:B" slugify to "A_B". First taken, second gets ~1
|
||||
// Both "A/B" and "A:B" slugify to "A_B". First taken, second gets ~2
|
||||
let (f, m) = nfi("A:B", Middleware::Dir, &["a_b"]);
|
||||
assert_eq!(f, "A_B~1");
|
||||
assert_eq!(f, "A_B~2");
|
||||
assert!(m);
|
||||
}
|
||||
|
||||
|
|
@ -1173,9 +1173,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn nfi_clean_name_dedup_still_needs_meta_dir() {
|
||||
// Clean name "Foo", but "foo" is already taken → dedup adds ~1 → needs meta
|
||||
// Clean name "Foo", but "foo" is already taken → dedup adds ~2 → needs meta
|
||||
let (f, m) = nfi("Foo", Middleware::Dir, &["foo"]);
|
||||
assert_eq!(f, "Foo~1");
|
||||
assert_eq!(f, "Foo~2");
|
||||
assert!(
|
||||
m,
|
||||
"dedup suffix means filesystem name differs from instance name"
|
||||
|
|
@ -1185,7 +1185,7 @@ mod tests {
|
|||
#[test]
|
||||
fn nfi_case_collision_dir() {
|
||||
let (f, m) = nfi("Assets", Middleware::Dir, &["assets"]);
|
||||
assert_eq!(f, "Assets~1");
|
||||
assert_eq!(f, "Assets~2");
|
||||
assert!(m);
|
||||
}
|
||||
|
||||
|
|
@ -1193,7 +1193,7 @@ mod tests {
|
|||
fn nfi_slug_collision_file_middleware() {
|
||||
// For file middleware, dedup compares full filesystem name against taken.
|
||||
let (f, m) = nfi("Hey/Bro", Middleware::ModuleScript, &["hey_bro.luau"]);
|
||||
assert_eq!(f, "Hey_Bro~1.luau");
|
||||
assert_eq!(f, "Hey_Bro~2.luau");
|
||||
assert!(m);
|
||||
}
|
||||
|
||||
|
|
@ -1222,7 +1222,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["Foo", "Foo"]);
|
||||
assert_eq!(r[0].1, "Foo");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "Foo~1");
|
||||
assert_eq!(r[1].1, "Foo~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1230,8 +1230,8 @@ mod tests {
|
|||
fn ordering_three_identical_names() {
|
||||
let r = process_siblings_dir(&["X", "X", "X"]);
|
||||
assert_eq!(r[0].1, "X");
|
||||
assert_eq!(r[1].1, "X~1");
|
||||
assert_eq!(r[2].1, "X~2");
|
||||
assert_eq!(r[1].1, "X~2");
|
||||
assert_eq!(r[2].1, "X~3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1240,7 +1240,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["Hey_Bro", "Hey/Bro"]);
|
||||
assert_eq!(r[0].1, "Hey_Bro");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "Hey_Bro~1");
|
||||
assert_eq!(r[1].1, "Hey_Bro~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1250,7 +1250,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["Hey/Bro", "Hey_Bro"]);
|
||||
assert_eq!(r[0].1, "Hey_Bro");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "Hey_Bro~1");
|
||||
assert_eq!(r[1].1, "Hey_Bro~2");
|
||||
assert!(r[1].2, "dedup suffix → needs meta even for natural name");
|
||||
}
|
||||
|
||||
|
|
@ -1259,9 +1259,9 @@ mod tests {
|
|||
let r = process_siblings_dir(&["Script", "script", "SCRIPT"]);
|
||||
assert_eq!(r[0].1, "Script");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "script~1");
|
||||
assert_eq!(r[1].1, "script~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "SCRIPT~2");
|
||||
assert_eq!(r[2].1, "SCRIPT~3");
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
||||
|
|
@ -1270,8 +1270,8 @@ mod tests {
|
|||
// All three slugify to "A_B"
|
||||
let r = process_siblings_dir(&["A/B", "A:B", "A*B"]);
|
||||
assert_eq!(r[0].1, "A_B");
|
||||
assert_eq!(r[1].1, "A_B~1");
|
||||
assert_eq!(r[2].1, "A_B~2");
|
||||
assert_eq!(r[1].1, "A_B~2");
|
||||
assert_eq!(r[2].1, "A_B~3");
|
||||
assert!(r[0].2);
|
||||
assert!(r[1].2);
|
||||
assert!(r[2].2);
|
||||
|
|
@ -1293,7 +1293,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["CON", "CON_"]);
|
||||
assert_eq!(r[0].1, "CON_");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "CON_~1");
|
||||
assert_eq!(r[1].1, "CON_~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1302,17 +1302,17 @@ mod tests {
|
|||
let r = process_siblings_dir(&[
|
||||
"Utils", // first claim
|
||||
"utils", // case collision with Utils
|
||||
"Utils", // exact collision (already taken + ~1 taken)
|
||||
"Utils", // exact collision (already taken + ~2 taken)
|
||||
"UTILS", // case collision
|
||||
"Uti/ls", // different slug "Uti_ls", no collision
|
||||
]);
|
||||
assert_eq!(r[0].1, "Utils");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "utils~1");
|
||||
assert_eq!(r[1].1, "utils~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "Utils~2");
|
||||
assert_eq!(r[2].1, "Utils~3");
|
||||
assert!(r[2].2);
|
||||
assert_eq!(r[3].1, "UTILS~3");
|
||||
assert_eq!(r[3].1, "UTILS~4");
|
||||
assert!(r[3].2);
|
||||
assert_eq!(r[4].1, "Uti_ls");
|
||||
assert!(r[4].2);
|
||||
|
|
@ -1323,20 +1323,20 @@ mod tests {
|
|||
// All six slugify to "X_Y" as dirs
|
||||
let r = process_siblings_dir(&["X/Y", "X:Y", "X*Y", "X?Y", "X<Y", "X>Y"]);
|
||||
assert_eq!(r[0].1, "X_Y");
|
||||
assert_eq!(r[1].1, "X_Y~1");
|
||||
assert_eq!(r[2].1, "X_Y~2");
|
||||
assert_eq!(r[3].1, "X_Y~3");
|
||||
assert_eq!(r[4].1, "X_Y~4");
|
||||
assert_eq!(r[5].1, "X_Y~5");
|
||||
assert_eq!(r[1].1, "X_Y~2");
|
||||
assert_eq!(r[2].1, "X_Y~3");
|
||||
assert_eq!(r[3].1, "X_Y~4");
|
||||
assert_eq!(r[4].1, "X_Y~5");
|
||||
assert_eq!(r[5].1, "X_Y~6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordering_tilde_in_name_vs_dedup_suffix() {
|
||||
// "Foo~1" (tilde slugified to "Foo_1") vs dedup suffix "Foo~1"
|
||||
// The slugified "Foo~1" → "Foo_1" which is different from dedup "Foo~1"
|
||||
// "Foo~1" (tilde slugified to "Foo_1") vs dedup suffix "Foo~2"
|
||||
// The slugified "Foo~1" → "Foo_1" which is different from dedup "Foo~2"
|
||||
let r = process_siblings_dir(&["Foo", "Foo", "Foo~1"]);
|
||||
assert_eq!(r[0].1, "Foo");
|
||||
assert_eq!(r[1].1, "Foo~1"); // dedup suffix
|
||||
assert_eq!(r[1].1, "Foo~2"); // dedup suffix
|
||||
assert_eq!(r[2].1, "Foo_1"); // tilde was slugified to _
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
|
@ -1350,9 +1350,9 @@ mod tests {
|
|||
]);
|
||||
assert_eq!(r[0].1, "Leading");
|
||||
assert!(r[0].2, "leading space was stripped");
|
||||
assert_eq!(r[1].1, "Leading~1");
|
||||
assert_eq!(r[1].1, "Leading~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "Leading~2");
|
||||
assert_eq!(r[2].1, "Leading~3");
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
||||
|
|
@ -1556,9 +1556,9 @@ mod tests {
|
|||
#[test]
|
||||
fn disk_seed_prevents_overwrite_of_existing_dir() {
|
||||
// "Utils" directory exists on disk. New instance "Utils" (dir)
|
||||
// must get "Utils~1".
|
||||
// must get "Utils~2".
|
||||
let r = process_new_children_with_disk_seed(&["utils"], &[("Utils", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Utils~1");
|
||||
assert_eq!(r[0].1, "Utils~2");
|
||||
assert!(r[0].2);
|
||||
}
|
||||
|
||||
|
|
@ -1566,16 +1566,16 @@ mod tests {
|
|||
fn disk_seed_prevents_overwrite_of_existing_dir_case_insensitive() {
|
||||
// "SCRIPTS" directory on disk, new instance "Scripts" (dir) must dedup.
|
||||
let r = process_new_children_with_disk_seed(&["scripts"], &[("Scripts", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Scripts~1");
|
||||
assert_eq!(r[0].1, "Scripts~2");
|
||||
assert!(r[0].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disk_seed_slug_collision_with_existing_dir() {
|
||||
// "Hey_Bro" directory on disk. New instance "Hey/Bro" slugifies
|
||||
// to "Hey_Bro" — must dedup to "Hey_Bro~1".
|
||||
// to "Hey_Bro" — must dedup to "Hey_Bro~2".
|
||||
let r = process_new_children_with_disk_seed(&["hey_bro"], &[("Hey/Bro", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Hey_Bro~1");
|
||||
assert_eq!(r[0].1, "Hey_Bro~2");
|
||||
assert!(r[0].2);
|
||||
}
|
||||
|
||||
|
|
@ -1591,9 +1591,9 @@ mod tests {
|
|||
("NewThing", Middleware::Dir),
|
||||
],
|
||||
);
|
||||
assert_eq!(r[0].1, "Utils~1", "Utils collides with utils/ on disk");
|
||||
assert_eq!(r[0].1, "Utils~2", "Utils collides with utils/ on disk");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "Config~1", "Config collides with config/ on disk");
|
||||
assert_eq!(r[1].1, "Config~2", "Config collides with config/ on disk");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "NewThing", "NewThing is clean");
|
||||
assert!(!r[2].2);
|
||||
|
|
@ -1607,7 +1607,7 @@ mod tests {
|
|||
&["stuff", "stuff.meta.json5", "other.luau"],
|
||||
&[("Stuff", Middleware::Dir)],
|
||||
);
|
||||
assert_eq!(r[0].1, "Stuff~1");
|
||||
assert_eq!(r[0].1, "Stuff~2");
|
||||
assert!(r[0].2);
|
||||
}
|
||||
|
||||
|
|
@ -1620,7 +1620,7 @@ mod tests {
|
|||
&["mymodule.luau"],
|
||||
&[("MyModule", Middleware::ModuleScript)],
|
||||
);
|
||||
assert_eq!(r[0].1, "MyModule~1.luau");
|
||||
assert_eq!(r[0].1, "MyModule~2.luau");
|
||||
assert!(r[0].2, "dedup suffix means needs meta");
|
||||
}
|
||||
|
||||
|
|
@ -1628,7 +1628,7 @@ mod tests {
|
|||
fn disk_seed_file_middleware_sibling_slug_collision_deduped() {
|
||||
// Two file-middleware children whose names slugify to the same base.
|
||||
// Since taken_names accumulates dedup_keys (bare slugs), the second
|
||||
// child correctly detects the collision and gets ~1.
|
||||
// child correctly detects the collision and gets ~2.
|
||||
let r = process_new_children_with_disk_seed(
|
||||
&[],
|
||||
&[
|
||||
|
|
@ -1638,7 +1638,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(r[0].1, "A_B.luau");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "A_B~1.luau");
|
||||
assert_eq!(r[1].1, "A_B~2.luau");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1727,12 +1727,12 @@ mod tests {
|
|||
fn project_siblings_same_slug_different_names() {
|
||||
// Two new project children: "A/B" and "A:B" both slug to "A_B".
|
||||
// Before fix: both got "A_B" path (collision!).
|
||||
// After fix: second gets "A_B~1".
|
||||
// After fix: second gets "A_B~2".
|
||||
let r =
|
||||
process_project_siblings(&[], &[("A/B", Middleware::Dir), ("A:B", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "A_B");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "A_B~1");
|
||||
assert_eq!(r[1].1, "A_B~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1746,13 +1746,13 @@ mod tests {
|
|||
);
|
||||
assert_eq!(r[0].1, "Hey_Bro");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "Hey_Bro~1");
|
||||
assert_eq!(r[1].1, "Hey_Bro~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_siblings_three_way_slug_collision() {
|
||||
// Three siblings all slug to "X_Y" — must get X_Y, X_Y~1, X_Y~2.
|
||||
// Three siblings all slug to "X_Y" — must get X_Y, X_Y~2, X_Y~3.
|
||||
let r = process_project_siblings(
|
||||
&[],
|
||||
&[
|
||||
|
|
@ -1762,22 +1762,22 @@ mod tests {
|
|||
],
|
||||
);
|
||||
assert_eq!(r[0].1, "X_Y");
|
||||
assert_eq!(r[1].1, "X_Y~1");
|
||||
assert_eq!(r[2].1, "X_Y~2");
|
||||
assert_eq!(r[1].1, "X_Y~2");
|
||||
assert_eq!(r[2].1, "X_Y~3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_siblings_disk_plus_siblings_collision_dir() {
|
||||
// "Helper" directory already on disk. Two new dir siblings: "Helper" x2.
|
||||
// First → "Helper~1" (disk has "helper").
|
||||
// Second → "Helper~2" (both disk and first sibling taken).
|
||||
// First → "Helper~2" (disk has "helper").
|
||||
// Second → "Helper~3" (both disk and first sibling taken).
|
||||
let r = process_project_siblings(
|
||||
&["helper"],
|
||||
&[("Helper", Middleware::Dir), ("Helper", Middleware::Dir)],
|
||||
);
|
||||
assert_eq!(r[0].1, "Helper~1");
|
||||
assert_eq!(r[0].1, "Helper~2");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "Helper~2");
|
||||
assert_eq!(r[1].1, "Helper~3");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1795,11 +1795,11 @@ mod tests {
|
|||
);
|
||||
assert_eq!(r[0].1, "Test");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "test~1");
|
||||
assert_eq!(r[1].1, "test~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "TEST~2");
|
||||
assert_eq!(r[2].1, "TEST~3");
|
||||
assert!(r[2].2);
|
||||
assert_eq!(r[3].1, "tEsT~3");
|
||||
assert_eq!(r[3].1, "tEsT~4");
|
||||
assert!(r[3].2);
|
||||
}
|
||||
|
||||
|
|
@ -1819,7 +1819,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["CON", "CON/"]);
|
||||
assert_eq!(r[0].1, "CON_");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "CON_~1");
|
||||
assert_eq!(r[1].1, "CON_~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1829,29 +1829,29 @@ mod tests {
|
|||
let r = process_siblings_dir(&["foo.server", "foo/server"]);
|
||||
assert_eq!(r[0].1, "foo_server");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "foo_server~1");
|
||||
assert_eq!(r[1].1, "foo_server~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nightmare_tilde_looks_like_dedup_but_gets_slugified() {
|
||||
// "Foo~1" has tilde slugified to "Foo_1". Then real "Foo" gets
|
||||
// dedup "Foo~1". These should NOT collide because tilde-in-name
|
||||
// dedup "Foo~2". These should NOT collide because tilde-in-name
|
||||
// becomes underscore, but dedup adds actual tilde.
|
||||
let r = process_siblings_dir(&["Foo~1", "Foo", "Foo"]);
|
||||
assert_eq!(r[0].1, "Foo_1", "tilde slugified");
|
||||
assert_eq!(r[1].1, "Foo", "clean, no collision");
|
||||
assert_eq!(r[2].1, "Foo~1", "dedup suffix, different from Foo_1");
|
||||
assert_eq!(r[2].1, "Foo~2", "dedup suffix, different from Foo_1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nightmare_dedup_suffix_avoids_slugified_tilde_name() {
|
||||
// Process "Foo", then "Foo" again (dedup to "Foo~1"), then "Foo~1"
|
||||
// (tilde slugified to "Foo_1"). The dedup "Foo~1" and slug "Foo_1"
|
||||
// Process "Foo", then "Foo" again (dedup to "Foo~2"), then "Foo~1"
|
||||
// (tilde slugified to "Foo_1"). The dedup "Foo~2" and slug "Foo_1"
|
||||
// should coexist.
|
||||
let r = process_siblings_dir(&["Foo", "Foo", "Foo~1"]);
|
||||
assert_eq!(r[0].1, "Foo");
|
||||
assert_eq!(r[1].1, "Foo~1");
|
||||
assert_eq!(r[1].1, "Foo~2");
|
||||
assert_eq!(r[2].1, "Foo_1");
|
||||
// All three have distinct lowercased names
|
||||
let names: HashSet<String> = r.iter().map(|(_, f, _)| f.to_lowercase()).collect();
|
||||
|
|
@ -1863,7 +1863,7 @@ mod tests {
|
|||
// "カフェ/Bar" → "カフェ_Bar", "カフェ:Bar" → "カフェ_Bar" — collision
|
||||
let r = process_siblings_dir(&["カフェ/Bar", "カフェ:Bar"]);
|
||||
assert_eq!(r[0].1, "カフェ_Bar");
|
||||
assert_eq!(r[1].1, "カフェ_Bar~1");
|
||||
assert_eq!(r[1].1, "カフェ_Bar~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1872,7 +1872,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["🎮 Games", "🎮 Games"]);
|
||||
assert_eq!(r[0].1, "🎮 Games");
|
||||
assert!(!r[0].2, "emoji name is clean");
|
||||
assert_eq!(r[1].1, "🎮 Games~1");
|
||||
assert_eq!(r[1].1, "🎮 Games~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1883,9 +1883,9 @@ mod tests {
|
|||
let r = process_siblings_dir(&["CON", "CON_", "con"]);
|
||||
assert_eq!(r[0].1, "CON_");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "CON_~1");
|
||||
assert_eq!(r[1].1, "CON_~2");
|
||||
assert!(r[1].2, "natural CON_ collides with reserved CON's slug");
|
||||
assert_eq!(r[2].1, "con_~2");
|
||||
assert_eq!(r[2].1, "con_~3");
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
||||
|
|
@ -1895,7 +1895,7 @@ mod tests {
|
|||
let r = process_siblings_dir(&["test.", "test"]);
|
||||
assert_eq!(r[0].1, "test");
|
||||
assert!(r[0].2, "trailing dot stripped → slug differs");
|
||||
assert_eq!(r[1].1, "test~1");
|
||||
assert_eq!(r[1].1, "test~2");
|
||||
assert!(r[1].2);
|
||||
}
|
||||
|
||||
|
|
@ -1905,9 +1905,9 @@ mod tests {
|
|||
let r = process_siblings_dir(&[" A", "A", " A"]);
|
||||
assert_eq!(r[0].1, "A");
|
||||
assert!(r[0].2, "leading space stripped");
|
||||
assert_eq!(r[1].1, "A~1");
|
||||
assert_eq!(r[1].1, "A~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "A~2");
|
||||
assert_eq!(r[2].1, "A~3");
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
||||
|
|
@ -2004,11 +2004,7 @@ mod tests {
|
|||
];
|
||||
let r = process_new_children_with_disk_seed(&["a_b"], &new_children);
|
||||
for (i, (_, filename, needs_meta)) in r.iter().enumerate() {
|
||||
let expected = if i == 0 {
|
||||
"A_B~1".to_string()
|
||||
} else {
|
||||
format!("A_B~{}", i + 1)
|
||||
};
|
||||
let expected = format!("A_B~{}", i + 2);
|
||||
assert_eq!(filename, &expected, "child {i}");
|
||||
assert!(needs_meta, "child {i} needs meta");
|
||||
}
|
||||
|
|
@ -2019,8 +2015,8 @@ mod tests {
|
|||
// Multiple instances with empty names. All slugify to "instance".
|
||||
let r = process_siblings_dir(&["", "", ""]);
|
||||
assert_eq!(r[0].1, "instance");
|
||||
assert_eq!(r[1].1, "instance~1");
|
||||
assert_eq!(r[2].1, "instance~2");
|
||||
assert_eq!(r[1].1, "instance~2");
|
||||
assert_eq!(r[2].1, "instance~3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2028,10 +2024,10 @@ mod tests {
|
|||
// Names that are entirely forbidden chars → all become "instance".
|
||||
let r = process_siblings_dir(&["<>", ":/", "?*", "|\\", "\"~"]);
|
||||
assert_eq!(r[0].1, "instance");
|
||||
assert_eq!(r[1].1, "instance~1");
|
||||
assert_eq!(r[2].1, "instance~2");
|
||||
assert_eq!(r[3].1, "instance~3");
|
||||
assert_eq!(r[4].1, "instance~4");
|
||||
assert_eq!(r[1].1, "instance~2");
|
||||
assert_eq!(r[2].1, "instance~3");
|
||||
assert_eq!(r[3].1, "instance~4");
|
||||
assert_eq!(r[4].1, "instance~5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2039,7 +2035,7 @@ mod tests {
|
|||
// "Hello\x00World" → "Hello_World", "Hello/World" → "Hello_World"
|
||||
let r = process_siblings_dir(&["Hello\x00World", "Hello/World"]);
|
||||
assert_eq!(r[0].1, "Hello_World");
|
||||
assert_eq!(r[1].1, "Hello_World~1");
|
||||
assert_eq!(r[1].1, "Hello_World~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2059,7 +2055,7 @@ mod tests {
|
|||
// "init/meta" → slug "init_meta" → collision!
|
||||
let r = process_siblings_dir(&["init.meta", "init/meta"]);
|
||||
assert_eq!(r[0].1, "init_meta");
|
||||
assert_eq!(r[1].1, "init_meta~1");
|
||||
assert_eq!(r[1].1, "init_meta~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2068,9 +2064,9 @@ mod tests {
|
|||
let r = process_siblings_dir(&["a.project", "a/project", "a_project"]);
|
||||
assert_eq!(r[0].1, "a_project");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "a_project~1");
|
||||
assert_eq!(r[1].1, "a_project~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "a_project~2");
|
||||
assert_eq!(r[2].1, "a_project~3");
|
||||
assert!(r[2].2, "natural name collides with slugified siblings");
|
||||
}
|
||||
|
||||
|
|
@ -2082,7 +2078,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Script");
|
||||
assert!(!r[0].2);
|
||||
for (i, entry) in r.iter().enumerate().skip(1) {
|
||||
assert_eq!(entry.1, format!("Script~{i}"));
|
||||
assert_eq!(entry.1, format!("Script~{}", i + 1));
|
||||
assert!(entry.2);
|
||||
}
|
||||
// All filenames must be unique
|
||||
|
|
@ -2097,22 +2093,22 @@ mod tests {
|
|||
let r = process_siblings_dir(&[
|
||||
"A_B", // clean, claims "A_B"
|
||||
"C_D", // clean, claims "C_D"
|
||||
"A/B", // slug "A_B" → collision → "A_B~1"
|
||||
"A/B", // slug "A_B" → collision → "A_B~2"
|
||||
"E_F", // clean, claims "E_F"
|
||||
"C:D", // slug "C_D" → collision → "C_D~1"
|
||||
"A:B", // slug "A_B" → collision with A_B and A_B~1 → "A_B~2"
|
||||
"C:D", // slug "C_D" → collision → "C_D~2"
|
||||
"A:B", // slug "A_B" → collision with A_B and A_B~2 → "A_B~3"
|
||||
]);
|
||||
assert_eq!(r[0].1, "A_B");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "C_D");
|
||||
assert!(!r[1].2);
|
||||
assert_eq!(r[2].1, "A_B~1");
|
||||
assert_eq!(r[2].1, "A_B~2");
|
||||
assert!(r[2].2);
|
||||
assert_eq!(r[3].1, "E_F");
|
||||
assert!(!r[3].2);
|
||||
assert_eq!(r[4].1, "C_D~1");
|
||||
assert_eq!(r[4].1, "C_D~2");
|
||||
assert!(r[4].2);
|
||||
assert_eq!(r[5].1, "A_B~2");
|
||||
assert_eq!(r[5].1, "A_B~3");
|
||||
assert!(r[5].2);
|
||||
}
|
||||
|
||||
|
|
@ -2317,7 +2313,7 @@ mod tests {
|
|||
#[test]
|
||||
fn tilde_dedup_collision_roundtrip() {
|
||||
// Two instances "A/B" and "A_B" both slugify to "A_B".
|
||||
// First gets "A_B", second gets "A_B~1".
|
||||
// First gets "A_B", second gets "A_B~2".
|
||||
let mut taken = HashSet::new();
|
||||
|
||||
let dom1 = make_inst("A/B", "ModuleScript");
|
||||
|
|
@ -2336,8 +2332,8 @@ mod tests {
|
|||
|
||||
assert_eq!(name1.as_ref(), "A_B.luau");
|
||||
assert!(meta1, "A/B was slugified, needs meta name");
|
||||
assert_eq!(name2.as_ref(), "A_B~1.luau");
|
||||
assert!(meta2, "A_B was deduped to ~1, needs meta name");
|
||||
assert_eq!(name2.as_ref(), "A_B~2.luau");
|
||||
assert!(meta2, "A_B was deduped to ~2, needs meta name");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2486,7 +2482,7 @@ mod tests {
|
|||
|
||||
assert_eq!(f1.as_ref(), "A_B.luau");
|
||||
assert!(m1);
|
||||
assert_eq!(f2.as_ref(), "A_B~1.luau");
|
||||
assert_eq!(f2.as_ref(), "A_B~2.luau");
|
||||
assert!(m2);
|
||||
assert_ne!(dk1.to_lowercase(), dk2.to_lowercase());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue