diff --git a/crates/weft-pack/src/main.rs b/crates/weft-pack/src/main.rs index 3e3ef4c..f3f4923 100644 --- a/crates/weft-pack/src/main.rs +++ b/crates/weft-pack/src/main.rs @@ -43,10 +43,15 @@ fn main() -> anyhow::Result<()> { let manifest = load_manifest(Path::new(dir))?; print_info(&manifest); } + Some("install") => { + let dir = args.get(2).context("usage: weft-pack install ")?; + install_package(Path::new(dir))?; + } _ => { eprintln!("usage:"); - eprintln!(" weft-pack check validate a package directory"); - eprintln!(" weft-pack info print package metadata"); + eprintln!(" weft-pack check validate a package directory"); + eprintln!(" weft-pack info print package metadata"); + eprintln!(" weft-pack install install package to app store"); std::process::exit(1); } } @@ -140,22 +145,57 @@ fn is_valid_app_id(id: &str) -> bool { }) } -fn _resolve_store_roots() -> Vec { +fn resolve_install_root() -> anyhow::Result { if let Ok(explicit) = std::env::var("WEFT_APP_STORE") { - return vec![PathBuf::from(explicit)]; + return Ok(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"), + return Ok(PathBuf::from(home) + .join(".local") + .join("share") + .join("weft") + .join("apps")); + } + anyhow::bail!("cannot determine install root: HOME and WEFT_APP_STORE are both unset") +} + +fn install_package(dir: &Path) -> anyhow::Result<()> { + check_package(dir)?; + let manifest = load_manifest(dir)?; + let app_id = &manifest.package.id; + + let store_root = resolve_install_root()?; + let dest = store_root.join(app_id); + + if dest.exists() { + anyhow::bail!( + "package '{}' is already installed at {}; remove it first", + app_id, + dest.display() ); } - roots.push(PathBuf::from("/usr/share/weft/apps")); - roots + + copy_dir(dir, &dest) + .with_context(|| format!("copy {} -> {}", dir.display(), dest.display()))?; + + println!("installed {} -> {}", app_id, dest.display()); + Ok(()) +} + +fn copy_dir(src: &Path, dst: &Path) -> anyhow::Result<()> { + std::fs::create_dir_all(dst)?; + for entry in std::fs::read_dir(src)? { + let entry = entry?; + let src_path = entry.path(); + let dst_path = dst.join(entry.file_name()); + if src_path.is_dir() { + copy_dir(&src_path, &dst_path)?; + } else { + std::fs::copy(&src_path, &dst_path) + .with_context(|| format!("copy {}", src_path.display()))?; + } + } + Ok(()) } #[cfg(test)] diff --git a/crates/weft-servo-shell/src/main.rs b/crates/weft-servo-shell/src/main.rs index c3e120f..86dfc34 100644 --- a/crates/weft-servo-shell/src/main.rs +++ b/crates/weft-servo-shell/src/main.rs @@ -43,23 +43,6 @@ fn system_ui_html_path() -> anyhow::Result { } fn embed_servo(_wayland_display: &str, _html_path: &std::path::Path) -> anyhow::Result<()> { - // Wave 4 skeleton entry point. - // - // Full implementation requires completion of the items in - // docs/architecture/winit-wayland-audit.md before production readiness, - // and the following integration work: - // - // 1. Add servo git dependency (not on crates.io; requires building Servo) - // 2. Implement servo::EmbedderMethods and servo::WindowMethods for the - // WEFT Wayland surface (winit + EGL, or smithay-client-toolkit directly) - // 3. Call servo::Servo::new() with the window and embedder - // 4. Load the system UI via servo::ServoUrl::parse(html_path) - // 5. Run the Servo event loop, forwarding Wayland events from winit - // - // The Servo dependency is intentionally absent from Cargo.toml at this stage. - // It requires a git dependency on github.com/servo/servo which embeds - // SpiderMonkey (GeckoMedia) and has a multi-minute build time. It is added - // when the embedder contract is ready. anyhow::bail!( "Servo embedding not yet implemented; \ see docs/architecture/winit-wayland-audit.md for gap assessment"