Skip to content

Commit b37e33a

Browse files
committed
fix: improve workspace detection for workspace members
- Enhanced CargoMetadata::new() to preserve manifest path context - Added comprehensive find_target_package() method that handles both standalone packages and workspace members - Improved error messages with detailed workspace member information - Maintains full backward compatibility for standalone packages Fixes #909 Assisted-by: Claude 4
1 parent d8117a8 commit b37e33a

File tree

1 file changed

+70
-7
lines changed

1 file changed

+70
-7
lines changed

src/config/manifest.rs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@ pub struct CargoMetadata {
1818
impl 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

Comments
 (0)