Skip to content

Commit dd96103

Browse files
0xPoeTurbo87
authored andcommitted
Better error msg for invalid feature name
Signed-off-by: hi-rustin <rustin.liu@gmail.com>
1 parent a19abf8 commit dd96103

File tree

5 files changed

+62
-48
lines changed

5 files changed

+62
-48
lines changed

src/controllers/krate/publish.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
238238
}
239239

240240
for (key, values) in features.iter() {
241-
if !Crate::valid_feature_name(key) {
242-
return Err(cargo_err(&format!(
243-
"\"{key}\" is an invalid feature name (feature names must contain only Unicode XID characters, `+`, `-`, or `.` \
244-
(numbers, `+`, `-`, `_`, `.`, or most letters)"
245-
)));
246-
}
241+
Crate::valid_feature_name(key)?;
247242

248243
let num_features = values.len();
249244
if num_features > max_features {
@@ -258,9 +253,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
258253
}
259254

260255
for value in values.iter() {
261-
if !Crate::valid_feature(value) {
262-
return Err(cargo_err(&format!("\"{value}\" is an invalid feature name")));
263-
}
256+
Crate::valid_feature(value)?;
264257
}
265258
}
266259

@@ -603,11 +596,7 @@ pub fn validate_dependency(dep: &EncodableCrateDependency) -> AppResult<()> {
603596
}
604597

605598
for feature in &dep.features {
606-
if !Crate::valid_feature(feature) {
607-
return Err(cargo_err(&format_args!(
608-
"\"{feature}\" is an invalid feature name",
609-
)));
610-
}
599+
Crate::valid_feature(feature)?;
611600
}
612601

