1use std::path::{Path, PathBuf};
4
5use cargo_metadata::MetadataCommand;
6
7#[derive(Debug)]
9pub enum WorkspaceError {
10 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#[derive(Debug)]
32pub struct CrateMember {
33 pub name: String,
35 pub path: PathBuf,
37}
38
39pub 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
74pub fn is_workspace(path: &Path) -> Result<bool, WorkspaceError> {
79 let members = detect_workspace(path)?;
80 Ok(members.len() > 1)
81}