dgmod/
workspace.rs

1//! Cargo workspace detection
2
3use std::path::{Path, PathBuf};
4
5use cargo_metadata::MetadataCommand;
6
7/// Error type for workspace operations
8#[derive(Debug)]
9pub enum WorkspaceError {
10    /// Failed to run cargo metadata
11    Metadata(cargo_metadata::Error),
12}
13
14impl std::fmt::Display for WorkspaceError {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            Self::Metadata(e) => write!(f, "Failed to read workspace metadata: {e}"),
18        }
19    }
20}
21
22impl std::error::Error for WorkspaceError {
23    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
24        match self {
25            Self::Metadata(e) => Some(e),
26        }
27    }
28}
29
30/// Information about a workspace member
31#[derive(Debug)]
32pub struct CrateMember {
33    /// Name of the crate
34    pub name: String,
35    /// Root directory of the crate
36    pub path: PathBuf,
37}
38
39/// Detect if a path is a workspace and get its members
40///
41/// # Errors
42/// Returns `WorkspaceError::Metadata` if cargo metadata cannot be read.
43pub fn detect_workspace(path: &Path) -> Result<Vec<CrateMember>, WorkspaceError> {
44    let manifest = path.join("Cargo.toml");
45
46    let metadata = MetadataCommand::new()
47        .manifest_path(&manifest)
48        .no_deps()
49        .exec()
50        .map_err(WorkspaceError::Metadata)?;
51
52    let members: Vec<CrateMember> = metadata
53        .workspace_members
54        .iter()
55        .filter_map(|id| {
56            metadata
57                .packages
58                .iter()
59                .find(|p| &p.id == id)
60                .map(|pkg| CrateMember {
61                    name: pkg.name.clone(),
62                    path: pkg
63                        .manifest_path
64                        .parent()
65                        .map(|p| p.to_path_buf().into_std_path_buf())
66                        .unwrap_or_default(),
67                })
68        })
69        .collect();
70
71    Ok(members)
72}
73
74/// Check if a path is a workspace (has multiple members)
75///
76/// # Errors
77/// Returns `WorkspaceError::Metadata` if cargo metadata cannot be read.
78pub fn is_workspace(path: &Path) -> Result<bool, WorkspaceError> {
79    let members = detect_workspace(path)?;
80    Ok(members.len() > 1)
81}