cargo-rustapi 0.1.470

The official CLI tool for the RustAPI framework. Scaffold new projects, run development servers, and manage database migrations.
//! Project templates

mod api;
mod full;
mod minimal;
mod web;

use anyhow::Result;
use clap::ValueEnum;

/// Available project templates
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ProjectTemplate {
    /// Minimal starter template
    Minimal,
    /// REST API template with CRUD
    Api,
    /// Web app template with Tera templates
    Web,
    /// Full-featured template with all batteries
    Full,
}

/// Opinionated feature presets layered on top of project templates.
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ProjectPreset {
    /// Production-oriented HTTP API defaults.
    #[value(name = "prod-api")]
    Production,
    /// AI-friendly API defaults with TOON support.
    #[value(name = "ai-api")]
    Ai,
    /// Realtime API defaults with WebSocket support.
    #[value(name = "realtime-api")]
    Realtime,
}

impl ProjectPreset {
    /// Default base template for this preset.
    pub fn default_template(self) -> ProjectTemplate {
        ProjectTemplate::Api
    }

    /// Recommended features that should be enabled for this preset.
    pub fn recommended_features(self) -> Vec<String> {
        match self {
            ProjectPreset::Production => vec![
                "extras-config",
                "extras-cors",
                "extras-rate-limit",
                "extras-security-headers",
                "extras-structured-logging",
                "extras-timeout",
            ],
            ProjectPreset::Ai => vec![
                "extras-config",
                "extras-structured-logging",
                "extras-timeout",
                "protocol-toon",
            ],
            ProjectPreset::Realtime => vec![
                "extras-cors",
                "extras-structured-logging",
                "extras-timeout",
                "protocol-ws",
            ],
        }
        .into_iter()
        .map(str::to_string)
        .collect()
    }
}

/// Generate a project from a template
pub async fn generate_project(
    name: &str,
    template: ProjectTemplate,
    features: &[String],
) -> Result<()> {
    match template {
        ProjectTemplate::Minimal => minimal::generate(name, features).await,
        ProjectTemplate::Api => api::generate(name, features).await,
        ProjectTemplate::Web => web::generate(name, features).await,
        ProjectTemplate::Full => full::generate(name, features).await,
    }
}

/// Common files for all templates
pub mod common {
    use anyhow::Result;
    use tokio::fs;

    pub async fn generate_gitignore(path: &str) -> Result<()> {
        let content = r#"# Generated by Cargo
/target/

# IDE
.idea/
.vscode/
*.swp
*.swo

# Environment
.env
.env.local
.env.*.local

# OS
.DS_Store
Thumbs.db

# Logs
*.log
"#;
        fs::write(format!("{path}/.gitignore"), content).await?;
        Ok(())
    }

    pub async fn generate_env_example(path: &str) -> Result<()> {
        let content = r#"# Server configuration
HOST=127.0.0.1
PORT=8080

# Environment (development, production)
RUSTAPI_ENV=development

# Database (if using sqlx)
# DATABASE_URL=postgres://user:pass@localhost/db

# JWT Secret (if using extras-jwt feature)
# JWT_SECRET=your-secret-key-here

# Logging
RUST_LOG=info
"#;
        fs::write(format!("{path}/.env.example"), content).await?;
        Ok(())
    }

    pub fn features_to_cargo(features: &[String]) -> String {
        if features.is_empty() {
            String::new()
        } else {
            format!(
                ", features = [{}]",
                features
                    .iter()
                    .map(|f| format!("\"{}\"", f))
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}