rok-cli 0.3.6

Developer CLI for rok-based Axum applications
//! `rok new <app-name>` — scaffold a new rok/Axum project from a template.

use std::path::Path;

use super::templates::{self, Template};

pub fn run(name: &str, template: Option<&str>) -> anyhow::Result<()> {
    let root = Path::new(name);
    if root.exists() {
        anyhow::bail!("Directory '{name}' already exists.");
    }

    let config = match template {
        Some(t) => {
            let tmpl = Template::from_str(t)?;
            templates::WizardConfig {
                template: tmpl,
                db: templates::DbDriver::Postgres,
                auth: templates::AuthDriver::Jwt,
                cache: templates::CacheDriver::Memory,
                queue: templates::QueueDriver::None,
                include_examples: true,
            }
        }
        None => templates::prompt_wizard()?,
    };

    let tmpl = config.template;

    println!(
        "Creating rok application: {name} (template: {})",
        tmpl.label()
    );
    println!();

    if !tmpl.handles_directory_creation() {
        std::fs::create_dir_all(root)?;
    }
    tmpl.generate(name, root, &config)?;

    // Best-effort git init — silently skip if git is not installed
    let _ = git_init(root);

    println!();
    println!("Application '{name}' created!");
    println!();
    println!("Next steps:");
    println!("  cd {name}");
    println!("  rok secrets:generate     # generate JWT_SECRET and write .env");
    println!("  # Or: cp .env.example .env && edit DATABASE_URL");
    println!("  rok serve");

    Ok(())
}

fn git_init(root: &Path) -> anyhow::Result<()> {
    use std::process::Command;

    // Skip when the target is already inside an existing git repo (e.g. a
    // workspace `examples/` directory).  Creating a nested repo would turn it
    // into a git submodule and break the parent repo's tracking.
    let already_in_repo = Command::new("git")
        .args(["rev-parse", "--is-inside-work-tree"])
        .current_dir(root)
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false);
    if already_in_repo {
        return Ok(());
    }

    Command::new("git").arg("init").current_dir(root).output()?;
    Command::new("git")
        .args(["add", "."])
        .current_dir(root)
        .output()?;
    Command::new("git")
        .args(["commit", "-m", "Initial commit (rok new)"])
        .env("GIT_AUTHOR_NAME", "rok")
        .env("GIT_AUTHOR_EMAIL", "rok@localhost")
        .env("GIT_COMMITTER_NAME", "rok")
        .env("GIT_COMMITTER_EMAIL", "rok@localhost")
        .current_dir(root)
        .output()?;
    Ok(())
}