rok-cli 0.3.2

Developer CLI for rok-based Axum applications
//! Project template registry for `rok new`.

pub mod api;
pub mod htmx;
pub mod microservice;
pub mod minimal;
pub mod saas;

use std::path::Path;

// ── Template enum ─────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Template {
    Api,
    Saas,
    Htmx,
    Microservice,
    Minimal,
}

impl Template {
    pub fn from_str(s: &str) -> anyhow::Result<Self> {
        match s {
            "api" => Ok(Self::Api),
            "saas" => Ok(Self::Saas),
            "htmx" => Ok(Self::Htmx),
            "microservice" => Ok(Self::Microservice),
            "minimal" => Ok(Self::Minimal),
            _ => anyhow::bail!(
                "Unknown template '{s}'. Valid options: api, saas, htmx, microservice, minimal"
            ),
        }
    }

    pub fn label(&self) -> &'static str {
        match self {
            Self::Api => "api",
            Self::Saas => "saas",
            Self::Htmx => "htmx",
            Self::Microservice => "microservice",
            Self::Minimal => "minimal",
        }
    }

    /// Whether this template creates its own root directory.
    /// Git-based templates (Api) clone into a new directory, so `rok new`
    /// should skip creating the empty root directory beforehand.
    pub fn handles_directory_creation(&self) -> bool {
        matches!(self, Self::Api)
    }

    pub fn generate(&self, project_name: &str, root: &Path) -> anyhow::Result<()> {
        match self {
            Self::Api => api::generate(project_name, root),
            Self::Saas => saas::generate(project_name, root),
            Self::Htmx => htmx::generate(project_name, root),
            Self::Microservice => microservice::generate(project_name, root),
            Self::Minimal => minimal::generate(project_name, root),
        }
    }
}

// ── Interactive selection ─────────────────────────────────────────────────────

pub fn prompt_template() -> anyhow::Result<Template> {
    use dialoguer::Select;

    let items = &[
        "api          REST API with JWT auth + CRUD ready",
        "saas         Multi-tenant SaaS (magic link, billing hooks)",
        "htmx         Full-stack with Htmx + Minijinja templates",
        "microservice Minimal service with health endpoint + Docker",
        "minimal      Bare skeleton (axum + sqlx only)",
    ];

    let sel = Select::new()
        .with_prompt("Choose a template")
        .items(items)
        .default(0)
        .interact()?;

    Ok(match sel {
        0 => Template::Api,
        1 => Template::Saas,
        2 => Template::Htmx,
        3 => Template::Microservice,
        _ => Template::Minimal,
    })
}