use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use std::time::{Duration, SystemTime};
use boxlite_shared::errors::{BoxliteError, BoxliteResult};
include!(concat!(env!("OUT_DIR"), "/embedded_manifest.rs"));
pub struct EmbeddedRuntime {
dir: PathBuf,
}
impl EmbeddedRuntime {
const STALE_TTL: Duration = Duration::from_secs(7 * 24 * 3600);
pub fn get() -> Option<&'static Self> {
static INSTANCE: OnceLock<Option<EmbeddedRuntime>> = OnceLock::new();
INSTANCE.get_or_init(Self::init).as_ref()
}
pub fn dir(&self) -> &Path {
&self.dir
}
fn init() -> Option<Self> {
if MANIFEST.is_empty() {
return None;
}
match Self::extract() {
Ok(runtime) => {
runtime.cleanup_stale();
Some(runtime)
}
Err(e) => {
tracing::warn!("Embedded runtime extraction failed: {}", e);
None
}
}
}
fn extract() -> BoxliteResult<Self> {
let dir = Self::versioned_dir()?;
let stamp = dir.join(".complete");
if stamp.exists() {
let now = filetime::FileTime::now();
let _ = filetime::set_file_mtime(&stamp, now);
return Ok(Self { dir });
}
let tmp = dir.with_extension(format!("extracting.{}", std::process::id()));
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp)
.map_err(|e| BoxliteError::Storage(format!("mkdir {}: {}", tmp.display(), e)))?;
for (name, mode, data) in MANIFEST {
let path = tmp.join(name);
std::fs::write(&path, data)
.map_err(|e| BoxliteError::Storage(format!("write {}: {}", path.display(), e)))?;
#[cfg(unix)]
Self::set_permissions(&path, *mode)?;
}
std::fs::write(tmp.join(".complete"), crate::VERSION)
.map_err(|e| BoxliteError::Storage(format!("write stamp: {}", e)))?;
match std::fs::rename(&tmp, &dir) {
Ok(()) => {
tracing::info!(
dir = %dir.display(),
files = MANIFEST.len(),
manifest_hash = env!("BOXLITE_MANIFEST_HASH"),
"Extracted embedded runtime"
);
}
Err(_) if dir.join(".complete").exists() => {
let _ = std::fs::remove_dir_all(&tmp);
tracing::debug!("Embedded runtime already extracted by another process");
}
Err(e) => {
let _ = std::fs::remove_dir_all(&tmp);
return Err(BoxliteError::Storage(format!(
"rename {} → {}: {}",
tmp.display(),
dir.display(),
e
)));
}
}
Ok(Self { dir })
}
fn cleanup_stale(&self) {
let Some(parent) = self.dir.parent() else {
return;
};
let Ok(entries) = std::fs::read_dir(parent) else {
return;
};
let cutoff = SystemTime::now() - Self::STALE_TTL;
for entry in entries.filter_map(Result::ok) {
let path = entry.path();
if path == self.dir || !path.is_dir() {
continue;
}
let stamp = path.join(".complete");
let is_stale = std::fs::metadata(&stamp)
.and_then(|m| m.modified())
.is_ok_and(|mtime| mtime < cutoff);
if is_stale {
tracing::info!(dir = %path.display(), "Removing stale embedded cache");
let _ = std::fs::remove_dir_all(&path);
}
}
}
fn versioned_dir() -> BoxliteResult<PathBuf> {
let data_dir = dirs::data_local_dir()
.ok_or_else(|| BoxliteError::Storage("No local data directory".into()))?;
let dir_name = if env!("BOXLITE_BUILD_PROFILE") == "release" {
format!("v{}", crate::VERSION)
} else {
format!("v{}-{}", crate::VERSION, env!("BOXLITE_MANIFEST_HASH"))
};
let dir = data_dir.join("boxlite").join("runtimes").join(dir_name);
let parent = dir.parent().ok_or_else(|| {
BoxliteError::Storage(format!(
"Embedded runtime path has no parent: {}",
dir.display()
))
})?;
std::fs::create_dir_all(parent)
.map_err(|e| BoxliteError::Storage(format!("mkdir {}: {}", parent.display(), e)))?;
Ok(dir)
}
#[cfg(unix)]
fn set_permissions(path: &Path, mode: u32) -> BoxliteResult<()> {
use std::os::unix::fs::PermissionsExt;
let mode = match mode & 0o777 {
0 => 0o644,
mode => mode,
};
std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode)).map_err(|e| {
BoxliteError::Storage(format!("chmod {:o} {}: {}", mode, path.display(), e))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manifest_is_available() {
let _ = MANIFEST.len();
}
#[test]
fn versioned_dir_uses_data_local_dir() {
let dir = EmbeddedRuntime::versioned_dir().unwrap();
let dir_str = dir.to_string_lossy();
assert!(
dir_str.contains("boxlite/runtimes/"),
"Expected path to contain boxlite/runtimes/, got {}",
dir.display()
);
let dir_name = dir.file_name().unwrap().to_string_lossy();
assert!(
dir_name.starts_with(&format!("v{}", crate::VERSION)),
"Expected dir to start with v{}, got {}",
crate::VERSION,
dir.display()
);
if env!("BOXLITE_BUILD_PROFILE") != "release" {
let expected = format!("v{}-{}", crate::VERSION, env!("BOXLITE_MANIFEST_HASH"));
assert_eq!(
dir_name, expected,
"Debug build dir should include hash suffix"
);
}
}
#[test]
fn extraction_creates_complete_stamp() {
if MANIFEST.is_empty() {
return;
}
if let Some(runtime) = EmbeddedRuntime::get() {
assert!(runtime.dir().join(".complete").exists());
for (name, _, _) in MANIFEST {
assert!(
runtime.dir().join(name).exists(),
"Expected {} to exist in cache",
name
);
}
}
}
}