slave 0.4.0

Laravel-inspired CLI for Rust MCP servers: scaffold apps whose business operations are MCP tools over Streamable HTTP, with built-in auth, users, roles and Turso migrations
//! Building Dioxus single-page apps (`make:app --stack dioxus`) with `dx`.

use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use anyhow::{Context, Result, bail};

/// How to get `dx`, matching the Dioxus version the template uses.
pub const INSTALL_HINT: &str = "install it with `cargo install dioxus-cli --version ^0.7 --locked`";

/// Whether `apps/<app>` is a Dioxus app.
pub fn is_dioxus_app(app_dir: &Path) -> bool {
    app_dir.join("Dioxus.toml").is_file()
}

pub fn dx_installed() -> bool {
    Command::new("dx")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .is_ok_and(|status| status.success())
}

/// Builds the app's WebAssembly bundle and returns the directory to serve.
pub fn build_spa(root: &Path, target_dir: &Path, app: &str, release: bool) -> Result<PathBuf> {
    if !dx_installed() {
        bail!("`{app}` is a Dioxus app, but `dx` was not found; {INSTALL_HINT}");
    }
    let mut command = Command::new("dx");
    command
        .args(["build", "--platform", "web", "--package", app])
        .current_dir(root);
    if release {
        command.arg("--release");
    }
    let status = command
        .status()
        .with_context(|| format!("failed to run `dx build` for {app}"))?;
    if !status.success() {
        bail!("`dx build` failed for {app}");
    }
    let profile = if release { "release" } else { "debug" };
    let public = target_dir
        .join("dx")
        .join(app)
        .join(profile)
        .join("web")
        .join("public");
    if !public.join("index.html").is_file() {
        bail!(
            "`dx build` finished but {} has no index.html",
            public.display()
        );
    }
    Ok(public)
}