use std::path::{Path, PathBuf};
use std::sync::Mutex;
use lean_rs::error::LeanResult;
pub(crate) const SHIM_PACKAGE_NAME: &str = "lean_rs_host_shims";
pub(crate) const SHIM_LIB_NAME: &str = "LeanRsHostShims";
pub(crate) const INTEROP_PACKAGE_NAME: &str = "lean_rs_interop_shims";
pub(crate) const INTEROP_LIB_NAME: &str = "LeanRsInterop";
const BUNDLED_SHIMS_DIR: &str = "shims";
const BUNDLED_HOST_SHIMS_DIR: &str = "lean-rs-host-shims";
const BUNDLED_INTEROP_SHIMS_DIR: &str = "lean-rs-interop-shims";
static BUNDLED_SHIM_BUILD_LOCK: Mutex<()> = Mutex::new(());
pub(crate) struct LakeProject {
root: PathBuf,
}
impl LakeProject {
pub(crate) fn new(root: impl AsRef<Path>) -> LeanResult<Self> {
let root = root.as_ref();
if !root.is_dir() {
return Err(lean_rs::__host_internals::host_module_init(format!(
"Lake project root '{}' does not exist or is not a directory",
root.display()
)));
}
Ok(Self {
root: root.to_path_buf(),
})
}
pub(crate) fn capability_dylib(&self, package: &str, lib_name: &str) -> PathBuf {
let dylib_extension = if cfg!(target_os = "macos") { "dylib" } else { "so" };
let lib_dir = self.root.join(".lake").join("build").join("lib");
let escaped_package = package.replace('_', "__");
let new_style = lib_dir.join(format!("lib{escaped_package}_{lib_name}.{dylib_extension}"));
let old_style = lib_dir.join(format!("lib{lib_name}.{dylib_extension}"));
if new_style.is_file() {
new_style
} else if old_style.is_file() {
old_style
} else {
new_style
}
}
pub(crate) fn olean_search_path(&self) -> PathBuf {
self.root.join(".lake").join("build").join("lib").join("lean")
}
pub(crate) fn shim_olean_search_path() -> LeanResult<PathBuf> {
let package_dir = bundled_host_shims_root();
drop(build_bundled_target(&package_dir, SHIM_LIB_NAME)?);
Ok(package_dir.join(".lake").join("build").join("lib").join("lean"))
}
pub(crate) fn interop_olean_search_path() -> LeanResult<PathBuf> {
let package_dir = bundled_interop_shims_root();
drop(build_bundled_target(&package_dir, INTEROP_LIB_NAME)?);
Ok(package_dir.join(".lake").join("build").join("lib").join("lean"))
}
pub(crate) fn source_roots(&self) -> LeanResult<Vec<PathBuf>> {
Ok(vec![self.root.clone(), bundled_host_shims_root()])
}
pub(crate) fn shim_dylib() -> LeanResult<PathBuf> {
build_bundled_target(&bundled_host_shims_root(), SHIM_LIB_NAME)
}
pub(crate) fn interop_dylib() -> LeanResult<PathBuf> {
build_bundled_target(&bundled_interop_shims_root(), INTEROP_LIB_NAME)
}
}
fn bundled_host_shims_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join(BUNDLED_SHIMS_DIR)
.join(BUNDLED_HOST_SHIMS_DIR)
}
fn bundled_interop_shims_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join(BUNDLED_SHIMS_DIR)
.join(BUNDLED_INTEROP_SHIMS_DIR)
}
fn build_bundled_target(project_root: &Path, target_name: &str) -> LeanResult<PathBuf> {
let _guard = BUNDLED_SHIM_BUILD_LOCK.lock().map_err(|_| {
lean_rs::__host_internals::host_module_init("bundled lean-rs shim build lock is poisoned".to_owned())
})?;
lean_toolchain::build_lake_target_quiet(project_root, target_name).map_err(|diagnostic| {
lean_rs::__host_internals::host_module_init(format!(
"could not build bundled lean-rs shim target `{target_name}` under {}: {diagnostic}",
project_root.display()
))
})
}