workspacer_workspace/
workspace.rs

1// ---------------- [ File: workspacer-workspace/src/workspace.rs ]
2crate::ix!();
3
4#[derive(Builder,MutGetters,Getters,Debug)]
5#[getset(get="pub",get_mut="pub")]
6#[builder(setter(into))]
7pub struct Workspace<P,H:CrateHandleInterface<P>> 
8where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait 
9{
10    path:   P,
11    crates: Vec<Arc<AsyncMutex<H>>>,
12}
13
14impl<P,H:CrateHandleInterface<P>> WorkspaceInterface<P,H> for Workspace<P,H> 
15where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Clone + Send + Sync + 'async_trait 
16{ }
17
18impl<P,H:CrateHandleInterface<P>> AsRef<Path> for Workspace<P,H> 
19where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
20{
21    /// Allows `Workspace` to be treated as a path
22    fn as_ref(&self) -> &Path {
23        self.path.as_ref()
24    }
25}
26
27impl<P,H:CrateHandleInterface<P>> GetCrates<P,H> for Workspace<P,H> 
28where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
29{
30    fn crates(&self) -> &[Arc<AsyncMutex<H>>] {
31        &self.crates
32    }
33}
34
35impl<P,H:CrateHandleInterface<P>> GetCratesMut<P,H> for Workspace<P,H> 
36where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
37{
38    fn crates_mut(&mut self) -> &mut Vec<Arc<AsyncMutex<H>>> {
39        &mut self.crates
40    }
41}
42
43#[async_trait]
44impl<P,H:CrateHandleInterface<P>> FindCrateByName<P,H> for Workspace<P,H> 
45where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
46{
47    async fn find_crate_by_name(&self, name: &str) -> Option<Arc<AsyncMutex<H>>> {
48        for item in self.crates() {
49            let crate_guard = item.lock().await;
50            if crate_guard.name() == name {
51                return Some(item.clone());
52            }
53        }
54        None
55    }
56}
57
58#[async_trait]
59impl<P,H:CrateHandleInterface<P>> GetAllCrateNames for Workspace<P,H> 
60where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
61{
62    async fn get_all_crate_names(&self) -> Vec<String> {
63        let mut names = Vec::new();
64        for item in self.crates() {
65            let crate_guard = item.lock().await;
66            names.push(crate_guard.name().to_string());
67        }
68        names
69    }
70}
71
72#[async_trait]
73impl<P,H:CrateHandleInterface<P>> AsyncTryFrom<P> for Workspace<P,H>
74where
75    // your existing constraints
76    for<'async_trait> P: From<PathBuf> + AsRef<Path> + Clone + Send + Sync + 'async_trait,
77{
78    type Error = WorkspaceError;
79
80    /// Asynchronously initializes a new workspace at the provided path,
81    /// ensuring there's a `[workspace]` table in Cargo.toml. If not found,
82    /// returns `WorkspaceError::ActuallyInSingleCrate`.
83    async fn new(path: &P) -> Result<Self, Self::Error> {
84        // Step 1) Basic check: is there a Cargo.toml at all?
85        let path_buf = path.as_ref().to_path_buf();
86        let cargo_toml = path_buf.join("Cargo.toml");
87        if !cargo_toml.exists() {
88            return Err(WorkspaceError::InvalidWorkspace {
89                invalid_workspace_path: path_buf,
90            });
91        }
92
93        // Step 2) Read and parse the top-level Cargo.toml
94        let cargo_toml_str = tokio::fs::read_to_string(&cargo_toml).await.map_err(|e| {
95            WorkspaceError::IoError {
96                io_error: e.into(),
97                context: format!("Failed to read {:?}", cargo_toml),
98            }
99        })?;
100
101        // Step 3) Check for a `[workspace]` table
102        let parsed: toml::Value = toml::from_str(&cargo_toml_str).map_err(|_e| {
103            WorkspaceError::InvalidWorkspace {
104                invalid_workspace_path: path_buf.clone(),
105            }
106        })?;
107
108        let is_workspace = parsed
109            .as_table()
110            .map_or(false, |tbl| tbl.contains_key("workspace"));
111        if !is_workspace {
112            // We found a Cargo.toml but no [workspace] => single crate
113            return Err(WorkspaceError::ActuallyInSingleCrate {
114                path: path_buf,
115            });
116        }
117
118        // Step 4) If it’s truly a workspace, find crates as normal
119        // (Your original logic)
120        if !Self::is_valid(&path_buf).await {
121            return Err(WorkspaceError::InvalidWorkspace {
122                invalid_workspace_path: path_buf,
123            });
124        }
125
126        let crates = Self::find_items(&path_buf).await?;
127        Ok(Self { path: path.clone(), crates })
128    }
129}