waos-cli 0.2.0

cargo waos: writes a new program, builds a program as a component, runs it in the WaOS on this machine, packages it as a signed .wapk, and takes screenshots of its windows and gives them input while it runs.
//! A new program's `Cargo.toml`: its package, its dependencies on the SDK and on Waft by their
//! crates.io versions, as a published template names them, and its `[package.metadata.waos]`,
//! with every field `cargo waos package` reads.
//!
//! The invariant: the versions are this release's. The SDK's is this crate's own, since the
//! workspace versions its crates together; Waft's is the one `waft-embedder-wasdk` is built on,
//! which `tests/new.rs` holds it to by building each template against the workspace.

use super::icon;
use super::program_name::ProgramName;
use super::template::Template;

/// The SDK's version, `wasdk`'s and `waft-embedder-wasdk`'s, as a requirement: this release's
/// major and minor.
const SDK_VERSION: &str = concat!(
    env!("CARGO_PKG_VERSION_MAJOR"),
    ".",
    env!("CARGO_PKG_VERSION_MINOR")
);

/// Waft's version, the facade's and the kits', as a requirement.
const WAFT_VERSION: &str = "0.5";

/// The `Cargo.toml` of a program named `name`, written with `template`'s kit.
pub fn cargo_toml(name: &ProgramName, template: Template) -> String {
    let id = name.id();
    let display_name = name.display_name();
    let kit = template.kit();
    let icon = icon::FILE;
    format!(
        r#"[package]
name = "{name}"
version = "0.1.0"
edition = "2024"

[dependencies]
wasdk = "{SDK_VERSION}"
waft-embedder-wasdk = "{SDK_VERSION}"
waft = "{WAFT_VERSION}"
{kit} = "{WAFT_VERSION}"

# What WaOS knows the program by. The id names it for good: every update is published under it.
# The icon is a square PNG, at least 256 pixels on a side.
[package.metadata.waos]
id = "{id}"
name = "{display_name}"
icon = "{icon}"

# A workspace of its own, so it builds by itself in any folder, even inside another workspace.
[workspace]
"#
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    fn written(name: &str, template: Template) -> toml::Table {
        let name = name.parse().unwrap();
        cargo_toml(&name, template).parse().unwrap()
    }

    #[test]
    fn the_manifest_names_the_sdk_waft_and_the_kit_by_version() {
        let manifest = written("my-app", Template::Winui);
        let dependencies = manifest["dependencies"].as_table().unwrap();
        let mut named: Vec<(&str, &str)> = dependencies
            .iter()
            .map(|(name, version)| (name.as_str(), version.as_str().unwrap()))
            .collect();
        named.sort();
        assert_eq!(
            named,
            [
                ("waft", "0.5"),
                ("waft-embedder-wasdk", SDK_VERSION),
                ("waft-winui", "0.5"),
                ("wasdk", SDK_VERSION),
            ]
        );
        let cupertino = written("my-app", Template::Cupertino);
        assert!(cupertino["dependencies"].get("waft-cupertino").is_some());
        assert!(cupertino["dependencies"].get("waft-winui").is_none());
    }

    #[test]
    fn the_metadata_gives_the_id_the_name_and_the_icon() {
        let manifest = written("my-app", Template::Cupertino);
        let package = &manifest["package"];
        assert_eq!(package["name"].as_str(), Some("my-app"));
        let waos = &package["metadata"]["waos"];
        assert_eq!(waos["id"].as_str(), Some("dev.example.myapp"));
        assert_eq!(waos["name"].as_str(), Some("My app"));
        assert_eq!(waos["icon"].as_str(), Some("icon.png"));
    }

    #[test]
    fn the_program_is_a_workspace_of_its_own() {
        let manifest = written("hello", Template::Cupertino);
        assert!(manifest["workspace"].as_table().unwrap().is_empty());
    }
}