waos-cli 0.1.0

cargo waos: writes a new program, builds a program as a component, runs it in the WaOS on this machine, and packages it as a signed .wapk.
//! Starting the WaOS app installed on this machine, for a run when no WaOS answers, with its log
//! kept where the user's logs are.
//!
//! The invariant: the app is found as the system finds an installed app, by its identifier,
//! never by a path in a checkout: whoever writes a program has WaOS installed, as Expo Go is
//! installed for a project's author.

use std::path::PathBuf;

use anyhow::Result;

/// The app's identifier, as `apps/waos`'s `[package.metadata.waft]` sets it.
pub const BUNDLE_IDENTIFIER: &str = "dev.waos.app";

/// What WaOS is started with: its library's panel closed, so a development run shows only the
/// program; the panel opens at a click on WaOS's Dock icon.
#[cfg(target_os = "macos")]
const PANEL_CLOSED: &str = "--panel-closed";

/// What WaOS reads from its environment, passed on when set here: `RUST_LOG`, what its log
/// holds, and `WAOS_NATIVE_UNWIND_INFO`, whether it compiles programs with what a native profiler
/// or debugger needs to walk through their frames.
#[cfg(target_os = "macos")]
const PASSED_ON: [&str; 2] = ["RUST_LOG", "WAOS_NATIVE_UNWIND_INFO"];

/// Starts WaOS.app, as the Finder would but with its library's panel closed ([`PANEL_CLOSED`]),
/// and answers where its log goes once the system has taken the request; the app answers on its
/// socket a moment later.
///
/// What WaOS writes to standard error, its log, is appended to `~/Library/Logs/WaOS.log`, and
/// the variables of [`PASSED_ON`] set here are passed on. All three take effect only when the
/// system starts the app: a WaOS running already keeps its own.
#[cfg(target_os = "macos")]
pub fn start() -> Result<PathBuf> {
    use std::ffi::OsString;
    use std::process::Command;

    use anyhow::{Context, bail};

    let log = log_path();
    let mut open = Command::new("open");
    open.args(["-b", BUNDLE_IDENTIFIER])
        .arg("--stderr")
        .arg(&log);
    for name in PASSED_ON {
        if let Some(value) = std::env::var_os(name) {
            let mut variable = OsString::from(format!("{name}="));
            variable.push(value);
            open.arg("--env").arg(variable);
        }
    }
    // What follows `--args` is WaOS's own command line, and comes last.
    open.args(["--args", PANEL_CLOSED]);
    let status = open.status().context("running open")?;
    if !status.success() {
        bail!(
            "WaOS.app is not installed: build it with `cargo waft build macos -p waos-app` and open it once, so the system knows where it is"
        );
    }
    Ok(log)
}

/// Where a WaOS started here keeps its log: the user's logs, where Console finds it.
#[cfg(target_os = "macos")]
fn log_path() -> PathBuf {
    let home = std::env::home_dir().unwrap_or_else(|| PathBuf::from("/"));
    home.join("Library/Logs/WaOS.log")
}

#[cfg(not(target_os = "macos"))]
pub fn start() -> Result<PathBuf> {
    anyhow::bail!(
        "no WaOS answers, and only macOS's WaOS.app ({BUNDLE_IDENTIFIER}) is known how to start: start WaOS yourself"
    )
}