fluxor_cli 1.1.2

Fluxor CLI: a command-line tool that allows developers to quickly and efficiently create project starters for the Fluxor web framework.
Documentation
#![doc(html_logo_url = "https://github.com/dr-montasir/fluxor_cli/raw/HEAD/fluxor-icon-64x64.svg")]
#![doc = r"<div align='center'><a href='https://github.com/dr-montasir/fluxor_cli' target='_blank'><img src='https://github.com/dr-montasir/fluxor_cli/raw/HEAD/fluxor-icon-64x64.svg' alt='Fluxor CLI' width='80' height='auto' /></a><br><br><a href='https://github.com/dr-montasir/fluxor_cli' target='_blank'>FLUXOR</a><br><br>Fluxor_cli is the command-line interface for the Fluxor web framework, enabling rapid project scaffolding and management for Rust applications focused on data science and computing.</div>"]

pub mod utils;
pub mod metadata;
mod examples;

pub use clap::Parser;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::process::Command as ProcessCommand;

use utils::*;
use examples::*;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Args {
    #[clap(subcommand)]
    pub command: Commands,
}

#[derive(Parser, Debug)]
pub enum Commands {
    New {
        name: String,
        #[clap(short, long, default_value = "latest")]
        version: String,
        #[clap(short, long, default_value = "helloworld")]
        example: String,
    },
}

