Skip to content

Commit 3b5fac5

Browse files
committed
Auto merge of #12255 - epage:sanitize, r=weihanglo
fix(embedded): Align package name sanitization with cargo-new ### What does this PR try to resolve? This is a follow up to #12245 which is working to resolve the tracking issue #12207 This first aligns sanitization of package names with the central package name validation logic, putting the code next to each other so they can more easily stay in sync. Oddly enough, cargo-new is stricter than our normal package-name validation. I went ahead and sanitized along with that as well. In working on this, I was bothered by - the mix of `-` and `_` in file names because of sanitization, so I made it more consistent by detecting which the user is using - -using `_` in bins, so I switched the default to `-` ### How should we test and review this PR? One existing test covers a variety of sanitization needs Another existing test hit one of the other cases (`test` being reserved) ### Additional information For implementation convenience, I changed the directory we write the manifest to. The impact of this should be minimal since - We reuse the full file name, so if it worked for the user it should work for us - We should be moving away from the temp manifest in future commits
2 parents d93b018 + 667ff78 commit 3b5fac5

File tree

4 files changed

+61
-29
lines changed

4 files changed

+61
-29
lines changed

src/cargo/ops/cargo_new.rs

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions) -> CargoResult<&'a str> {
163163
})
164164
}
165165

166+
/// See also `util::toml::embedded::sanitize_name`
166167
fn check_name(
167168
name: &str,
168169
show_name_help: bool,

src/cargo/util/restricted_names.rs

+24
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult<
8383
Ok(())
8484
}
8585

86+
/// Ensure a package name is [valid][validate_package_name]
87+
pub fn sanitize_package_name(name: &str, placeholder: char) -> String {
88+
let mut slug = String::new();
89+
let mut chars = name.chars();
90+
if let Some(ch) = chars.next() {
91+
if ch.is_digit(10) {
92+
slug.push(placeholder);
93+
slug.push(ch);
94+
} else if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' {
95+
slug.push(ch);
96+
} else {
97+
slug.push(placeholder);
98+
}
99+
}
100+
for ch in chars {
101+
if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' {
102+
slug.push(ch);
103+
} else {
104+
slug.push(placeholder);
105+
}
106+
}
107+
slug
108+
}
109+
86110
/// Check the entire path for names reserved in Windows.
87111
pub fn is_windows_reserved_path(path: &Path) -> bool {
88112
path.iter()

src/cargo/util/toml/embedded.rs

+34-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::Context as _;
22

33
use crate::core::Workspace;
4+
use crate::util::restricted_names;
45
use crate::CargoResult;
56
use crate::Config;
67

@@ -79,8 +80,7 @@ fn write(
7980
.file_stem()
8081
.ok_or_else(|| anyhow::format_err!("no file name"))?
8182
.to_string_lossy();
82-
let separator = '_';
83-
let name = sanitize_package_name(file_name.as_ref(), separator);
83+
let name = sanitize_name(file_name.as_ref());
8484

8585
let mut workspace_root = target_dir.to_owned();
8686
workspace_root.push("eval");
@@ -140,8 +140,7 @@ fn expand_manifest_(script: &RawScript, config: &Config) -> CargoResult<toml::Ta
140140
.file_stem()
141141
.ok_or_else(|| anyhow::format_err!("no file name"))?
142142
.to_string_lossy();
143-
let separator = '_';
144-
let name = sanitize_package_name(file_name.as_ref(), separator);
143+
let name = sanitize_name(file_name.as_ref());
145144
let bin_name = name.clone();
146145
package
147146
.entry("name".to_owned())
@@ -193,27 +192,35 @@ fn expand_manifest_(script: &RawScript, config: &Config) -> CargoResult<toml::Ta
193192
Ok(manifest)
194193
}
195194

196-
fn sanitize_package_name(name: &str, placeholder: char) -> String {
197-
let mut slug = String::new();
198-
for (i, c) in name.chars().enumerate() {
199-
match (i, c) {
200-
(0, '0'..='9') => {
201-
slug.push(placeholder);
202-
slug.push(c);
203-
}
204-
(_, '0'..='9') | (_, 'a'..='z') | (_, '_') | (_, '-') => {
205-
slug.push(c);
206-
}
207-
(_, 'A'..='Z') => {
208-
// Convert uppercase characters to lowercase to avoid `non_snake_case` warnings.
209-
slug.push(c.to_ascii_lowercase());
210-
}
211-
(_, _) => {
212-
slug.push(placeholder);
213-
}
195+
/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
196+
fn sanitize_name(name: &str) -> String {
197+
let placeholder = if name.contains('_') {
198+
'_'
199+
} else {
200+
// Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the
201+
// more common convention for CLIs
202+
'-'
203+
};
204+
205+
let mut name = restricted_names::sanitize_package_name(name, placeholder);
206+
207+
loop {
208+
if restricted_names::is_keyword(&name) {
209+
name.push(placeholder);
210+
} else if restricted_names::is_conflicting_artifact_name(&name) {
211+
// Being an embedded manifest, we always assume it is a `[[bin]]`
212+
name.push(placeholder);
213+
} else if name == "test" {
214+
name.push(placeholder);
215+
} else if restricted_names::is_windows_reserved(&name) {
216+
// Go ahead and be consistent across platforms
217+
name.push(placeholder);
218+
} else {
219+
break;
214220
}
215221
}
216-
slug
222+
223+
name
217224
}
218225

219226
fn hash(script: &RawScript) -> blake3::Hash {
@@ -448,12 +455,12 @@ mod test_expand {
448455
fn test_default() {
449456
snapbox::assert_eq(
450457
r#"[[bin]]
451-
name = "test"
458+
name = "test-"
452459
path = "/home/me/test.rs"
453460
454461
[package]
455462
edition = "2021"
456-
name = "test"
463+
name = "test-"
457464
publish = false
458465
version = "0.0.0"
459466
@@ -470,15 +477,15 @@ strip = true
470477
fn test_dependencies() {
471478
snapbox::assert_eq(
472479
r#"[[bin]]
473-
name = "test"
480+
name = "test-"
474481
path = "/home/me/test.rs"
475482
476483
[dependencies]
477484
time = "0.1.25"
478485
479486
[package]
480487
edition = "2021"
481-
name = "test"
488+
name = "test-"
482489
publish = false
483490
version = "0.0.0"
484491

tests/testsuite/script.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,9 @@ args: []
426426
)
427427
.with_stderr(
428428
r#"[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
429-
[COMPILING] s-h_w_c_ v0.0.0 ([ROOT]/home/.cargo/eval/target/eval/[..]/s-h_w_c_)
429+
[COMPILING] s-h-w-c- v0.0.0 ([ROOT]/home/.cargo/eval/target/eval/[..]/s-h-w-c-)
430430
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
431-
[RUNNING] `[ROOT]/home/.cargo/eval/target/eval/[..]/s-h_w_c_/target/debug/s-h_w_c_[EXE]`
431+
[RUNNING] `[ROOT]/home/.cargo/eval/target/eval/[..]/s-h-w-c-/target/debug/s-h-w-c-[EXE]`
432432
"#,
433433
)
434434
.run();

0 commit comments

Comments
 (0)