mirror of
https://github.com/zhom/banderole.git
synced 2026-06-06 14:33:53 +02:00
154 lines
5.1 KiB
Rust
154 lines
5.1 KiB
Rust
use anyhow::{Context, Result};
|
|
use std::env;
|
|
use std::fs;
|
|
use std::io::Cursor;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Stdio};
|
|
use zip::ZipArchive;
|
|
|
|
// These will be replaced during the build process with actual embedded data
|
|
// The build script will generate a data.rs file with the actual data
|
|
include!(concat!(env!("OUT_DIR"), "/data.rs"));
|
|
|
|
fn main() -> Result<()> {
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
// Get cache directory
|
|
let cache_dir = get_cache_dir()?;
|
|
let app_dir = cache_dir.join(&BUILD_ID);
|
|
let ready_file = app_dir.join(".ready");
|
|
|
|
// Check if already extracted and ready
|
|
if ready_file.exists() && is_extraction_valid(&app_dir)? {
|
|
return run_app(&app_dir, &args[1..]);
|
|
}
|
|
|
|
// Extract application if needed
|
|
extract_application(&app_dir)?;
|
|
|
|
// Mark as ready
|
|
fs::write(&ready_file, "ready")?;
|
|
|
|
// Run the application
|
|
run_app(&app_dir, &args[1..])
|
|
}
|
|
|
|
fn get_cache_dir() -> Result<PathBuf> {
|
|
let cache_dir = if let Some(xdg_cache) = env::var_os("XDG_CACHE_HOME") {
|
|
PathBuf::from(xdg_cache).join("banderole")
|
|
} else if let Some(home) = env::var_os("HOME") {
|
|
PathBuf::from(home).join(".cache").join("banderole")
|
|
} else if cfg!(windows) {
|
|
if let Some(appdata) = env::var_os("LOCALAPPDATA") {
|
|
PathBuf::from(appdata).join("banderole")
|
|
} else if let Some(temp) = env::var_os("TEMP") {
|
|
PathBuf::from(temp).join("banderole-cache")
|
|
} else {
|
|
PathBuf::from("C:\\temp\\banderole-cache")
|
|
}
|
|
} else {
|
|
PathBuf::from("/tmp/banderole-cache")
|
|
};
|
|
|
|
fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
|
|
Ok(cache_dir)
|
|
}
|
|
|
|
fn is_extraction_valid(app_dir: &Path) -> Result<bool> {
|
|
let app_package_json = app_dir.join("app").join("package.json");
|
|
let node_executable = if cfg!(windows) {
|
|
app_dir.join("node").join("node.exe")
|
|
} else {
|
|
app_dir.join("node").join("bin").join("node")
|
|
};
|
|
|
|
Ok(app_package_json.exists() && node_executable.exists())
|
|
}
|
|
|
|
fn extract_application(app_dir: &Path) -> Result<()> {
|
|
// Create app directory
|
|
fs::create_dir_all(app_dir).context("Failed to create app directory")?;
|
|
|
|
// Extract embedded zip data
|
|
let cursor = Cursor::new(ZIP_DATA);
|
|
let mut archive = ZipArchive::new(cursor).context("Failed to open embedded zip archive")?;
|
|
|
|
for i in 0..archive.len() {
|
|
let mut file = archive.by_index(i).context("Failed to read zip entry")?;
|
|
let outpath = app_dir.join(file.name());
|
|
|
|
if file.name().ends_with('/') {
|
|
// Directory
|
|
fs::create_dir_all(&outpath).context("Failed to create directory")?;
|
|
} else {
|
|
// File
|
|
if let Some(parent) = outpath.parent() {
|
|
fs::create_dir_all(parent).context("Failed to create parent directory")?;
|
|
}
|
|
|
|
let mut outfile = fs::File::create(&outpath).context("Failed to create output file")?;
|
|
std::io::copy(&mut file, &mut outfile).context("Failed to extract file")?;
|
|
|
|
// Set executable permissions on Unix systems
|
|
#[cfg(unix)]
|
|
{
|
|
if let Some(mode) = file.unix_mode() {
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let permissions = std::fs::Permissions::from_mode(mode);
|
|
fs::set_permissions(&outpath, permissions).context("Failed to set permissions")?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn run_app(app_dir: &Path, args: &[String]) -> Result<()> {
|
|
let app_path = app_dir.join("app");
|
|
let node_executable = if cfg!(windows) {
|
|
app_dir.join("node").join("node.exe")
|
|
} else {
|
|
app_dir.join("node").join("bin").join("node")
|
|
};
|
|
|
|
// Change to app directory
|
|
env::set_current_dir(&app_path).context("Failed to change to app directory")?;
|
|
|
|
// Find main script from package.json
|
|
let main_script = find_main_script(&app_path)?;
|
|
|
|
// Build command arguments
|
|
let mut cmd_args = vec![main_script];
|
|
cmd_args.extend(args.iter().cloned());
|
|
|
|
// Execute Node.js application
|
|
let status = Command::new(&node_executable)
|
|
.args(&cmd_args)
|
|
.stdin(Stdio::inherit())
|
|
.stdout(Stdio::inherit())
|
|
.stderr(Stdio::inherit())
|
|
.status()
|
|
.context("Failed to execute Node.js application")?;
|
|
|
|
std::process::exit(status.code().unwrap_or(1));
|
|
}
|
|
|
|
fn find_main_script(app_path: &Path) -> Result<String> {
|
|
let package_json_path = app_path.join("package.json");
|
|
|
|
if package_json_path.exists() {
|
|
let package_content = fs::read_to_string(&package_json_path)
|
|
.context("Failed to read package.json")?;
|
|
|
|
if let Ok(package_json) = serde_json::from_str::<serde_json::Value>(&package_content) {
|
|
if let Some(main) = package_json["main"].as_str() {
|
|
return Ok(main.to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default to index.js
|
|
Ok("index.js".to_string())
|
|
}
|