Skip to content

Commit 711f849

Browse files
committed
Allow feature names to begin with numbers
Diesel 1.2 had planned on renaming our `large-tables` and `huge-tables` features to `32-column-tables` and `64-column-tables` respectively, while also introducing the `128-column-tables` feature. This change was made several months ago in Diesel. Cargo will happily accept those as feature names, and resolve them properly from other crates. However while publishing Diesel 1.2, I ran into a snag mid-release when I realized that Cargo is incorrectly assuming that a feature name must be the same as a crate name. I suspect this is an artifact of the fact that feature names often are crate names (and perhaps in the past that was the only form of feature?). However, now they are an entirely separate thing, and we should allow the same set of feature names that Cargo does. (As an aside, do we want to apply the same 64 character limit that we apply to crate names to feature names?) Fixes #1329.
1 parent a093b70 commit 711f849

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

src/models/krate.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -250,24 +250,26 @@ impl Crate {
250250
}
251251

252252
fn valid_ident(name: &str) -> bool {
253-
if name.is_empty() {
254-
return false;
255-
}
256-
name.chars().next().unwrap().is_alphabetic()
253+
Self::valid_feature_name(name) &&
254+
name.chars().nth(0).map(char::is_alphabetic).unwrap_or(false)
255+
}
256+
257+
pub fn valid_feature_name(name: &str) -> bool {
258+
!name.is_empty()
257259
&& name.chars()
258260
.all(|c| c.is_alphanumeric() || c == '_' || c == '-')
259261
&& name.chars().all(|c| c.is_ascii())
260262
}
261263

262-
pub fn valid_feature_name(name: &str) -> bool {
264+
pub fn valid_feature(name: &str) -> bool {
263265
let mut parts = name.split('/');
264266
match parts.next() {
265-
Some(part) if !Crate::valid_ident(part) => return false,
267+
Some(part) if !Crate::valid_feature_name(part) => return false,
266268
None => return false,
267269
_ => {}
268270
}
269271
match parts.next() {
270-
Some(part) if !Crate::valid_ident(part) => return false,
272+
Some(part) if !Crate::valid_feature_name(part) => return false,
271273
_ => {}
272274
}
273275
parts.next().is_none()

src/tests/krate.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -753,11 +753,12 @@ fn new_krate_bad_name() {
753753

754754
#[test]
755755
fn valid_feature_names() {
756-
assert!(Crate::valid_feature_name("foo"));
757-
assert!(!Crate::valid_feature_name(""));
758-
assert!(!Crate::valid_feature_name("/"));
759-
assert!(!Crate::valid_feature_name("%/%"));
760-
assert!(Crate::valid_feature_name("a/a"));
756+
assert!(Crate::valid_feature("foo"));
757+
assert!(!Crate::valid_feature(""));
758+
assert!(!Crate::valid_feature("/"));
759+
assert!(!Crate::valid_feature("%/%"));
760+
assert!(Crate::valid_feature("a/a"));
761+
assert!(Crate::valid_feature("32-column-tables"));
761762
}
762763

763764
#[test]

src/views/krate_publish.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct NewCrate {
1818
pub name: CrateName,
1919
pub vers: CrateVersion,
2020
pub deps: Vec<CrateDependency>,
21-
pub features: HashMap<CrateName, Vec<Feature>>,
21+
pub features: HashMap<FeatureName, Vec<Feature>>,
2222
pub authors: Vec<String>,
2323
pub description: Option<String>,
2424
pub homepage: Option<String>,
@@ -51,6 +51,8 @@ pub struct CategoryList(pub Vec<Category>);
5151
pub struct Category(pub String);
5252
#[derive(Serialize, Debug, Deref)]
5353
pub struct Feature(pub String);
54+
#[derive(PartialEq, Eq, Hash, Serialize, Debug, Deref)]
55+
pub struct FeatureName(pub String);
5456

5557
#[derive(Serialize, Deserialize, Debug)]
5658
pub struct CrateDependency {
@@ -102,6 +104,20 @@ impl<'de> Deserialize<'de> for Keyword {
102104
}
103105
}
104106

107+
impl<'de> Deserialize<'de> for FeatureName {
108+
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
109+
let s = String::deserialize(d)?;
110+
if !Crate::valid_feature_name(&s) {
111+
let value = de::Unexpected::Str(&s);
112+
let expected = "a valid feature name containing only letters, \
113+
numbers, hyphens, or underscores";
114+
Err(de::Error::invalid_value(value, &expected))
115+
} else {
116+
Ok(FeatureName(s))
117+
}
118+
}
119+
}
120+
105121
impl<'de> Deserialize<'de> for Feature {
106122
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Feature, D::Error> {
107123
let s = String::deserialize(d)?;

0 commit comments

Comments
 (0)