workspacer_either/
workspacer_either.rs1crate::ix!();
2
3pub enum SingleOrWorkspace {
8 Single(CrateHandle),
9 Workspace(Workspace<PathBuf, CrateHandle>),
10}
11
12impl SingleOrWorkspace {
13 #[instrument(level = "trace")]
18 pub async fn detect(path: &Path) -> Result<Self, WorkspaceError> {
19 let path_canon = match tokio::fs::canonicalize(path).await {
21 Ok(abs) => abs,
22 Err(e) => {
23 error!("Could not canonicalize path={:?}: {:?}", path, e);
24 path.to_path_buf()
26 }
27 };
28
29 debug!("Attempting to interpret path='{}' as a workspace", path_canon.display());
30
31 match Workspace::<PathBuf, CrateHandle>::new(&path_canon).await {
32 Ok(ws) => {
33 debug!("Successfully loaded path='{}' as a workspace", path_canon.display());
34 Ok(SingleOrWorkspace::Workspace(ws))
35 }
36 Err(e) => {
37 match e {
38 WorkspaceError::ActuallyInSingleCrate { .. } => {
39 debug!("Falling back to single crate handle for path='{}'", path_canon.display());
41 let ch = CrateHandle::new(&path_canon).await.map_err(|crate_err| {
42 WorkspaceError::CrateError(crate_err)
43 })?;
44 Ok(SingleOrWorkspace::Single(ch))
45 }
46 other => {
47 error!("Failed to interpret path='{}' as a workspace: {:?}", path_canon.display(), other);
48 Err(other)
49 }
50 }
51 }
52 }
53 }
54
55 #[instrument(level = "trace", skip(self))]
60 pub async fn ensure_git_clean(&self) -> Result<(), WorkspaceError> {
61 use std::process::Command;
63
64 debug!("Running git cleanliness check in single_or_workspace ...");
65 let output = Command::new("git")
66 .args(&["status", "--porcelain"])
67 .output()
68 .map_err(|io_err| {
69 error!("Failed to run `git status --porcelain`: {:?}", io_err);
70 WorkspaceError::GitError(GitError::FailedToRunGitStatusMakeSureGitIsInstalled)
71 })?;
72
73 if !output.status.success() {
74 let code = output.status.code().unwrap_or(-1);
75 error!(
76 "Command `git status --porcelain` exited with non-zero status code: {}",
77 code
78 );
79 return Err(WorkspaceError::GitError(
80 GitError::WorkingDirectoryIsNotCleanAborting,
81 ));
82 }
83
84 let stdout_str = String::from_utf8_lossy(&output.stdout);
85 if !stdout_str.trim().is_empty() {
86 error!("Git working directory is not clean:\n{}", stdout_str);
87 return Err(WorkspaceError::GitError(
88 GitError::WorkingDirectoryIsNotCleanAborting,
89 ));
90 }
91
92 info!("Git working directory is clean; proceeding.");
93 Ok(())
94 }
95
96 #[instrument(level="trace", skip(self))]
98 pub async fn validate_integrity(&mut self) -> Result<(), WorkspaceError> {
99 match self {
100 SingleOrWorkspace::Single(ch) => {
101 ch.validate_integrity().await.map_err(|crate_err| {
102 WorkspaceError::CrateError(crate_err)
103 })
104 }
105 SingleOrWorkspace::Workspace(ws) => {
106 ws.validate_integrity().await
107 }
108 }
109 }
110
111 #[instrument(level="trace", skip(self, operation))]
130 pub async fn run_operation<R, F>(&mut self, operation: F) -> Result<R, WorkspaceError>
131 where
132 R: Send + 'static,
134
135 F: for<'a> FnOnce(
137 &'a mut SingleOrWorkspace
138 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<R, WorkspaceError>> + Send + 'a>>
139 + Send
140 + 'static
141 {
142 let fut = operation(self);
144 fut.await
145 }
146}