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 {
@@ -803,7 +805,7 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
803805 // Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
804806 // This should not block the creation of the new project. It is only a best effort to
805807 // inherit the workspace package keys.
806- if let Ok ( workspace_document) = root_manifest. parse :: < toml_edit:: Document > ( ) {
808+ if let Ok ( mut workspace_document) = root_manifest. parse :: < toml_edit:: Document > ( ) {
807809 if let Some ( workspace_package_keys) = workspace_document
808810 . get ( "workspace" )
809811 . and_then ( |workspace| workspace. get ( "package" ) )
@@ -826,6 +828,13 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
826828 table[ "workspace" ] = toml_edit:: value ( true ) ;
827829 manifest[ "lints" ] = toml_edit:: Item :: Table ( table) ;
828830 }
831+
832+ // Try to add the new package to the workspace members.
833+ update_manifest_with_new_member (
834+ & root_manifest_path,
835+ & mut workspace_document,
836+ opts. path ,
837+ ) ?;
829838 }
830839 }
831840
@@ -925,3 +934,95 @@ fn update_manifest_with_inherited_workspace_package_keys(
925934 try_remove_and_inherit_package_key ( key, manifest) ;
926935 }
927936}
937+
938+ /// Adds the new package member to the [workspace.members] array.
939+ /// - It first checks if the name matches any element in [workspace.exclude],
940+ /// and it ignores the name if there is a match.
941+ /// - Then it check if the name matches any element already in [workspace.members],
942+ /// and it ignores the name if there is a match.
943+ /// - If [workspace.members] doesn't exist in the manifest, it will add a new section
944+ /// with the new package in it.
945+ fn update_manifest_with_new_member (
946+ root_manifest_path : & Path ,
947+ workspace_document : & mut toml_edit:: Document ,
948+ package_path : & Path ,
949+ ) -> CargoResult < ( ) > {
950+ // Find the relative path for the package from the workspace root directory.
951+ let workspace_root = root_manifest_path. parent ( ) . with_context ( || {
952+ format ! (
953+ "workspace root manifest doesn't have a parent directory `{}`" ,
954+ root_manifest_path. display( )
955+ )
956+ } ) ?;
957+ let relpath = pathdiff:: diff_paths ( package_path, workspace_root) . with_context ( || {
958+ format ! (
959+ "path comparison requires two absolute paths; package_path: `{}`, workspace_path: `{}`" ,
960+ package_path. display( ) ,
961+ workspace_root. display( )
962+ )
963+ } ) ?;
964+
965+ let mut components = Vec :: new ( ) ;
966+ for comp in relpath. iter ( ) {
967+ let comp = comp. to_str ( ) . with_context ( || {
968+ format ! ( "invalid unicode component in path `{}`" , relpath. display( ) )
969+ } ) ?;
970+ components. push ( comp) ;
971+ }
972+ let display_path = components. join ( "/" ) ;
973+
974+ // Don't add the new package to the workspace's members
975+ // if there is an exclusion match for it.
976+ if let Some ( exclude) = workspace_document
977+ . get ( "workspace" )
978+ . and_then ( |workspace| workspace. get ( "exclude" ) )
979+ . and_then ( |exclude| exclude. as_array ( ) )
980+ {
981+ for member in exclude {
982+ let pat = member
983+ . as_str ( )
984+ . with_context ( || format ! ( "invalid non-string exclude path `{}`" , member) ) ?;
985+ if pat == display_path {
986+ return Ok ( ( ) ) ;
987+ }
988+ }
989+ }
990+
991+ // If the members element already exist, check if one of the patterns
992+ // in the array already includes the new package's relative path.
993+ // - Add the relative path if the members don't match the new package's path.
994+ // - Create a new members array if there are no members element in the workspace yet.
995+ if let Some ( members) = workspace_document
996+ . get_mut ( "workspace" )
997+ . and_then ( |workspace| workspace. get_mut ( "members" ) )
998+ . and_then ( |members| members. as_array_mut ( ) )
999+ {
1000+ for member in members. iter ( ) {
1001+ let pat = member
1002+ . as_str ( )
1003+ . with_context ( || format ! ( "invalid non-string member `{}`" , member) ) ?;
1004+ let pattern = glob:: Pattern :: new ( pat)
1005+ . with_context ( || format ! ( "cannot build glob pattern from `{}`" , pat) ) ?;
1006+
1007+ if pattern. matches ( & display_path) {
1008+ return Ok ( ( ) ) ;
1009+ }
1010+ }
1011+
1012+ let was_sorted = is_sorted ( members. iter ( ) . map ( Value :: as_str) ) ;
1013+ members. push ( & display_path) ;
1014+ if was_sorted {
1015+ members. sort_by ( |lhs, rhs| lhs. as_str ( ) . cmp ( & rhs. as_str ( ) ) ) ;
1016+ }
1017+ } else {
1018+ let mut array = Array :: new ( ) ;
1019+ array. push ( & display_path) ;
1020+
1021+ workspace_document[ "workspace" ] [ "members" ] = toml_edit:: value ( array) ;
1022+ }
1023+
1024+ write_atomic (
1025+ & root_manifest_path,
1026+ workspace_document. to_string ( ) . to_string ( ) . as_bytes ( ) ,
1027+ )
1028+ }
0 commit comments