Skip to main content

rustbasic_cli/
lib.rs

1pub mod scaffolding;
2pub mod database;
3pub mod monitoring;
4pub mod builder;
5pub mod utils;
6pub use scaffolding::*;
7pub use database::*;
8pub use monitoring::*;
9pub use builder::*;
10pub use utils::*;
11
12use std::env;
13use dotenvy::dotenv;
14use colored::*;
15use std::future::Future;
16use std::pin::Pin;
17
18pub type AsyncHook = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
19
20pub async fn run_cli<F, G>(migrate_fn: F, seed_fn: G) 
21where 
22    F: Fn(String) -> Pin<Box<dyn Future<Output = Result<(), String>>>>,
23    G: Fn() -> Pin<Box<dyn Future<Output = ()>>>
24{
25    let args: Vec<String> = env::args().collect();
26
27    if args.len() < 2 {
28        print_help();
29        return;
30    }
31
32    let command = &args[1];
33
34    // .env hanya diwajibkan untuk perintah selain 'new'
35    if command != "new" {
36        let _ = dotenv();
37    }
38
39    match command.as_str() {
40        "migrate" | "migrate:refresh" | "migrate:back" | "migrate:rollback" => {
41             match migrate_fn(command.clone()).await {
42                Ok(_) => println!("\n{} Operasi '{}' berhasil.", "āœ…".green(), command),
43                Err(e) => eprintln!("\n{} Gagal: {}", "āŒ".red(), e),
44             }
45        }
46        "db:seed" => {
47            println!("{}", "🌱 Menjalankan seeder database...".cyan());
48            seed_fn().await;
49            println!("\n{} Database seeding berhasil.", "āœ…".green());
50        }
51        _ => {
52            // Perintah lainnya ditangani oleh main.rs global atau binari ini jika dipanggil langsung
53            // Tapi biasanya binari lokal hanya dipanggil untuk migrate/seed
54        }
55    }
56}
57
58pub fn print_help() {
59    println!("\n{}", "šŸ› ļø  RustBasic CLI".magenta().bold());
60    println!("{}", "=================".magenta());
61    println!("{}", "Penggunaan:".bold());
62    println!("  {} {} <Nama>         {}", "rustbasic".blue(), "new".green(), "Membuat project RustBasic baru".dimmed());
63    println!("  {} {} <Nama>   {}", "rustbasic".blue(), "make:controller".green(), "Membuat controller baru".dimmed());
64    println!("  {} {} <Nama> [-m]   {}", "rustbasic".blue(), "make:model".green(), "Membuat model & migration".dimmed());
65    println!("  {} {} <Nama>    {}", "rustbasic".blue(), "make:migration".green(), "Membuat file migrasi (Create)".dimmed());
66    println!("  {} {} <Kolom> <Tabel> {}", "rustbasic".blue(), "make:migration:add".green(), "Membuat file migrasi (Add Column)".dimmed());
67    println!("  {} {} <Nama>     {}", "rustbasic".blue(), "make:middleware".green(), "Membuat middleware baru".dimmed());
68    println!("  {} {} <Nama>       {}", "rustbasic".blue(), "make:seeder".green(), "Membuat seeder baru".dimmed());
69    println!("  {} {}                  {}", "rustbasic".blue(), "migrate".green(), "Menjalankan migrasi database".dimmed());
70    println!("  {} {}                {}", "rustbasic".blue(), "storage:link".green(), "Menghubungkan public/storage ke storage/app/public".dimmed());
71    println!("  {} {}                   {}", "rustbasic".blue(), "serve".green(), "Menjalankan server (Auto-Reload)".dimmed());
72    println!("  {} {}                 {}", "rustbasic".blue(), "version".green(), "Menampilkan versi CLI".dimmed());
73    println!("\nšŸ’” Gunakan 'rustbasic version' untuk informasi lebih lanjut.");
74}
75
76/// Fungsi utama untuk menangani CLI di tingkat project (dipanggil oleh project main.rs)
77pub async fn handle<M: sea_orm_migration::MigratorTrait>(cfg: &rustbasic_core::Config, args: &[String]) -> bool {
78    if args.len() < 2 {
79        return false;
80    }
81
82    let command = args[1].as_str();
83    
84    // Daftar perintah yang ditangani oleh CLI lokal project
85    let is_migration_cmd = command.starts_with("migrate") || command == "db:seed";
86    let is_storage_cmd = command == "storage:link";
87    
88    if !is_migration_cmd && !is_storage_cmd {
89        return false;
90    }
91
92    println!("{} {}", "šŸ› ļø  RustBasic Local CLI - Command:".magenta().bold(), command.yellow());
93
94    if is_storage_cmd {
95        handle_storage_link();
96        return true;
97    }
98
99    // Gunakan fungsi connect dari core agar logic koneksi konsisten
100    let db = rustbasic_core::database::connect(cfg).await;
101
102    match command {
103        "migrate" => {
104            println!("šŸš€ {}", "Menjalankan migrasi database...".cyan());
105            if let Err(e) = M::up(&db, None).await {
106                println!("āŒ {} {}", "Gagal menjalankan migrasi:".red().bold(), e);
107            } else {
108                println!("āœ… {}", "Migrasi selesai!".green().bold());
109            }
110        }
111        "migrate:refresh" => {
112            println!("šŸ”„ {}", "Mereset dan menjalankan ulang migrasi...".cyan());
113            if let Err(e) = M::fresh(&db).await {
114                println!("āŒ {} {}", "Gagal refresh migrasi:".red().bold(), e);
115            } else {
116                println!("āœ… {}", "Database berhasil di-refresh!".green().bold());
117            }
118        }
119        "migrate:back" | "migrate:rollback" => {
120            println!("ā¬…ļø  {}", "Rollback migrasi terakhir...".cyan());
121            if let Err(e) = M::down(&db, None).await {
122                println!("āŒ {} {}", "Gagal rollback:".red().bold(), e);
123            } else {
124                println!("āœ… {}", "Rollback berhasil!".green().bold());
125            }
126        }
127        "db:seed" => {
128            println!("🌱 {}", "Fitur db:seed membutuhkan implementasi lokal.".yellow());
129        }
130        _ => return false,
131    }
132
133    true
134}
135
136/// Membuat symbolic link dari public/storage ke storage/app/public
137fn handle_storage_link() {
138    let target = "public/storage";
139    let source = "storage/app/public";
140
141    // 1. Buat folder source jika belum ada
142    if let Err(e) = std::fs::create_dir_all(source) {
143        println!("āŒ {} {}", "Gagal membuat direktori storage:".red().bold(), e);
144        return;
145    }
146
147    // 2. Cek apakah link sudah ada
148    let path = std::path::Path::new(target);
149    if path.exists() || path.is_symlink() {
150        println!("ā„¹ļø  {}", "Link 'public/storage' sudah ada atau berupa file/folder lain.".yellow());
151        return;
152    }
153
154    // 3. Buat symlink
155    println!("šŸ”— {}", "Membuat symbolic link...".cyan());
156
157    #[cfg(unix)]
158    {
159        use std::os::unix::fs::symlink;
160        // Gunakan path relatif agar tetap valid jika project dipindah
161        if let Err(e) = symlink("../storage/app/public", target) {
162            println!("āŒ {} {}", "Gagal membuat symlink:".red().bold(), e);
163        } else {
164            println!("āœ… {} [public/storage -> storage/app/public]", "Link storage berhasil dibuat!".green().bold());
165        }
166    }
167
168    #[cfg(windows)]
169    {
170        use std::os::windows::fs::symlink_dir;
171        if let Err(e) = symlink_dir("../storage/app/public", target) {
172            println!("āŒ {} {}", "Gagal membuat symlink:".red().bold(), e);
173        } else {
174            println!("āœ… {} [public/storage -> storage/app/public]", "Link storage berhasil dibuat!".green().bold());
175        }
176    }
177}