feat: enhance workspace discovery with standalone crate detection#12
feat: enhance workspace discovery with standalone crate detection#12brndnmtthws merged 4 commits intomainfrom
Conversation
brndnmtthws
commented
Jul 10, 2025
- Add comprehensive standalone crate detection logic
- Implement workspace member pattern matching with glob support
- Track discovered workspaces to identify standalone crates
- Add builder pattern for WorkspaceRoot configuration
- Fix workspace member expansion to use relative paths
- Add tests for standalone crate and excluded member scenarios
- Add comprehensive standalone crate detection logic - Implement workspace member pattern matching with glob support - Track discovered workspaces to identify standalone crates - Add builder pattern for WorkspaceRoot configuration - Fix workspace member expansion to use relative paths - Add tests for standalone crate and excluded member scenarios
There was a problem hiding this comment.
Pull Request Overview
This PR enhances workspace discovery by introducing standalone crate detection via workspace member/exclude patterns and glob support, updates the WorkspaceRoot builder to carry pattern data, and surfaces workspace paths in dependency reports.
- Track discovered workspaces and filter out member crates with incorrect locks
- Add
member_patterns/exclude_patternstoWorkspaceRootand its builder - Expose workspace paths in the deps command JSON/human reports
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/workspace_discovery.rs | Track DiscoveredWorkspace, implement glob-based matching and standalone crate filtering |
| src/toml_parser.rs | Add get_workspace_excludes to mirror get_workspace_members |
| src/executors/deps.rs | Change print! to println! for consistency in output |
| src/executors/analyze.rs | Change print! to println! for consistency in output |
| src/commands/deps.rs | Add path field to WorkspaceDepsEntry, retrieve/display workspace paths |
Comments suppressed due to low confidence (2)
src/commands/deps.rs:29
- Adding the
path: Stringfield is a breaking change to the public JSON schema. Please document this in the CHANGELOG, bump your API version, and ensure downstream consumers are updated.
pub path: String,
src/toml_parser.rs:104
- [nitpick] This public method lacks a doc comment. Consider adding
/// Returns the list of workspace exclude patterns from the Cargo.tomlfor consistency with the other getters.
pub fn get_workspace_excludes(&self) -> Vec<String> {
src/workspace_discovery.rs
Outdated
| } | ||
| } else { | ||
| // Direct path comparison | ||
| return relative_path == pattern || relative_path.starts_with(&format!("{pattern}/")); |
There was a problem hiding this comment.
On Windows, relative_path may use backslashes, causing this forward‐slash comparison to fail. Consider normalizing path separators or using Path APIs (e.g., Path::starts_with) instead of string comparisons.
| return relative_path == pattern || relative_path.starts_with(&format!("{pattern}/")); | |
| let pattern_path = Path::new(pattern); | |
| return Path::new(relative_path) == pattern_path || Path::new(relative_path).starts_with(pattern_path); |
src/workspace_discovery.rs
Outdated
| if pattern.contains('*') { | ||
| // Handle glob pattern | ||
| let glob_pattern = workspace_path.join(pattern); | ||
| if let Some(glob_str) = glob_pattern.to_str() | ||
| && let Ok(glob_matcher) = glob::Pattern::new(glob_str) | ||
| { | ||
| let full_path = workspace_path.join(relative_path); | ||
| if let Some(full_path_str) = full_path.to_str() { | ||
| return glob_matcher.matches(full_path_str); | ||
| } | ||
| } | ||
| // Alternative: just check if the relative path matches the pattern | ||
| if let Ok(pattern_matcher) = glob::Pattern::new(pattern) { | ||
| return pattern_matcher.matches(relative_path); | ||
| } | ||
| } else { | ||
| // Direct path comparison | ||
| return relative_path == pattern || relative_path.starts_with(&format!("{pattern}/")); | ||
| } | ||
| false |
There was a problem hiding this comment.
[nitpick] Restricting glob handling to only * misses other common patterns (?, [], {}) supported by glob::Pattern. Consider using glob::Pattern::new directly for all patterns and handling errors uniformly.
| if pattern.contains('*') { | |
| // Handle glob pattern | |
| let glob_pattern = workspace_path.join(pattern); | |
| if let Some(glob_str) = glob_pattern.to_str() | |
| && let Ok(glob_matcher) = glob::Pattern::new(glob_str) | |
| { | |
| let full_path = workspace_path.join(relative_path); | |
| if let Some(full_path_str) = full_path.to_str() { | |
| return glob_matcher.matches(full_path_str); | |
| } | |
| } | |
| // Alternative: just check if the relative path matches the pattern | |
| if let Ok(pattern_matcher) = glob::Pattern::new(pattern) { | |
| return pattern_matcher.matches(relative_path); | |
| } | |
| } else { | |
| // Direct path comparison | |
| return relative_path == pattern || relative_path.starts_with(&format!("{pattern}/")); | |
| } | |
| false | |
| // Handle glob pattern using glob::Pattern::new | |
| if let Ok(pattern_matcher) = glob::Pattern::new(pattern) { | |
| let full_path = workspace_path.join(relative_path); | |
| if let Some(full_path_str) = full_path.to_str() { | |
| return pattern_matcher.matches(full_path_str); | |
| } | |
| // Fallback: check if the relative path matches the pattern directly | |
| return pattern_matcher.matches(relative_path); | |
| } | |
| // Direct path comparison as a fallback | |
| relative_path == pattern || relative_path.starts_with(&format!("{pattern}/")) |