@@ -19,22 +19,73 @@ pub fn list_rust_files(dir: &Path) -> Result<Vec<String>> {
1919 Ok ( files)
2020}
2121
22- fn member ( manifest : & Path , members : & [ String ] , package : & str ) -> Result < Option < PathBuf > > {
23- let workspace_dir = manifest. parent ( ) . unwrap ( ) ;
24- for member in members {
25- for manifest_dir in glob:: glob ( workspace_dir. join ( member) . to_str ( ) . unwrap ( ) ) ? {
22+ fn match_package_name ( manifest : & Manifest , name : Option < & str > ) -> Option < String > {
23+ if let Some ( p) = & manifest. package {
24+ if let Some ( name) = name {
25+ if name == p. name {
26+ return Some ( p. name . clone ( ) ) ;
27+ }
28+ } else {
29+ return Some ( p. name . clone ( ) ) ;
30+ }
31+ }
32+
33+ None
34+ }
35+
36+ /// Tries to find a package by the given `name` in the [workspace root] or member
37+ /// of the given [workspace] [`Manifest`].
38+ ///
39+ /// When a workspace is not detected, call [`find_package()`] instead.
40+ ///
41+ /// [workspace root]: https://doc.rust-lang.org/cargo/reference/workspaces.html#root-package
42+ /// [workspace]: https://doc.rust-lang.org/cargo/reference/workspaces.html#workspaces
43+ pub fn find_package_in_workspace (
44+ workspace_manifest_path : & Path ,
45+ workspace_manifest : & Manifest ,
46+ name : Option < & str > ,
47+ ) -> Result < ( PathBuf , String ) > {
48+ let workspace = workspace_manifest
49+ . workspace
50+ . as_ref ( )
51+ . ok_or ( Error :: ManifestNotAWorkspace ) ?;
52+
53+ // When building inside a workspace, require a package to be selected on the cmdline
54+ // as selecting the right package is non-trivial and selecting multiple packages
55+ // isn't currently supported yet. See the selection mechanism:
56+ // https://doc.rust-lang.org/cargo/reference/workspaces.html#package-selection
57+ let name = name. ok_or ( Error :: MultiplePackagesNotSupported ) ?;
58+
59+ // Check if the workspace manifest also contains a [package]
60+ if let Some ( package) = match_package_name ( workspace_manifest, Some ( name) ) {
61+ return Ok ( ( workspace_manifest_path. to_owned ( ) , package) ) ;
62+ }
63+
64+ // Check all member packages inside the workspace
65+ let workspace_root = workspace_manifest_path. parent ( ) . unwrap ( ) ;
66+ for member in & workspace. members {
67+ for manifest_dir in glob:: glob ( workspace_root. join ( member) . to_str ( ) . unwrap ( ) ) ? {
2668 let manifest_path = manifest_dir?. join ( "Cargo.toml" ) ;
2769 let manifest = Manifest :: parse_from_toml ( & manifest_path) ?;
28- if let Some ( p) = manifest. package . as_ref ( ) {
29- if p. name == package {
30- return Ok ( Some ( manifest_path) ) ;
31- }
70+
71+ // Workspace members cannot themselves be/contain a new workspace
72+ if manifest. workspace . is_some ( ) {
73+ return Err ( Error :: UnexpectedWorkspace ( manifest_path) ) ;
74+ }
75+
76+ if let Some ( package) = match_package_name ( & manifest, Some ( name) ) {
77+ return Ok ( ( manifest_path, package) ) ;
3278 }
3379 }
3480 }
35- Ok ( None )
81+
82+ Err ( Error :: ManifestNotFound )
3683}
3784
85+ /// Recursively walk up the directories until finding a `Cargo.toml`
86+ ///
87+ /// When a workspace has been detected, [`find_package_in_workspace()`] to find packages
88+ /// instead (that are members of the given workspace).
3889pub fn find_package ( path : & Path , name : Option < & str > ) -> Result < ( PathBuf , String ) > {
3990 let path = dunce:: canonicalize ( path) . map_err ( |e| Error :: Io ( path. to_owned ( ) , e) ) ?;
4091 for manifest_path in path
@@ -43,38 +94,29 @@ pub fn find_package(path: &Path, name: Option<&str>) -> Result<(PathBuf, String)
4394 . filter ( |dir| dir. exists ( ) )
4495 {
4596 let manifest = Manifest :: parse_from_toml ( & manifest_path) ?;
46- if let Some ( p) = manifest. package . as_ref ( ) {
47- if let Some ( name) = name {
48- if name == p. name {
49- return Ok ( ( manifest_path, p. name . clone ( ) ) ) ;
50- }
51- } else {
52- return Ok ( ( manifest_path, p. name . clone ( ) ) ) ;
53- }
97+
98+ // This function shouldn't be called when a workspace exists.
99+ if manifest. workspace . is_some ( ) {
100+ return Err ( Error :: UnexpectedWorkspace ( manifest_path) ) ;
54101 }
55- if let Some ( w) = manifest. workspace . as_ref ( ) {
56- // TODO: This should also work if name is None - then all packages should simply be returned
57- let name = name. ok_or ( Error :: MultiplePackagesNotSupported ) ?;
58- if let Some ( manifest_path) = member ( & manifest_path, & w. members , name) ? {
59- return Ok ( ( manifest_path, name. to_string ( ) ) ) ;
60- }
102+
103+ if let Some ( package) = match_package_name ( & manifest, name) {
104+ return Ok ( ( manifest_path, package) ) ;
61105 }
62106 }
63107 Err ( Error :: ManifestNotFound )
64108}
65109
66- pub fn find_workspace ( manifest : & Path , name : & str ) -> Result < Option < PathBuf > > {
67- let dir = manifest . parent ( ) . unwrap ( ) ;
68- for manifest_path in dir
110+ /// Find the first `Cargo.toml` that contains a `[workspace]`
111+ pub fn find_workspace ( potential_root : & Path ) -> Result < Option < ( PathBuf , Manifest ) > > {
112+ for manifest_path in potential_root
69113 . ancestors ( )
70114 . map ( |dir| dir. join ( "Cargo.toml" ) )
71115 . filter ( |dir| dir. exists ( ) )
72116 {
73117 let manifest = Manifest :: parse_from_toml ( & manifest_path) ?;
74- if let Some ( w) = manifest. workspace . as_ref ( ) {
75- if member ( & manifest_path, & w. members , name) ?. is_some ( ) {
76- return Ok ( Some ( manifest_path) ) ;
77- }
118+ if manifest. workspace . is_some ( ) {
119+ return Ok ( Some ( ( manifest_path, manifest) ) ) ;
78120 }
79121 }
80122 Ok ( None )
0 commit comments