From 5f842f73c6a9f32719508db4b1c534959b02f6e8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 11 May 2020 21:26:36 -0700 Subject: [PATCH] Permit '+' in feature name We permit "c++20" and "dependency/c++20". But we do not permit "c++20/feature". In a Cargo.toml these would appear as: [features] default = ["c++20"] "c++20" = ["my-dependency/c++20"] --- src/models/krate.rs | 27 ++++++++++++++++----------- src/tests/krate.rs | 3 +++ src/views/krate_publish.rs | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/models/krate.rs b/src/models/krate.rs index 6e73ae54ca..9c277a0fa4 100644 --- a/src/models/krate.rs +++ b/src/models/krate.rs @@ -269,26 +269,31 @@ impl Crate { .unwrap_or(false) } + /// Validates the THIS parts of `features = ["THIS", "and/THIS"]`. pub fn valid_feature_name(name: &str) -> bool { !name.is_empty() && name .chars() - .all(|c| c.is_alphanumeric() || c == '_' || c == '-') - && name.chars().all(|c| c.is_ascii()) + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '+') } + /// Validates the prefix in front of the slash: `features = ["THIS/feature"]`. + /// Normally this corresponds to the crate name of a dependency. + fn valid_feature_prefix(name: &str) -> bool { + !name.is_empty() + && name + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-') + } + + /// Validates a whole feature string, `features = ["THIS", "ALL/THIS"]`. pub fn valid_feature(name: &str) -> bool { let mut parts = name.split('/'); - match parts.next() { - Some(part) if !Crate::valid_feature_name(part) => return false, - None => return false, - _ => {} - } - match parts.next() { - Some(part) if !Crate::valid_feature_name(part) => return false, - _ => {} - } + let name_part = parts.next_back(); // required + let prefix_part = parts.next_back(); // optional parts.next().is_none() + && name_part.map_or(false, Crate::valid_feature_name) + && prefix_part.map_or(true, Crate::valid_feature_prefix) } pub fn minimal_encodable( diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 7fb4290062..2e5c6c1e6e 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -983,6 +983,9 @@ fn valid_feature_names() { assert!(!Crate::valid_feature("%/%")); assert!(Crate::valid_feature("a/a")); assert!(Crate::valid_feature("32-column-tables")); + assert!(Crate::valid_feature("c++20")); + assert!(Crate::valid_feature("krate/c++20")); + assert!(!Crate::valid_feature("c++20/wow")); } #[test] diff --git a/src/views/krate_publish.rs b/src/views/krate_publish.rs index 0169744400..f5648c2722 100644 --- a/src/views/krate_publish.rs +++ b/src/views/krate_publish.rs @@ -113,7 +113,7 @@ impl<'de> Deserialize<'de> for EncodableFeatureName { if !Crate::valid_feature_name(&s) { let value = de::Unexpected::Str(&s); let expected = "a valid feature name containing only letters, \ - numbers, hyphens, or underscores"; + numbers, '-', '+', or '_'"; Err(de::Error::invalid_value(value, &expected)) } else { Ok(EncodableFeatureName(s))