Skip to main content

rustbasic_cli/
lib.rs

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