@@ -220,32 +220,18 @@ impl Crate {
220220 . unwrap_or ( false )
221221 }
222222
223- pub fn invalid_dependency_name_msg ( dep : & str ) -> String {
224- format ! (
225- "\" {dep}\" is an invalid dependency name (dependency \
226- names must start with a letter or underscore, contain only \
227- letters, numbers, hyphens, or underscores and have at most \
228- {MAX_NAME_LENGTH} characters)"
229- )
230- }
231-
232223 /// Validates the THIS parts of `features = ["THIS", "and/THIS", "dep:THIS", "dep?/THIS"]`.
233224 /// 1. The name must be non-empty.
234225 /// 2. The first character must be a Unicode XID start character, `_`, or a digit.
235226 /// 3. The remaining characters must be Unicode XID characters, `_`, `+`, `-`, or `.`.
236- pub fn valid_feature_name ( name : & str ) -> AppResult < ( ) > {
227+ pub fn validate_feature_name ( name : & str ) -> Result < ( ) , InvalidFeature > {
237228 if name. is_empty ( ) {
238- return Err ( cargo_err ( "feature cannot be an empty" ) ) ;
229+ return Err ( InvalidFeature :: Empty ) ;
239230 }
240231 let mut chars = name. chars ( ) ;
241232 if let Some ( ch) = chars. next ( ) {
242233 if !( unicode_xid:: UnicodeXID :: is_xid_start ( ch) || ch == '_' || ch. is_ascii_digit ( ) ) {
243- return Err ( cargo_err ( & format ! (
244- "invalid character `{}` in feature `{}`, \
245- the first character must be a Unicode XID start character or digit \
246- (most letters or `_` or `0` to `9`)",
247- ch, name,
248- ) ) ) ;
234+ return Err ( InvalidFeature :: Start ( ch, name. into ( ) ) ) ;
249235 }
250236 }
251237 for ch in chars {
@@ -254,33 +240,30 @@ impl Crate {
254240 || ch == '-'
255241 || ch == '.' )
256242 {
257- return Err ( cargo_err ( & format ! (
258- "invalid character `{}` in feature `{}`, \
259- characters must be Unicode XID characters, `+`, `-`, or `.` \
260- (numbers, `+`, `-`, `_`, `.`, or most letters)",
261- ch, name,
262- ) ) ) ;
243+ return Err ( InvalidFeature :: Char ( ch, name. into ( ) ) ) ;
263244 }
264245 }
265246
266247 Ok ( ( ) )
267248 }
268249
269250 /// Validates a whole feature string, `features = ["THIS", "and/THIS", "dep:THIS", "dep?/THIS"]`.
270- pub fn valid_feature ( name : & str ) -> AppResult < ( ) > {
251+ pub fn validate_feature ( name : & str ) -> Result < ( ) , InvalidFeature > {
271252 if let Some ( ( dep, dep_feat) ) = name. split_once ( '/' ) {
272253 let dep = dep. strip_suffix ( '?' ) . unwrap_or ( dep) ;
273254 if !Crate :: valid_dependency_name ( dep) {
274- return Err ( cargo_err ( & Crate :: invalid_dependency_name_msg ( dep) ) ) ;
255+ let err = InvalidDependencyName ( dep. into ( ) ) ;
256+ return Err ( InvalidFeature :: DependencyName ( err) ) ;
275257 }
276- Crate :: valid_feature_name ( dep_feat)
258+ Crate :: validate_feature_name ( dep_feat)
277259 } else if let Some ( ( _, dep) ) = name. split_once ( "dep:" ) {
278260 if !Crate :: valid_dependency_name ( dep) {
279- return Err ( cargo_err ( & Crate :: invalid_dependency_name_msg ( dep) ) ) ;
261+ let err = InvalidDependencyName ( dep. into ( ) ) ;
262+ return Err ( InvalidFeature :: DependencyName ( err) ) ;
280263 }
281264 return Ok ( ( ) ) ;
282265 } else {
283- Crate :: valid_feature_name ( name)
266+ Crate :: validate_feature_name ( name)
284267 }
285268 }
286269
@@ -528,6 +511,34 @@ impl CrateVersions for [Crate] {
528511 }
529512}
530513
514+ #[ derive( Debug , Eq , PartialEq , thiserror:: Error ) ]
515+ pub enum InvalidFeature {
516+ #[ error( "feature cannot be empty" ) ]
517+ Empty ,
518+ #[ error(
519+ "invalid character `{0}` in feature `{1}`, the first character must be \
520+ a Unicode XID start character or digit (most letters or `_` or `0` to \
521+ `9`)"
522+ ) ]
523+ Start ( char , String ) ,
524+ #[ error(
525+ "invalid character `{0}` in feature `{1}`, characters must be Unicode \
526+ XID characters, `+`, `-`, or `.` (numbers, `+`, `-`, `_`, `.`, or most \
527+ letters)"
528+ ) ]
529+ Char ( char , String ) ,
530+ #[ error( transparent) ]
531+ DependencyName ( #[ from] InvalidDependencyName ) ,
532+ }
533+
534+ #[ derive( Debug , Eq , PartialEq , thiserror:: Error ) ]
535+ #[ error(
536+ "\" {0}\" is an invalid dependency name (dependency names must start with a \
537+ letter or underscore, contain only letters, numbers, hyphens, or \
538+ underscores and have at most {MAX_NAME_LENGTH} characters)"
539+ ) ]
540+ pub struct InvalidDependencyName ( pub String ) ;
541+
531542#[ cfg( test) ]
532543mod tests {
533544 use crate :: models:: Crate ;
@@ -559,29 +570,57 @@ mod tests {
559570 assert ! ( Crate :: valid_dependency_name( "_foo" ) ) ;
560571 assert ! ( !Crate :: valid_dependency_name( "-foo" ) ) ;
561572 }
573+
562574 #[ test]
563575 fn valid_feature_names ( ) {
564- assert ! ( Crate :: valid_feature( "foo" ) . is_ok( ) ) ;
565- assert ! ( Crate :: valid_feature( "1foo" ) . is_ok( ) ) ;
566- assert ! ( Crate :: valid_feature( "_foo" ) . is_ok( ) ) ;
567- assert ! ( Crate :: valid_feature( "_foo-_+.1" ) . is_ok( ) ) ;
568- assert ! ( Crate :: valid_feature( "_foo-_+.1" ) . is_ok( ) ) ;
569- assert ! ( Crate :: valid_feature( "" ) . is_err( ) ) ;
570- assert ! ( Crate :: valid_feature( "/" ) . is_err( ) ) ;
571- assert ! ( Crate :: valid_feature( "%/%" ) . is_err( ) ) ;
572- assert ! ( Crate :: valid_feature( "a/a" ) . is_ok( ) ) ;
573- assert ! ( Crate :: valid_feature( "32-column-tables" ) . is_ok( ) ) ;
574- assert ! ( Crate :: valid_feature( "c++20" ) . is_ok( ) ) ;
575- assert ! ( Crate :: valid_feature( "krate/c++20" ) . is_ok( ) ) ;
576- assert ! ( Crate :: valid_feature( "c++20/wow" ) . is_err( ) ) ;
577- assert ! ( Crate :: valid_feature( "foo?/bar" ) . is_ok( ) ) ;
578- assert ! ( Crate :: valid_feature( "dep:foo" ) . is_ok( ) ) ;
579- assert ! ( Crate :: valid_feature( "dep:foo?/bar" ) . is_err( ) ) ;
580- assert ! ( Crate :: valid_feature( "foo/?bar" ) . is_err( ) ) ;
581- assert ! ( Crate :: valid_feature( "foo?bar" ) . is_err( ) ) ;
582- assert ! ( Crate :: valid_feature( "bar.web" ) . is_ok( ) ) ;
583- assert ! ( Crate :: valid_feature( "foo/bar.web" ) . is_ok( ) ) ;
584- assert ! ( Crate :: valid_feature( "dep:0foo" ) . is_err( ) ) ;
585- assert ! ( Crate :: valid_feature( "0foo?/bar.web" ) . is_err( ) ) ;
576+ use super :: InvalidDependencyName ;
577+ use super :: InvalidFeature :: * ;
578+
579+ assert_ok ! ( Crate :: validate_feature( "foo" ) ) ;
580+ assert_ok ! ( Crate :: validate_feature( "1foo" ) ) ;
581+ assert_ok ! ( Crate :: validate_feature( "_foo" ) ) ;
582+ assert_ok ! ( Crate :: validate_feature( "_foo-_+.1" ) ) ;
583+ assert_ok ! ( Crate :: validate_feature( "_foo-_+.1" ) ) ;
584+ assert_err_eq ! ( Crate :: validate_feature( "" ) , Empty ) ;
585+ assert_err_eq ! (
586+ Crate :: validate_feature( "/" ) ,
587+ InvalidDependencyName ( "" . into( ) ) . into( )
588+ ) ;
589+ assert_err_eq ! (
590+ Crate :: validate_feature( "%/%" ) ,
591+ InvalidDependencyName ( "%" . into( ) ) . into( )
592+ ) ;
593+ assert_ok ! ( Crate :: validate_feature( "a/a" ) ) ;
594+ assert_ok ! ( Crate :: validate_feature( "32-column-tables" ) ) ;
595+ assert_ok ! ( Crate :: validate_feature( "c++20" ) ) ;
596+ assert_ok ! ( Crate :: validate_feature( "krate/c++20" ) ) ;
597+ assert_err_eq ! (
598+ Crate :: validate_feature( "c++20/wow" ) ,
599+ InvalidDependencyName ( "c++20" . into( ) ) . into( )
600+ ) ;
601+ assert_ok ! ( Crate :: validate_feature( "foo?/bar" ) ) ;
602+ assert_ok ! ( Crate :: validate_feature( "dep:foo" ) ) ;
603+ assert_err_eq ! (
604+ Crate :: validate_feature( "dep:foo?/bar" ) ,
605+ InvalidDependencyName ( "dep:foo" . into( ) ) . into( )
606+ ) ;
607+ assert_err_eq ! (
608+ Crate :: validate_feature( "foo/?bar" ) ,
609+ Start ( '?' , "?bar" . into( ) )
610+ ) ;
611+ assert_err_eq ! (
612+ Crate :: validate_feature( "foo?bar" ) ,
613+ Char ( '?' , "foo?bar" . into( ) )
614+ ) ;
615+ assert_ok ! ( Crate :: validate_feature( "bar.web" ) ) ;
616+ assert_ok ! ( Crate :: validate_feature( "foo/bar.web" ) ) ;
617+ assert_err_eq ! (
618+ Crate :: validate_feature( "dep:0foo" ) ,
619+ InvalidDependencyName ( "0foo" . into( ) ) . into( )
620+ ) ;
621+ assert_err_eq ! (
622+ Crate :: validate_feature( "0foo?/bar.web" ) ,
623+ InvalidDependencyName ( "0foo" . into( ) ) . into( )
624+ ) ;
586625 }
587626}
0 commit comments