use std::{
path::{Path, PathBuf},
str,
};
use anyhow::Context as _;
use once_cell::sync::OnceCell;
static WORKSPACE: OnceCell<Option<PathBuf>> = OnceCell::new();
fn locate_workspace_inner() -> anyhow::Result<PathBuf> {
let output = std::process::Command::new(
std::env::var("CARGO")
.ok()
.unwrap_or_else(|| "cargo".to_string()),
)
.arg("locate-project")
.arg("--workspace")
.output()
.context("Can't find Cargo workspace location")?;
let output =
serde_json::from_slice::<serde_json::Value>(&output.stdout).with_context(|| {
format!(
"Error parsing `cargo locate-project` output {}",
str::from_utf8(&output.stdout).unwrap_or("(non-utf8 output)")
)
})?;
let root = output.get("root").with_context(|| {
format!("root doesn't exist in output from `cargo locate-project` {output:?}")
})?;
let serde_json::Value::String(root) = root else {
return Err(anyhow::anyhow!("`root` is not a string: {root:?}"));
};
let root_path = PathBuf::from(root);
Ok(root_path
.parent()
.with_context(|| format!("`root` path doesn't have a parent: {}", root_path.display()))?
.to_path_buf())
}
pub fn locate_workspace() -> Option<&'static Path> {
WORKSPACE
.get_or_init(|| {
let result = locate_workspace_inner();
if result.is_err() {
tracing::info!(
"locate_workspace() failed. You are using an already compiled version"
);
}
result.ok()
})
.as_deref()
}
pub fn workspace_dir_or_current_dir() -> &'static Path {
locate_workspace().unwrap_or_else(|| Path::new("."))
}