11use crate :: core:: { Edition , Shell , Workspace } ;
22use crate :: util:: errors:: CargoResult ;
33use crate :: util:: important_paths:: find_root_manifest_for_wd;
4+ use crate :: util:: toml_mut:: is_sorted;
45use crate :: util:: { existing_vcs_repo, FossilRepo , GitRepo , HgRepo , PijulRepo } ;
56use crate :: util:: { restricted_names, Config } ;
6- use anyhow:: { anyhow, Context as _ } ;
7- use cargo_util:: paths;
7+ use anyhow:: { anyhow, Context } ;
8+ use cargo_util:: paths:: { self , write_atomic } ;
89use serde:: de;
910use serde:: Deserialize ;
1011use std:: collections:: BTreeMap ;
@@ -13,6 +14,7 @@ use std::io::{BufRead, BufReader, ErrorKind};
1314use std:: path:: { Path , PathBuf } ;
1415use std:: str:: FromStr ;
1516use std:: { fmt, slice} ;
17+ use toml_edit:: { Array , Value } ;
1618
1719#[ derive( Clone , Copy , Debug , PartialEq ) ]
1820pub enum VersionControl {
@@ -809,7 +811,7 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
809811 // Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
810812 // This should not block the creation of the new project. It is only a best effort to
811813 // inherit the workspace package keys.
812- if let Ok ( workspace_document) = root_manifest. parse :: < toml_edit:: Document > ( ) {
814+ if let Ok ( mut workspace_document) = root_manifest. parse :: < toml_edit:: Document > ( ) {
813815 if let Some ( workspace_package_keys) = workspace_document
814816 . get ( "workspace" )
815817 . and_then ( |workspace| workspace. get ( "package" ) )
@@ -832,6 +834,13 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
832834 table[ "workspace" ] = toml_edit:: value ( true ) ;
833835 manifest[ "lints" ] = toml_edit:: Item :: Table ( table) ;
834836 }
837+
838+ // Try to add the new package to the workspace members.
839+ update_manifest_with_new_member (
840+ & root_manifest_path,
841+ & mut workspace_document,
842+ opts. path ,
843+ ) ?;
835844 }
836845 }
837846
@@ -931,3 +940,95 @@ fn update_manifest_with_inherited_workspace_package_keys(
931940 try_remove_and_inherit_package_key ( key, manifest) ;
932941 }
933942}
943+
944+ /// Adds the new package member to the [workspace.members] array.
945+ /// - It first checks if the name matches any element in [workspace.exclude],
946+ /// and it ignores the name if there is a match.
947+ /// - Then it check if the name matches any element already in [workspace.members],
948+ /// and it ignores the name if there is a match.
949+ /// - If [workspace.members] doesn't exist in the manifest, it will add a new section
950+ /// with the new package in it.
951+ fn update_manifest_with_new_member (
952+ root_manifest_path : & Path ,
953+ workspace_document : & mut toml_edit:: Document ,
954+ package_path : & Path ,
955+ ) -> CargoResult < ( ) > {
956+ // Find the relative path for the package from the workspace root directory.
957+ let workspace_root = root_manifest_path. parent ( ) . with_context ( || {
958+ format ! (
959+ "workspace root manifest doesn't have a parent directory `{}`" ,
960+ root_manifest_path. display( )
961+ )
962+ } ) ?;
963+ let relpath = pathdiff:: diff_paths ( package_path, workspace_root) . with_context ( || {
964+ format ! (
965+ "path comparison requires two absolute paths; package_path: `{}`, workspace_path: `{}`" ,
966+ package_path. display( ) ,
967+ workspace_root. display( )
968+ )
969+ } ) ?;
970+
971+ let mut components = Vec :: new ( ) ;
972+ for comp in relpath. iter ( ) {
973+ let comp = comp. to_str ( ) . with_context ( || {
974+ format ! ( "invalid unicode component in path `{}`" , relpath. display( ) )
975+ } ) ?;
976+ components. push ( comp) ;
977+ }
978+ let display_path = components. join ( "/" ) ;
979+
980+ // Don't add the new package to the workspace's members
981+ // if there is an exclusion match for it.
982+ if let Some ( exclude) = workspace_document
983+ . get ( "workspace" )
984+ . and_then ( |workspace| workspace. get ( "exclude" ) )
985+ . and_then ( |exclude| exclude. as_array ( ) )
986+ {
987+ for member in exclude {
988+ let pat = member
989+ . as_str ( )
990+ . with_context ( || format ! ( "invalid non-string exclude path `{}`" , member) ) ?;
991+ if pat == display_path {
992+ return Ok ( ( ) ) ;
993+ }
994+ }
995+ }
996+
997+ // If the members element already exist, check if one of the patterns
998+ // in the array already includes the new package's relative path.
999+ // - Add the relative path if the members don't match the new package's path.
1000+ // - Create a new members array if there are no members element in the workspace yet.
1001+ if let Some ( members) = workspace_document
1002+ . get_mut ( "workspace" )
1003+ . and_then ( |workspace| workspace. get_mut ( "members" ) )
1004+ . and_then ( |members| members. as_array_mut ( ) )
1005+ {
1006+ for member in members. iter ( ) {
1007+ let pat = member
1008+ . as_str ( )
1009+ . with_context ( || format ! ( "invalid non-string member `{}`" , member) ) ?;
1010+ let pattern = glob:: Pattern :: new ( pat)
1011+ . with_context ( || format ! ( "cannot build glob pattern from `{}`" , pat) ) ?;
1012+
1013+ if pattern. matches ( & display_path) {
1014+ return Ok ( ( ) ) ;
1015+ }
1016+ }
1017+
1018+ let was_sorted = is_sorted ( members. iter ( ) . map ( Value :: as_str) ) ;
1019+ members. push ( & display_path) ;
1020+ if was_sorted {
1021+ members. sort_by ( |lhs, rhs| lhs. as_str ( ) . cmp ( & rhs. as_str ( ) ) ) ;
1022+ }
1023+ } else {
1024+ let mut array = Array :: new ( ) ;
1025+ array. push ( & display_path) ;
1026+
1027+ workspace_document[ "workspace" ] [ "members" ] = toml_edit:: value ( array) ;
1028+ }
1029+
1030+ write_atomic (
1031+ & root_manifest_path,
1032+ workspace_document. to_string ( ) . to_string ( ) . as_bytes ( ) ,
1033+ )
1034+ }
0 commit comments