@@ -62,19 +62,25 @@ pub struct NewOptions {
6262pub enum NewProjectKind {
6363 Bin ,
6464 Lib ,
65+ Auto ,
6566}
6667
6768impl NewProjectKind {
6869 fn is_bin ( self ) -> bool {
6970 self == NewProjectKind :: Bin
7071 }
72+
73+ fn is_auto ( self ) -> bool {
74+ self == NewProjectKind :: Auto
75+ }
7176}
7277
7378impl fmt:: Display for NewProjectKind {
7479 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
7580 match * self {
7681 NewProjectKind :: Bin => "binary (application)" ,
7782 NewProjectKind :: Lib => "library" ,
83+ NewProjectKind :: Auto => "auto-select type" ,
7884 }
7985 . fmt ( f)
8086 }
@@ -109,8 +115,8 @@ impl NewOptions {
109115 let kind = match ( bin, lib) {
110116 ( true , true ) => anyhow:: bail!( "can't specify both lib and binary outputs" ) ,
111117 ( false , true ) => NewProjectKind :: Lib ,
112- // default to bin
113- ( _ , false ) => NewProjectKind :: Bin ,
118+ ( true , false ) => NewProjectKind :: Bin ,
119+ ( false , false ) => NewProjectKind :: Auto ,
114120 } ;
115121
116122 let opts = NewOptions {
@@ -389,7 +395,26 @@ fn plan_new_source_file(bin: bool, package_name: String) -> SourceFileInformatio
389395 }
390396}
391397
392- pub fn new ( opts : & NewOptions , config : & Config ) -> CargoResult < ( ) > {
398+ fn calculate_new_project_kind (
399+ requested_kind : NewProjectKind ,
400+ found_files : & Vec < SourceFileInformation > ,
401+ ) -> NewProjectKind {
402+ let bin_file = found_files. iter ( ) . find ( |x| x. bin ) ;
403+
404+ let kind_from_files = if !found_files. is_empty ( ) && bin_file. is_none ( ) {
405+ NewProjectKind :: Lib
406+ } else {
407+ NewProjectKind :: Bin
408+ } ;
409+
410+ if requested_kind. is_auto ( ) {
411+ return kind_from_files;
412+ }
413+
414+ requested_kind
415+ }
416+
417+ pub fn new ( opts : & NewOptions , config : & Config ) -> CargoResult < NewProjectKind > {
393418 let path = & opts. path ;
394419 if path. exists ( ) {
395420 anyhow:: bail!(
@@ -399,20 +424,22 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
399424 )
400425 }
401426
427+ let kind = match opts. kind {
428+ NewProjectKind :: Bin => NewProjectKind :: Bin ,
429+ NewProjectKind :: Auto => NewProjectKind :: Bin ,
430+ _ => NewProjectKind :: Lib ,
431+ } ;
432+ let is_bin = kind. is_bin ( ) ;
433+
402434 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- ) ?;
435+ check_name ( name, opts. name . is_none ( ) , is_bin, & mut config. shell ( ) ) ?;
409436
410437 let mkopts = MkOptions {
411438 version_control : opts. version_control ,
412439 path,
413440 name,
414441 source_files : vec ! [ plan_new_source_file( opts. kind. is_bin( ) , name. to_string( ) ) ] ,
415- bin : opts . kind . is_bin ( ) ,
442+ bin : is_bin,
416443 edition : opts. edition . as_deref ( ) ,
417444 registry : opts. registry . as_deref ( ) ,
418445 } ;
@@ -424,10 +451,10 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
424451 path. display( )
425452 )
426453 } ) ?;
427- Ok ( ( ) )
454+ Ok ( kind )
428455}
429456
430- pub fn init ( opts : & NewOptions , config : & Config ) -> CargoResult < ( ) > {
457+ pub fn init ( opts : & NewOptions , config : & Config ) -> CargoResult < NewProjectKind > {
431458 // This is here just as a random location to exercise the internal error handling.
432459 if std:: env:: var_os ( "__CARGO_TEST_INTERNAL_ERROR" ) . is_some ( ) {
433460 return Err ( crate :: util:: internal ( "internal error test" ) ) ;
@@ -445,14 +472,34 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
445472
446473 detect_source_paths_and_types ( path, name, & mut src_paths_types) ?;
447474
475+ let kind = calculate_new_project_kind ( opts. kind , & src_paths_types) ;
476+ let has_bin = kind. is_bin ( ) ;
477+
448478 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"
479+ src_paths_types. push ( plan_new_source_file ( has_bin, name. to_string ( ) ) ) ;
480+ } else if src_paths_types. len ( ) == 1 && !src_paths_types. iter ( ) . any ( |x| x. bin == has_bin) {
481+ // we've found the only file and it's not the type user wants. Change the type and warn
482+ let file_type = if src_paths_types[ 0 ] . bin {
483+ NewProjectKind :: Bin . to_string ( )
484+ } else {
485+ NewProjectKind :: Lib . to_string ( )
486+ } ;
487+ config. shell ( ) . warn ( format ! (
488+ "file '{}' seems to be a {} file" ,
489+ src_paths_types[ 0 ] . relative_path, file_type
490+ ) ) ?;
491+ src_paths_types[ 0 ] . bin = has_bin
492+ } else if src_paths_types. len ( ) > 1 && !has_bin {
493+ // We have found both lib and bin files and the user would like us to treat both as libs
494+ anyhow:: bail!(
495+ "cannot have a package with \
496+ multiple libraries, \
497+ found both `{}` and `{}`",
498+ src_paths_types[ 0 ] . relative_path,
499+ src_paths_types[ 1 ] . relative_path
500+ )
454501 }
455- let has_bin = src_paths_types . iter ( ) . any ( |x| x . bin ) ;
502+
456503 check_name ( name, opts. name . is_none ( ) , has_bin, & mut config. shell ( ) ) ?;
457504
458505 let mut version_control = opts. version_control ;
@@ -508,7 +555,7 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
508555 path. display( )
509556 )
510557 } ) ?;
511- Ok ( ( ) )
558+ Ok ( kind )
512559}
513560
514561/// IgnoreList
0 commit comments