crate::ix!();
pub enum SingleOrWorkspace {
Single(CrateHandle),
Workspace(Workspace<PathBuf, CrateHandle>),
}
impl SingleOrWorkspace {
#[instrument(level = "trace")]
pub async fn detect(path: &Path) -> Result<Self, WorkspaceError> {
let path_canon = match tokio::fs::canonicalize(path).await {
Ok(abs) => abs,
Err(e) => {
error!("Could not canonicalize path={:?}: {:?}", path, e);
path.to_path_buf()
}
};
debug!("Attempting to interpret path='{}' as a workspace", path_canon.display());
match Workspace::<PathBuf, CrateHandle>::new(&path_canon).await {
Ok(ws) => {
debug!("Successfully loaded path='{}' as a workspace", path_canon.display());
Ok(SingleOrWorkspace::Workspace(ws))
}
Err(e) => {
match e {
WorkspaceError::ActuallyInSingleCrate { .. } => {
debug!("Falling back to single crate handle for path='{}'", path_canon.display());
let ch = CrateHandle::new(&path_canon).await.map_err(|crate_err| {
WorkspaceError::CrateError(crate_err)
})?;
Ok(SingleOrWorkspace::Single(ch))
}
other => {
error!("Failed to interpret path='{}' as a workspace: {:?}", path_canon.display(), other);
Err(other)
}
}
}
}
}
#[instrument(level = "trace", skip(self))]
pub async fn ensure_git_clean(&self) -> Result<(), WorkspaceError> {
use std::process::Command;
debug!("Running git cleanliness check in single_or_workspace ...");
let output = Command::new("git")
.args(&["status", "--porcelain"])
.output()
.map_err(|io_err| {
error!("Failed to run `git status --porcelain`: {:?}", io_err);
WorkspaceError::GitError(GitError::FailedToRunGitStatusMakeSureGitIsInstalled)
})?;
if !output.status.success() {
let code = output.status.code().unwrap_or(-1);
error!(
"Command `git status --porcelain` exited with non-zero status code: {}",
code
);
return Err(WorkspaceError::GitError(
GitError::WorkingDirectoryIsNotCleanAborting,
));
}
let stdout_str = String::from_utf8_lossy(&output.stdout);
if !stdout_str.trim().is_empty() {
error!("Git working directory is not clean:\n{}", stdout_str);
return Err(WorkspaceError::GitError(
GitError::WorkingDirectoryIsNotCleanAborting,
));
}
info!("Git working directory is clean; proceeding.");
Ok(())
}
#[instrument(level="trace", skip(self))]
pub async fn validate_integrity(&mut self) -> Result<(), WorkspaceError> {
match self {
SingleOrWorkspace::Single(ch) => {
ch.validate_integrity().await.map_err(|crate_err| {
WorkspaceError::CrateError(crate_err)
})
}
SingleOrWorkspace::Workspace(ws) => {
ws.validate_integrity().await
}
}
}
#[instrument(level="trace", skip(self, operation))]
pub async fn run_operation<R, F>(&mut self, operation: F) -> Result<R, WorkspaceError>
where
R: Send + 'static,
F: for<'a> FnOnce(
&'a mut SingleOrWorkspace
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<R, WorkspaceError>> + Send + 'a>>
+ Send
+ 'static
{
let fut = operation(self);
fut.await
}
}