use std::{
fs::{File, TryLockError},
path::{Path, PathBuf},
};
use proc_macro2::Span;
use crate::Result;
pub(crate) const OUT_DIR: &str = env!("OUT_DIR");
pub fn build_dir(project_dir: &Path, per_project_cache: bool) -> PathBuf {
if per_project_cache {
return project_dir.join("build_cache");
}
PathBuf::from(OUT_DIR).join("build_cache")
}
pub fn manifest_path() -> Result<PathBuf> {
let manifest_path = std::env::var("CARGO_MANIFEST_PATH")
.map_err(|_| error!(Span::call_site() => "CARGO_MANIFEST_PATH is not set"))?;
let manifest_path = PathBuf::from(&manifest_path);
Ok(manifest_path)
}
pub fn search_for_parent_manifest<U>(
start: &Path,
extract: impl Fn(&Path) -> Result<Option<U>>,
) -> Result<Option<U>> {
for current in start.ancestors() {
let try_path = current.join("Cargo.toml");
if !try_path.exists() {
continue;
}
let result = extract(&try_path)?;
if let Some(value) = result {
return Ok(Some(value));
}
}
Ok(None)
}
pub fn calculate_generated_path(ident: proc_macro2::Span) -> (PathBuf, bool) {
let span: proc_macro::Span = ident.unwrap();
let mut stable = true;
let file = span.local_file().map_or_else(
|| {
stable = false;
span.file()
},
|v| v.display().to_string(),
);
let crate_name = std::env::var("CARGO_PKG_NAME").unwrap_or_else(|_| {
stable = false;
"unknown".to_string()
});
let crate_version = std::env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| {
stable = false;
"unknown".to_string()
});
let file = sanitize_path(&file);
let line = span.line();
let column = span.column();
let path = format!("{OUT_DIR}/generated/{crate_name}_{crate_version}/{file}_{line}_{column}");
(PathBuf::from(path), stable)
}
fn sanitize_path(path: &str) -> String {
path.replace(['\\', '/'], "_")
}
#[derive(Debug)]
pub struct FsLockGuard {
path: PathBuf,
file: File,
}
impl FsLockGuard {
pub fn new(path: PathBuf) -> Result<Self> {
let file = std::fs::create_dir_all(path.parent().unwrap())
.and_then(|()| File::create(&path))
.map_err(|e| error!(Span::call_site() => "Failed to create lock file: {e}"))?;
let this = Self { path, file };
this.lock()?;
Ok(this)
}
fn lock(&self) -> Result<()> {
match self.file.try_lock() {
Err(TryLockError::WouldBlock) => {
debug!("Waiting for lock file: {}", self.path.display());
self.file.lock()
}
Err(TryLockError::Error(e)) => Err(e),
Ok(()) => Ok(()),
}
.map_err(|e| error!(Span::call_site() => "Failed to lock lock file: {e}"))
}
fn unlock(&self) -> Result<()> {
self.file
.unlock()
.map_err(|e| error!(Span::call_site() => "Failed to unlock lock file: {e}"))
}
}
impl Drop for FsLockGuard {
fn drop(&mut self) {
self.unlock().unwrap();
}
}