@@ -51,6 +51,7 @@ impl<'de> de::Deserialize<'de> for VersionControl {
5151pub struct NewOptions {
5252 pub version_control : Option < VersionControl > ,
5353 pub kind : NewProjectKind ,
54+ pub auto_detect_kind : bool ,
5455 /// Absolute path to the directory for the new package
5556 pub path : PathBuf ,
5657 pub name : Option < String > ,
@@ -106,16 +107,18 @@ impl NewOptions {
106107 edition : Option < String > ,
107108 registry : Option < String > ,
108109 ) -> CargoResult < NewOptions > {
110+ let auto_detect_kind = !bin && !lib;
111+
109112 let kind = match ( bin, lib) {
110113 ( true , true ) => anyhow:: bail!( "can't specify both lib and binary outputs" ) ,
111114 ( false , true ) => NewProjectKind :: Lib ,
112- // default to bin
113115 ( _, false ) => NewProjectKind :: Bin ,
114116 } ;
115117
116118 let opts = NewOptions {
117119 version_control,
118120 kind,
121+ auto_detect_kind,
119122 path,
120123 name,
121124 edition,
@@ -389,6 +392,26 @@ fn plan_new_source_file(bin: bool, package_name: String) -> SourceFileInformatio
389392 }
390393}
391394
395+ fn calculate_new_project_kind (
396+ requested_kind : NewProjectKind ,
397+ auto_detect_kind : bool ,
398+ found_files : & Vec < SourceFileInformation > ,
399+ ) -> NewProjectKind {
400+ let bin_file = found_files. iter ( ) . find ( |x| x. bin ) ;
401+
402+ let kind_from_files = if !found_files. is_empty ( ) && bin_file. is_none ( ) {
403+ NewProjectKind :: Lib
404+ } else {
405+ NewProjectKind :: Bin
406+ } ;
407+
408+ if auto_detect_kind {
409+ return kind_from_files;
410+ }
411+
412+ requested_kind
413+ }
414+
392415pub fn new ( opts : & NewOptions , config : & Config ) -> CargoResult < ( ) > {
393416 let path = & opts. path ;
394417 if path. exists ( ) {
@@ -399,20 +422,17 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
399422 )
400423 }
401424
425+ let is_bin = opts. kind . is_bin ( ) ;
426+
402427 let name = get_name ( path, opts) ?;
403- check_name (
404- name,
405- opts. name . is_none ( ) ,
406- opts. kind . is_bin ( ) ,
407- & mut config. shell ( ) ,
408- ) ?;
428+ check_name ( name, opts. name . is_none ( ) , is_bin, & mut config. shell ( ) ) ?;
409429
410430 let mkopts = MkOptions {
411431 version_control : opts. version_control ,
412432 path,
413433 name,
414434 source_files : vec ! [ plan_new_source_file( opts. kind. is_bin( ) , name. to_string( ) ) ] ,
415- bin : opts . kind . is_bin ( ) ,
435+ bin : is_bin,
416436 edition : opts. edition . as_deref ( ) ,
417437 registry : opts. registry . as_deref ( ) ,
418438 } ;
@@ -427,7 +447,7 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
427447 Ok ( ( ) )
428448}
429449
430- pub fn init ( opts : & NewOptions , config : & Config ) -> CargoResult < ( ) > {
450+ pub fn init ( opts : & NewOptions , config : & Config ) -> CargoResult < NewProjectKind > {
431451 // This is here just as a random location to exercise the internal error handling.
432452 if std:: env:: var_os ( "__CARGO_TEST_INTERNAL_ERROR" ) . is_some ( ) {
433453 return Err ( crate :: util:: internal ( "internal error test" ) ) ;
@@ -445,14 +465,34 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
445465
446466 detect_source_paths_and_types ( path, name, & mut src_paths_types) ?;
447467
468+ let kind = calculate_new_project_kind ( opts. kind , opts. auto_detect_kind , & src_paths_types) ;
469+ let has_bin = kind. is_bin ( ) ;
470+
448471 if src_paths_types. is_empty ( ) {
449- src_paths_types. push ( plan_new_source_file ( opts. kind . is_bin ( ) , name. to_string ( ) ) ) ;
450- } else {
451- // --bin option may be ignored if lib.rs or src/lib.rs present
452- // Maybe when doing `cargo init --bin` inside a library package stub,
453- // user may mean "initialize for library, but also add binary target"
472+ src_paths_types. push ( plan_new_source_file ( has_bin, name. to_string ( ) ) ) ;
473+ } else if src_paths_types. len ( ) == 1 && !src_paths_types. iter ( ) . any ( |x| x. bin == has_bin) {
474+ // we've found the only file and it's not the type user wants. Change the type and warn
475+ let file_type = if src_paths_types[ 0 ] . bin {
476+ NewProjectKind :: Bin
477+ } else {
478+ NewProjectKind :: Lib
479+ } ;
480+ config. shell ( ) . warn ( format ! (
481+ "file `{}` seems to be a {} file" ,
482+ src_paths_types[ 0 ] . relative_path, file_type
483+ ) ) ?;
484+ src_paths_types[ 0 ] . bin = has_bin
485+ } else if src_paths_types. len ( ) > 1 && !has_bin {
486+ // We have found both lib and bin files and the user would like us to treat both as libs
487+ anyhow:: bail!(
488+ "cannot have a package with \
489+ multiple libraries, \
490+ found both `{}` and `{}`",
491+ src_paths_types[ 0 ] . relative_path,
492+ src_paths_types[ 1 ] . relative_path
493+ )
454494 }
455- let has_bin = src_paths_types . iter ( ) . any ( |x| x . bin ) ;
495+
456496 check_name ( name, opts. name . is_none ( ) , has_bin, & mut config. shell ( ) ) ?;
457497
458498 let mut version_control = opts. version_control ;
@@ -508,7 +548,7 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
508548 path. display( )
509549 )
510550 } ) ?;
511- Ok ( ( ) )
551+ Ok ( kind )
512552}
513553
514554/// IgnoreList
0 commit comments