@@ -12,6 +12,7 @@ use std::str::FromStr;
12
12
use anyhow:: Context as _;
13
13
use cargo_util:: paths;
14
14
use cargo_util_schemas:: core:: PartialVersion ;
15
+ use cargo_util_schemas:: manifest:: NameValidationError ;
15
16
use cargo_util_schemas:: manifest:: PathBaseName ;
16
17
use cargo_util_schemas:: manifest:: RustVersion ;
17
18
use indexmap:: IndexSet ;
@@ -104,6 +105,14 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
104
105
options. gctx ,
105
106
& mut registry,
106
107
)
108
+ . map_err ( |err| {
109
+ if err. is :: < NameValidationError > ( ) {
110
+ if let Some ( note) = spec_fix_suggestion ( raw) {
111
+ return anyhow:: format_err!( "{err}\n note: {note}" ) ;
112
+ }
113
+ }
114
+ err
115
+ } )
107
116
} )
108
117
. collect :: < CargoResult < Vec < _ > > > ( ) ?
109
118
} ;
@@ -329,6 +338,27 @@ pub struct DepOp {
329
338
pub tag : Option < String > ,
330
339
}
331
340
341
+ fn spec_fix_suggestion ( arg : & DepOp ) -> Option < & ' static str > {
342
+ let spec = arg. crate_spec . as_deref ( ) ?;
343
+
344
+ // check if the arg is present to avoid suggesting it redundantly
345
+ if arg. git . is_none ( )
346
+ && ( spec. starts_with ( "git@" ) || spec. starts_with ( "ssh:" ) || spec. ends_with ( ".git" ) )
347
+ {
348
+ Some ( "git URLs must be specified with --git" )
349
+ } else if arg. registry . is_none ( )
350
+ && ( spec. starts_with ( "registry+" ) || spec. starts_with ( "sparse+" ) )
351
+ {
352
+ Some ( "registy can be specified with --registry name" )
353
+ } else if spec. contains ( "://" ) {
354
+ Some ( "`cargo add` expects crates specified as 'name' or 'name@version', not URLs" )
355
+ } else if arg. path . is_none ( ) && spec. contains ( '/' ) {
356
+ Some ( "local crates can be added with --path" )
357
+ } else {
358
+ None
359
+ }
360
+ }
361
+
332
362
fn resolve_dependency (
333
363
manifest : & LocalManifest ,
334
364
arg : & DepOp ,
0 commit comments