use std::path::PathBuf; use anyhow::Context; fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), ) .init(); let args: Vec = std::env::args().collect(); if args.len() < 3 { anyhow::bail!("usage: weft-runtime "); } let app_id = &args[1]; let session_id: u64 = args[2] .parse() .with_context(|| format!("invalid session_id: {}", args[2]))?; tracing::info!(session_id, %app_id, "weft-runtime starting"); let pkg_dir = resolve_package(app_id)?; tracing::info!(path = %pkg_dir.display(), "package resolved"); let wasm_path = pkg_dir.join("app.wasm"); if !wasm_path.exists() { anyhow::bail!("app.wasm not found at {}", wasm_path.display()); } tracing::info!(session_id, %app_id, wasm = %wasm_path.display(), "executing module"); run_module(&wasm_path)?; tracing::info!(session_id, %app_id, "exiting"); Ok(()) } fn resolve_package(app_id: &str) -> anyhow::Result { for store_root in package_store_roots() { let pkg_dir = store_root.join(app_id); let manifest = pkg_dir.join("wapp.toml"); if manifest.exists() { return Ok(pkg_dir); } } anyhow::bail!("package '{}' not found in any package store", app_id) } #[cfg(not(feature = "wasmtime-runtime"))] fn run_module(_wasm_path: &std::path::Path) -> anyhow::Result<()> { println!("READY"); Ok(()) } #[cfg(feature = "wasmtime-runtime")] fn run_module(wasm_path: &std::path::Path) -> anyhow::Result<()> { use anyhow::Context as _; use wasmtime::{Config, Engine, Module, Store}; let engine = Engine::new(&Config::default()).context("create Wasmtime engine")?; let module = Module::from_file(&engine, wasm_path).context("load Wasm module")?; let mut linker: wasmtime::Linker = wasmtime::Linker::new(&engine); wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).context("add WASI to linker")?; let wasi = wasmtime_wasi::WasiCtxBuilder::new() .inherit_stdout() .inherit_stderr() .build(); let mut store = Store::new(&engine, wasi); println!("READY"); let instance = linker .instantiate(&mut store, &module) .context("instantiate module")?; let start = instance .get_typed_func::<(), ()>(&mut store, "_start") .context("get _start export")?; start.call(&mut store, ()).context("call _start")?; Ok(()) } fn package_store_roots() -> Vec { if let Ok(explicit) = std::env::var("WEFT_APP_STORE") { return vec![PathBuf::from(explicit)]; } let mut roots = Vec::new(); if let Ok(home) = std::env::var("HOME") { roots.push( PathBuf::from(home) .join(".local") .join("share") .join("weft") .join("apps"), ); } roots.push(PathBuf::from("/usr/share/weft/apps")); roots } #[cfg(test)] mod tests { use super::*; #[test] fn package_store_roots_includes_system_path() { let roots = package_store_roots(); assert!( roots .iter() .any(|p| p == &PathBuf::from("/usr/share/weft/apps")) ); } #[test] fn package_store_roots_uses_weft_app_store_when_set() { // SAFETY: test binary is single-threaded at this point. unsafe { std::env::set_var("WEFT_APP_STORE", "/custom/store") }; let roots = package_store_roots(); assert_eq!(roots, vec![PathBuf::from("/custom/store")]); unsafe { std::env::remove_var("WEFT_APP_STORE") }; } #[test] fn resolve_package_finds_installed_package() { use std::fs; let store = std::env::temp_dir().join(format!("weft_runtime_resolve_{}", std::process::id())); let pkg_dir = store.join("com.example.resolve"); fs::create_dir_all(&pkg_dir).unwrap(); fs::write( pkg_dir.join("wapp.toml"), "[package]\nid=\"com.example.resolve\"\n", ) .unwrap(); let prior = std::env::var("WEFT_APP_STORE").ok(); unsafe { std::env::set_var("WEFT_APP_STORE", &store) }; let result = resolve_package("com.example.resolve"); unsafe { match prior { Some(v) => std::env::set_var("WEFT_APP_STORE", v), None => std::env::remove_var("WEFT_APP_STORE"), } } let _ = fs::remove_dir_all(&store); assert!(result.is_ok()); assert!(result.unwrap().ends_with("com.example.resolve")); } #[test] fn resolve_package_errors_on_unknown_id() { let store = std::env::temp_dir().join(format!("weft_runtime_resolve_empty_{}", std::process::id())); let _ = std::fs::create_dir_all(&store); let prior = std::env::var("WEFT_APP_STORE").ok(); unsafe { std::env::set_var("WEFT_APP_STORE", &store) }; let result = resolve_package("com.does.not.exist"); unsafe { match prior { Some(v) => std::env::set_var("WEFT_APP_STORE", v), None => std::env::remove_var("WEFT_APP_STORE"), } } let _ = std::fs::remove_dir_all(&store); assert!(result.is_err()); } }