Repository: astral-sh/uv
File: crates/uv-build-backend/src/lib.rs (Rust)

Code before:
    let mut pruned = Vec::with_capacity(names.len());
    for name in names {
        if let Some(last) = pruned.last() {
            if name == *last {
                continue;
            }
            // This is a more specific (narrow) module name than what came before
            if name
                .strip_prefix(last)
                .is_some_and(|suffix| suffix.starts_with('.'))
            {
                continue;
            }
        }
        pruned.push(name);
    }
    pruned
}

/// Wraps `prune_redundant_modules` with a warning when modules are ignored
fn prune_redundant_modules_warn(names: &[String]) -> Vec<String> {
    let pruned = prune_redundant_modules(names.to_vec());
    if names.len() != pruned.len() {
        let mut pruned: HashSet<_> = pruned.iter().collect();
        let ignored = names.iter().filter(|name| !pruned.remove(name)).join(" ");
        warn_user_once!("Ignoring redundant module name(s): {ignored}");
    }
    pruned
}

/// Returns the source root and the module path(s) with the `__init__.py[i]`  below to it while
/// checking the project layout and names.
///
/// Some target platforms have case-sensitive filesystems, while others have case-insensitive
/// filesystems. We always lower case the package name, our default for the module, while some
/// users want uppercase letters in their module names. For example, the package name is `pil_util`,
/// but the module `PIL_util`. To make the behavior as consistent as possible across platforms as
/// possible, we require that an upper case name is given explicitly through
/// `tool.uv.build-backend.module-name`.
///
/// By default, the dist-info-normalized package name is the module name. For
/// dist-info-normalization, the rules are lowercasing, replacing `.` with `_` and
/// replace `-` with `_`. Since `.` and `-` are not allowed in identifiers, we can use a string
/// comparison with the module name.
///
/// While we recommend one module per package, it is possible to declare a list of modules.
fn find_roots(
    source_tree: &Path,
    pyproject_toml: &PyProjectToml,
    relative_module_root: &Path,
    module_name: Option<&ModuleName>,

Review comment (suggestion):
This is a nit, but we like to get the detail right instead of using "(s)"; so split the message into plural and singular forms based on the number of items. e.g., https://github.com/astral-sh/uv/blob/fd7e6d0a057f7db56b124565c92d847d411fd39e/crates/uv/src/commands/mod.rs#L197

Code after:
    let mut pruned = Vec::with_capacity(names.len());
    for name in names {
        if let Some(last) = pruned.last() {
            if name == *last {
                continue;
            }
            // This is a more specific (narrow) module name than what came before
            if name
                .strip_prefix(last)
                .is_some_and(|suffix| suffix.starts_with('.'))
            {
                continue;
            }
        }
        pruned.push(name);
    }
    pruned
}

/// Wraps [`prune_redundant_modules`] with a conditional warning when modules are ignored
fn prune_redundant_modules_warn(names: &[String], show_warnings: bool) -> Vec<String> {
    let pruned = prune_redundant_modules(names.to_vec());
    if show_warnings && names.len() != pruned.len() {
        let mut pruned: HashSet<_> = pruned.iter().collect();
        let ignored: Vec<_> = names.iter().filter(|name| !pruned.remove(name)).collect();
        let s = if ignored.len() == 1 { "" } else { "s" };
        warn_user_once!(
            "Ignoring redundant module name{s} in `tool.uv.build-backend.module-name`: `{}`",
            ignored.into_iter().join("`, `")
        );
    }
    pruned
}

/// Returns the source root and the module path(s) with the `__init__.py[i]`  below to it while
/// checking the project layout and names.
///
/// Some target platforms have case-sensitive filesystems, while others have case-insensitive
/// filesystems. We always lower case the package name, our default for the module, while some
/// users want uppercase letters in their module names. For example, the package name is `pil_util`,
/// but the module `PIL_util`. To make the behavior as consistent as possible across platforms as
/// possible, we require that an upper case name is given explicitly through
/// `tool.uv.build-backend.module-name`.
///
/// By default, the dist-info-normalized package name is the module name. For
/// dist-info-normalization, the rules are lowercasing, replacing `.` with `_` and
/// replace `-` with `_`. Since `.` and `-` are not allowed in identifiers, we can use a string
/// comparison with the module name.
///
/// While we recommend one module per package, it is possible to declare a list of modules.
fn find_roots(