613602
if let Some(registry) = &dep.registry {

src/models/krate.rs

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,19 @@ impl Crate {
224224
/// 1. The name must be non-empty.
225225
/// 2. The first character must be a Unicode XID start character, `_`, or a digit.
226226
/// 3. The remaining characters must be Unicode XID characters, `_`, `+`, `-`, or `.`.
227-
pub fn valid_feature_name(name: &str) -> bool {
227+
pub fn valid_feature_name(name: &str) -> AppResult<()> {
228228
if name.is_empty() {
229-
return false;
229+
return Err(cargo_err("feature cannot be an empty"));
230230
}
231231
let mut chars = name.chars();
232232
if let Some(ch) = chars.next() {
233233
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_ascii_digit()) {
234-
return false;
234+
return Err(cargo_err(&format!(
235+
"invalid character `{}` in feature `{}`, \
236+
the first character must be a Unicode XID start character or digit \
237+
(most letters or `_` or `0` to `9`)",
238+
ch, name,
239+
)));
235240
}
236241
}
237242
for ch in chars {
@@ -240,20 +245,41 @@ impl Crate {
240245
|| ch == '-'
241246
|| ch == '.')
242247
{
243-
return false;
248+
return Err(cargo_err(&format!(
249+
"invalid character `{}` in feature `{}`, \
250+
characters must be Unicode XID characters, `+`, `-`, or `.` \
251+
(numbers, `+`, `-`, `_`, `.`, or most letters)",
252+
ch, name,
253+
)));
244254
}
245255
}
246256

247-
true
257+
Ok(())
248258
}
249259

250260
/// Validates a whole feature string, `features = ["THIS", "and/THIS", "dep:THIS", "dep?/THIS"]`.
251-
pub fn valid_feature(name: &str) -> bool {
261+
pub fn valid_feature(name: &str) -> AppResult<()> {
252262
if let Some((dep, dep_feat)) = name.split_once('/') {
253263
let dep = dep.strip_suffix('?').unwrap_or(dep);
254-
Crate::valid_dependency_name(dep) && Crate::valid_feature_name(dep_feat)
264+
if !Crate::valid_dependency_name(dep) {
265+
return Err(cargo_err(&format_args!(
266+
"\"{dep}\" is an invalid dependency name (dependency \
267+
names must start with a letter or underscore, contain only \
268+
letters, numbers, hyphens, or underscores and have at most \
269+
{MAX_NAME_LENGTH} characters)"
270+
)));
271+
}
272+
Crate::valid_feature_name(dep_feat)
255273
} else if let Some((_, dep)) = name.split_once("dep:") {
256-
Crate::valid_dependency_name(dep)
274+
if !Crate::valid_dependency_name(dep) {
275+
return Err(cargo_err(&format_args!(
276+
"\"{dep}\" is an invalid dependency name (dependency \
277+
names must start with a letter or underscore, contain only \
278+
letters, numbers, hyphens, or underscores and have at most \
279+
{MAX_NAME_LENGTH} characters)"
280+
)));
281+
}
282+
return Ok(());
257283
} else {
258284
Crate::valid_feature_name(name)
259285
}
@@ -534,30 +560,29 @@ mod tests {
534560
assert!(Crate::valid_dependency_name("_foo"));
535561
assert!(!Crate::valid_dependency_name("-foo"));
536562
}
537-
538563
#[test]
539564
fn valid_feature_names() {
540-
assert!(Crate::valid_feature("1foo"));
541-
assert!(Crate::valid_feature("_foo"));
542-
assert!(Crate::valid_feature("_foo-_+.1"));
543-
assert!(Crate::valid_feature("_foo-_+.1"));
544-
assert!(Crate::valid_feature("foo"));
545-
assert!(!Crate::valid_feature(""));
546-
assert!(!Crate::valid_feature("/"));
547-
assert!(!Crate::valid_feature("%/%"));
548-
assert!(Crate::valid_feature("a/a"));
549-
assert!(Crate::valid_feature("32-column-tables"));
550-
assert!(Crate::valid_feature("c++20"));
551-
assert!(Crate::valid_feature("krate/c++20"));
552-
assert!(!Crate::valid_feature("c++20/wow"));
553-
assert!(Crate::valid_feature("foo?/bar"));
554-
assert!(Crate::valid_feature("dep:foo"));
555-
assert!(!Crate::valid_feature("dep:foo?/bar"));
556-
assert!(!Crate::valid_feature("foo/?bar"));
557-
assert!(!Crate::valid_feature("foo?bar"));
558-
assert!(Crate::valid_feature("bar.web"));
559-
assert!(Crate::valid_feature("foo/bar.web"));
560-
assert!(!Crate::valid_feature("dep:0foo"));
561-
assert!(!Crate::valid_feature("0foo?/bar.web"));
565+
assert!(Crate::valid_feature("foo").is_ok());
566+
assert!(Crate::valid_feature("1foo").is_ok());
567+
assert!(Crate::valid_feature("_foo").is_ok());
568+
assert!(Crate::valid_feature("_foo-_+.1").is_ok());
569+
assert!(Crate::valid_feature("_foo-_+.1").is_ok());
570+
assert!(Crate::valid_feature("").is_err());
571+
assert!(Crate::valid_feature("/").is_err());
572+
assert!(Crate::valid_feature("%/%").is_err());
573+
assert!(Crate::valid_feature("a/a").is_ok());
574+
assert!(Crate::valid_feature("32-column-tables").is_ok());
575+
assert!(Crate::valid_feature("c++20").is_ok());
576+
assert!(Crate::valid_feature("krate/c++20").is_ok());
577+
assert!(Crate::valid_feature("c++20/wow").is_err());
578+
assert!(Crate::valid_feature("foo?/bar").is_ok());
579+
assert!(Crate::valid_feature("dep:foo").is_ok());
580+
assert!(Crate::valid_feature("dep:foo?/bar").is_err());
581+
assert!(Crate::valid_feature("foo/?bar").is_err());
582+
assert!(Crate::valid_feature("foo?bar").is_err());
583+
assert!(Crate::valid_feature("bar.web").is_ok());
584+
assert!(Crate::valid_feature("foo/bar.web").is_ok());
585+
assert!(Crate::valid_feature("dep:0foo").is_err());
586+
assert!(Crate::valid_feature("0foo?/bar.web").is_err());
562587
}
563588
}

src/tests/krate/publish/snapshots/all__krate__publish__dependencies__invalid_feature_name.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "\"🍺\" is an invalid feature name"
8+
"detail": "invalid character `🍺` in feature `🍺`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__features__invalid_feature.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "\"!bar\" is an invalid feature name"
8+
"detail": "invalid character `!` in feature `!bar`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__features__invalid_feature_name.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "\"~foo\" is an invalid feature name (feature names must contain only Unicode XID characters, `+`, `-`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)"
8+
"detail": "invalid character `~` in feature `~foo`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)"
99
}
1010
]
1111
}

0 commit comments

Comments
 (0)