@@ -18,21 +18,23 @@ pub struct CargoMetadata {
1818impl CargoMetadata {
1919 // Create a new instance from the Cargo.toml at the given path.
2020 pub async fn new ( manifest : & Path ) -> Result < Self > {
21+ let manifest_path = dunce:: simplified ( manifest) . to_path_buf ( ) ;
2122 let mut cmd = MetadataCommand :: new ( ) ;
22- cmd. manifest_path ( dunce :: simplified ( manifest ) ) ;
23+ cmd. manifest_path ( & manifest_path ) ;
2324 let metadata = spawn_blocking ( move || cmd. exec ( ) )
2425 . await
2526 . context ( "error awaiting spawned cargo metadata task" ) ?
2627 . context ( "error getting cargo metadata" ) ?;
2728
28- Self :: from_metadata ( metadata)
29+ Self :: from_metadata_with_manifest_path ( metadata, manifest_path )
2930 }
3031
31- pub ( crate ) fn from_metadata ( metadata : Metadata ) -> Result < Self > {
32- let package = metadata
33- . root_package ( )
34- . cloned ( )
35- . context ( "could not find the root package of the target crate" ) ?;
32+
33+
34+ /// Create a new instance from metadata with a known manifest path.
35+ /// This is the preferred method as it can better handle workspace scenarios.
36+ pub ( crate ) fn from_metadata_with_manifest_path ( metadata : Metadata , original_manifest_path : std:: path:: PathBuf ) -> Result < Self > {
37+ let package = Self :: find_target_package ( & metadata, Some ( & original_manifest_path) ) ?;
3638
3739 // Get the path to the Cargo.toml manifest.
3840 let manifest_path = package. manifest_path . to_string ( ) ;
@@ -43,4 +45,65 @@ impl CargoMetadata {
4345 manifest_path,
4446 } )
4547 }
48+
49+ /// Find the target package from metadata, handling both standalone packages and workspace members.
50+ fn find_target_package ( metadata : & Metadata , original_manifest_path : Option < & std:: path:: Path > ) -> Result < Package > {
51+ // First, try the traditional approach for standalone packages
52+ if let Some ( package) = metadata. root_package ( ) {
53+ return Ok ( package. clone ( ) ) ;
54+ }
55+
56+ // If no root package exists, we're likely in a workspace.
57+ // In this case, we need to find the package that corresponds to the manifest path
58+ // that was used to generate this metadata.
59+
60+ let workspace_packages = metadata. workspace_packages ( ) ;
61+
62+ if workspace_packages. is_empty ( ) {
63+ anyhow:: bail!(
64+ "could not find the root package of the target crate: no root package found and no workspace members available"
65+ ) ;
66+ }
67+
68+ // If we have the original manifest path, try to find the exact matching package
69+ if let Some ( original_path) = original_manifest_path {
70+ // Canonicalize the original path for comparison
71+ let canonical_original = dunce:: canonicalize ( original_path)
72+ . with_context ( || format ! ( "failed to canonicalize manifest path: {}" , original_path. display( ) ) ) ?;
73+
74+ for package in & workspace_packages {
75+ // Canonicalize the package's manifest path for comparison
76+ if let Ok ( canonical_package) = dunce:: canonicalize ( & package. manifest_path ) {
77+ if canonical_original == canonical_package {
78+ return Ok ( ( * package) . clone ( ) ) ;
79+ }
80+ }
81+ }
82+
83+ // If we couldn't find an exact match, provide helpful error information
84+ let package_names: Vec < String > = workspace_packages. iter ( )
85+ . map ( |p| format ! ( "{} ({})" , p. name, p. manifest_path) )
86+ . collect ( ) ;
87+ anyhow:: bail!(
88+ "could not find the root package of the target crate: manifest path '{}' does not match any workspace member. \
89+ Available workspace members: [{}]",
90+ original_path. display( ) ,
91+ package_names. join( ", " )
92+ ) ;
93+ }
94+
95+ // If there's only one workspace package, use it
96+ if workspace_packages. len ( ) == 1 {
97+ return Ok ( workspace_packages[ 0 ] . clone ( ) ) ;
98+ }
99+
100+ // If there are multiple workspace packages and we don't have a specific manifest path,
101+ // we can't determine which one to use.
102+ let package_names: Vec < & str > = workspace_packages. iter ( ) . map ( |p| p. name . as_str ( ) ) . collect ( ) ;
103+ anyhow:: bail!(
104+ "could not find the root package of the target crate: multiple workspace members found: [{}]. \
105+ Consider running trunk from within a specific package directory or using a more specific manifest path.",
106+ package_names. join( ", " )
107+ ) ;
108+ }
46109}
0 commit comments