use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use std::{fs, path};
use anyhow::Context;
use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, init_dev_corelib};
use indoc::indoc;
use tempfile::tempdir;
use tracing::{error, warn};
use crate::config::Config;
use crate::lang::db::AnalysisDatabase;
use crate::toolchain::scarb::{SCARB_TOML, ScarbToolchain};
pub fn try_to_init_unmanaged_core(
db: &mut AnalysisDatabase,
config: &Config,
scarb: &ScarbToolchain,
) {
if let Some(path) = find_unmanaged_core(config, scarb) {
init_dev_corelib(db, path);
} else {
warn!("failed to find unmanaged core crate")
}
}
pub fn find_unmanaged_core(config: &Config, scarb: &ScarbToolchain) -> Option<PathBuf> {
find_core_at_config_path(config)
.or_else(|| find_scarb_managed_core(scarb))
.or_else(|| {
if cfg!(feature = "testing") {
cairo_lang_filesystem::detect::detect_corelib()
} else {
None
}
})
.and_then(ensure_absolute)
}
fn find_core_at_config_path(config: &Config) -> Option<PathBuf> {
find_core_at_path(config.unmanaged_core_path.as_ref()?.as_path())
}
fn find_core_at_path(root_path: &Path) -> Option<PathBuf> {
let mut path = root_path.to_owned();
path.push("corelib");
path.push("src");
if path.exists() {
return Some(path);
}
let mut path = root_path.to_owned();
path.push("src");
if path.exists() {
return Some(path);
}
if root_path.exists() {
return Some(root_path.to_owned());
}
None
}
#[tracing::instrument(skip_all)]
fn find_scarb_managed_core(scarb: &ScarbToolchain) -> Option<PathBuf> {
let lookup = || {
let workspace = tempdir()
.context("failed to create temporary directory")
.inspect_err(|e| warn!("{e:?}"))
.ok()?;
let scarb_toml = workspace.path().join(SCARB_TOML);
fs::write(&scarb_toml, indoc! {r#"
[package]
name = "cairols_unmanaged_core_lookup"
version = "1.0.0"
"#})
.context("failed to write Scarb.toml")
.inspect_err(|e| warn!("{e:?}"))
.ok()?;
let metadata = scarb.silent().metadata(&scarb_toml).inspect_err(|e| warn!("{e:?}")).ok()?;
let _ = workspace
.close()
.context("failed to wipe temporary directory")
.inspect_err(|e| warn!("{e:?}"));
metadata.compilation_units.into_iter().find_map(|compilation_unit| {
compilation_unit
.components
.iter()
.find(|component| component.name == CORELIB_CRATE_NAME)
.map(|component| component.source_root().to_path_buf().into_std_path_buf())
})
};
static CACHE: OnceLock<Option<PathBuf>> = OnceLock::new();
CACHE.get_or_init(lookup).clone()
}
fn ensure_absolute(path: PathBuf) -> Option<PathBuf> {
path::absolute(&path)
.with_context(|| {
format!("failed to make `core` crate path absolute: {path}", path = path.display())
})
.inspect_err(|e| error!("{e:?}"))
.ok()
}