Skip to content

Commit

Permalink
Permit '+' in feature name
Browse files Browse the repository at this point in the history
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"]
  • Loading branch information
dtolnay committed May 12, 2020
1 parent c982a71 commit 5f842f7
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 12 deletions.
27 changes: 16 additions & 11 deletions src/models/krate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions src/tests/krate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion src/views/krate_publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit 5f842f7

Please sign in to comment.