pub fn fetch_latest_version(crate_name: &str) -> Result<String, Box<dyn std::error::Error>> {
    let output = ProcessCommand::new("cargo")
        .arg("search")
        .arg(crate_name)
        .output()?;

    if !output.status.success() {
        return Err(format!(
            "Failed to fetch latest version of {}: {}",
            crate_name,
            String::from_utf8_lossy(&output.stderr)
        )
        .into());
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    println!("Cargo search output: {}", stdout); // Debug print

    // Use a regex to extract the version number
    let re = Regex::new(r#"(\d+\.\d+\.\d+)"#).unwrap();
    let version = stdout
        .lines()
        .find(|line| line.contains(crate_name))
        .and_then(|line| re.find(line))
        .map(|version| version.as_str())
        .ok_or_else(|| format!("Crate '{}' not found", crate_name))?;

    println!("Parsed version: {}", version); // Debug print

    Ok(version.to_string())
}

// Fetch dependencies crate version if "latest" is specified
pub fn get_crate_version(version: &str, dep_crate_name: &str) -> String {
    if version == "latest" {
        fetch_latest_version(dep_crate_name).expect(&format!("Failed to fetch latest version of {}", dep_crate_name))
    } else {
        version.to_string()
    }
}

pub fn create_fluxor_web_project(name: &str, version: &str, example: &str) {
    let package_name = to_crate_name(name);
    let project_path = Path::new(&package_name);

    // Check if project directory already exists
    if project_path.exists() {
        eprintln!("Project directory already exists.");
        return;
    }

    // Create project directory
    fs::create_dir_all(&project_path).expect("Failed to create project directory");

    // Fetch fluxor version if "latest" is specified
    let fluxor_version = if version == "latest" {
        fetch_latest_version("fluxor").expect("Failed to fetch latest version of fluxor")
    } else {
        version.to_string()
    };

    // create README.md
    metadata::create_readme(project_path, &package_name);

    // Create Cargo.toml specific for the example
    let cargo_toml = match example {
        // Hello World Examples
        "helloworld" => hello_world::hello_world_cargo_toml(&package_name, &fluxor_version),
        "helloworld-api" => hello_world::hello_world_cargo_toml(&package_name, &fluxor_version),
        "helloworld-api-server" => hello_world::hello_world_api_server_cargo_toml(&package_name, &fluxor_version),
        // Routes Examples
        "routes" => routes::routes_cargo_toml(&package_name, &fluxor_version),
        "routes-project" => routes::routes_cargo_toml(&package_name, &fluxor_version),
        // Assets Examples
        "assets" => assets::assets_cargo_toml(&package_name, &fluxor_version),
        // DotEnv Examples
        "dotenv" => dotenv::dotenv_cargo_toml(&package_name, &fluxor_version),
        "cans-template-engine" => cans::template_cargo_toml(&package_name, &fluxor_version),
        // DB
        "db-redis" => db::db_cargo_toml(&package_name, &fluxor_version),
        // Fluxor Full Template Example
        "fluxor-template" => fluxor::fluxor_template_cargo_toml(
            &package_name, 
            &get_crate_version("latest", "crator"), 
            &get_crate_version("latest", "fluxor")
        ),
        _ => {
            eprintln!("Unknown example specified: {}", example);
            return;
        }
    };

    fs::write(project_path.join("Cargo.toml"), cargo_toml)
        .expect("Failed to create Cargo.toml");

    // Create src directory under the project
    let src_path = project_path.join("src");
    fs::create_dir_all(&src_path).expect("Failed to create src directory");

    // Create src folders and files based on the specified example
    match example {
        // Hello World Examples
        "helloworld" => {
            // metadata files
            hello_world::config_metadata(&project_path);

            // src/main.rs
            hello_world::hello_world_main_rs(&src_path);
        }
        "helloworld-api" => {
            // metadata files
            hello_world::config_metadata(&project_path);

            // src/main.rs
            hello_world::hello_world_api_main_rs(&src_path);
        }
        "helloworld-api-server" => {
            // metadata files
            hello_world::config_metadata(&project_path);

            // src/server.rs
            hello_world::hello_world_api_server_rs(&src_path);
        }
        // Routes Examples
        "routes" => {
            // metadata files
            routes::config_metadata(&project_path);

            // src/main.rs
            routes::routes_main_rs(&src_path);
        }
        "routes-project" => {
            // metadata files
            routes::config_metadata(&project_path);

            // src
            // src/main.rs
            routes::routes_project_main_rs(&package_name, &src_path);
            // src/lib.rs
            routes::routes_project_lib_rs(&src_path);

            // src/routes
            // Create routes directory under the src folder
            let src_routes_path = project_path.join("src/routes");
            fs::create_dir_all(&src_routes_path).expect("Failed to create src/routes directory");
            // src/routes/mod.rs
            routes::routes_project_routes_mod_rs(&src_routes_path);
            
            // src/routes/api
            // Create routes/api directory under the src folder
            let src_routes_api_path = project_path.join("src/routes/api");
            fs::create_dir_all(&src_routes_api_path).expect("Failed to create src/routes/api directory");
            // src/routes/api/mod.rs
            routes::routes_project_routes_api_mod_rs(&src_routes_api_path);
            // src/routes/api/msg.rs
            routes::routes_project_routes_api_msg_rs(&src_routes_api_path);
            // src/routes/api/dynamic_route.rs
            routes::routes_project_routes_api_dynamic_route_rs(&src_routes_api_path);

            // src/routes/pages
            // Create routes/pages directory under the src folder
            let src_routes_pages_path = project_path.join("src/routes/pages");
            fs::create_dir_all(&src_routes_pages_path).expect("Failed to create src/routes/pages directory");
            // src/routes/pages/mod.rs
            routes::routes_project_routes_pages_mod_rs(&src_routes_pages_path);
            // src/routes/pages/home.rs
            routes::routes_project_routes_pages_home_rs(&src_routes_pages_path);
            // src/routes/pages/about.rs
            routes::routes_project_routes_pages_about_rs(&src_routes_pages_path);
        }
        // Assets Examples
        "assets" => {
            // metadata files
            assets::config_metadata(&project_path);

            // main.rs
            assets::assets_main_rs(&src_path);

            // assets
            let assets_img_path = project_path.join("assets/img");
            let assets_css_path = project_path.join("assets/css");
            let assets_js_path = project_path.join("assets/js");
            
            fs::create_dir_all(&assets_img_path).expect("Failed to create sassets/img directory");
            fs::create_dir_all(&assets_css_path).expect("Failed to create sassets/css directory");
            fs::create_dir_all(&assets_js_path).expect("Failed to create sassets/js directory");
            
            assets::assets_img_fluxor_svg(&assets_img_path);
            assets::assets_css_styels_css(&assets_css_path);
            assets::assets_js_script_js(&assets_js_path);
        }
        // DotEnv Examples
        "dotenv" => {
            // metadata files
            dotenv::config_metadata(&project_path);

            // main.rs
            dotenv::dotenv_main_rs(&src_path);
        }
        // Cans:
        // Template Examples
        "cans-template-engine" => {
            // metadata files
            cans::config_metadata(&project_path);

            // main.rs
            cans::template_main_rs(&src_path);
        }
        // DB
        // REDIS DB
        "db-redis" => {
            // metadata files
            db::config_redis_metadata(&project_path);

            // main.rs
            db::db_redis_main_rs(&src_path);
        }
        // Fluxor
        // Full Fluxor Template Example
        "fluxor-template" => {
            // paths
            let assets_path = project_path.join("src/assets");
            let assets_images_path = project_path.join("src/assets/images");
            let assets_css_path = project_path.join("src/assets/css");
            let assets_js_path = project_path.join("src/assets/js");
            let components_path = project_path.join("src/components");
            let components_scripts_path = project_path.join("src/components/scripts");
            let db_path = project_path.join("src/db");
            let ds_path = project_path.join("src/ds");
            let helpers_path = project_path.join("src/helpers");
            let routes_path = project_path.join("src/routes");
            let routes_api_path = project_path.join("src/routes/api");
            let routes_api_greeting_path = project_path.join("src/routes/api/greeting");
            let routes_pages_path = project_path.join("src/routes/pages");
            let validators_path = project_path.join("src/validators");

            // metadata files
            fluxor::config_metadata(&project_path);

            // src

            // src/main.rs
            fluxor::fluxor_template_main_rs(&package_name, &src_path);
            // src/lib.rs
            fluxor::fluxor_template_lib_rs(&src_path);

            // src/assets

            // src/assets/images
            fs::create_dir_all(assets_path.join("images")).expect("Failed to create assets/images directory");
            // src/assets/css
            fs::create_dir_all(assets_path.join("css")).expect("Failed to create assets/css directory");
            // src/assets/js
            fs::create_dir_all(assets_path.join("js")).expect("Failed to create assets/js directory");

            // src/assets/manifest.json
            fluxor::assets_manifest_json(&assets_path);
            // src/assets/service-worker.js
            fluxor::assets_service_worker_js(&assets_path);
            // src/assets/images/logo.svg
            fluxor::assets_images_logo_svg(&assets_images_path);
            // src/assets/css/styles.css
            fluxor::assets_css_styles_css(&assets_css_path);
            // src/assets/js/alpine.min.js
            fluxor::assets_js_alpine_min_js(&assets_js_path);

            // src/components

            // src/components/scripts
            fs::create_dir_all(components_path.join("scripts")).expect("Failed to create components/scripts directory");

            // src/components/scripts/mod.rs
            fluxor::components_scripts_mod_rs(&components_scripts_path);
            // src/components/scripts/sw_register.rs
            fluxor::components_scripts_sw_register_rs(&components_scripts_path);
            // src/components/badge.rs
            fluxor::components_badge_rs(&components_path);
            // src/components/footer.rs
            fluxor::components_footer_rs(&components_path);
            // src/components/head.rs
            fluxor::components_head_rs(&components_path);
            // src/components/header.rs
            fluxor::components_header_rs(&components_path);
            // src/components/layout.rs
            fluxor::components_layout_rs(&components_path);
            // src/components/logo.rs
            fluxor::components_logo_rs(&components_path);
            // src/components/mod.rs
            fluxor::components_mod_rs(&components_path);
            // src/components/nav.rs
            fluxor::components_nav_rs(&components_path);
            // src/components/notfound.rs
            fluxor::components_notfound_rs(&components_path);

            // src/db

            fs::create_dir_all(&db_path).expect("Failed to create src/db directory");

            // src/db/mod.rs
            fluxor::db_mod_rs(&db_path);

            // src/ds

            fs::create_dir_all(&ds_path).expect("Failed to create src/ds directory");

            // src/ds/mod.rs
            fluxor::ds_mod_rs(&ds_path);

            // src/helpers

            fs::create_dir_all(&helpers_path).expect("Failed to create src/helpers directory");

            // src/helpers/mod.rs
            fluxor::helpers_mod_rs(&helpers_path);

            // src/routes

            fs::create_dir_all(&routes_path).expect("Failed to create src/routes directory");
            
            // src/routes/pages
            fs::create_dir_all(routes_path.join("pages")).expect("Failed to create routes/pages directory");
            // src/routes/api
            fs::create_dir_all(routes_path.join("api")).expect("Failed to create routes/api directory");
            // src/routes/api/greeting
            fs::create_dir_all(routes_path.join("api/greeting")).expect("Failed to create routes/api/greeting directory");

            // src/routes/mod.rs
            fluxor::routes_mod_rs(&routes_path);
            // src/routes/api/mod.rs
            fluxor::routes_api_mod_rs(&routes_api_path);
            // src/routes/api/greeting/mod.rs
            fluxor::routes_api_greeting_mod_rs(&routes_api_greeting_path);
            // src/routes/pages/mod.rs
            fluxor::routes_pages_mod_rs(&routes_pages_path);
            // src/routes/pages/home.rs
            fluxor::routes_pages_home_rs(&routes_pages_path);
            // src/routes/pages/analytics.rs
            fluxor::routes_pages_analytics_rs(&routes_pages_path);
            
            // src/validators

            fs::create_dir_all(&validators_path).expect("Failed to create src/validators directory");

            // src/validators/mod.rs
            fluxor::validators_mod_rs(&validators_path);
        }
        _ => {
            eprintln!("Unknown example specified: {}", example);
            return;
        }
    }

    println!("Fluxor project '{}' created successfully using the '{}' example.", package_name, example);
}