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        ensure_session().await;
38    }
39
40    match command.as_str() {
41        "migrate" | "migrate:refresh" | "migrate:back" | "migrate:rollback" => {
42             match migrate_fn(command.clone()).await {
43                Ok(_) => println!("\n{} Operasi '{}' berhasil.", "āœ…".green(), command),
44                Err(e) => eprintln!("\n{} Gagal: {}", "āŒ".red(), e),
45             }
46        }
47        "db:seed" => {
48            println!("{}", "🌱 Menjalankan seeder database...".cyan());
49            seed_fn().await;
50            println!("\n{} Database seeding berhasil.", "āœ…".green());
51        }
52        _ => {
53            // Perintah lainnya ditangani oleh main.rs global atau binari ini jika dipanggil langsung
54            // Tapi biasanya binari lokal hanya dipanggil untuk migrate/seed
55        }
56    }
57}
58
59pub fn print_help() {
60    println!("\n{}", "šŸ› ļø  RustBasic CLI".magenta().bold());
61    println!("{}", "=================".magenta());
62    println!("{}", "Penggunaan:".bold());
63    println!("  {} {} <Nama>         {}", "rustbasic".blue(), "new".green(), "Membuat project RustBasic baru".dimmed());
64    println!("  {} {} <Nama>   {}", "rustbasic".blue(), "make:controller".green(), "Membuat controller baru".dimmed());
65    println!("  {} {} <Nama> [-m]   {}", "rustbasic".blue(), "make:model".green(), "Membuat model & migration".dimmed());
66    println!("  {} {} <Nama>    {}", "rustbasic".blue(), "make:migration".green(), "Membuat file migrasi (Create)".dimmed());
67    println!("  {} {} <Kolom> <Tabel> {}", "rustbasic".blue(), "make:migration:add".green(), "Membuat file migrasi (Add Column)".dimmed());
68    println!("  {} {} <Nama>     {}", "rustbasic".blue(), "make:middleware".green(), "Membuat middleware baru".dimmed());
69    println!("  {} {} <Nama>       {}", "rustbasic".blue(), "make:seeder".green(), "Membuat seeder baru".dimmed());
70    println!("  {} {}                  {}", "rustbasic".blue(), "migrate".green(), "Menjalankan migrasi database".dimmed());
71    println!("  {} {}                {}", "rustbasic".blue(), "storage:link".green(), "Menghubungkan public/storage ke storage/app/public".dimmed());
72    println!("  {} {}                   {}", "rustbasic".blue(), "serve".green(), "Menjalankan server (Auto-Reload)".dimmed());
73    println!("  {} {}                 {}", "rustbasic".blue(), "version".green(), "Menampilkan versi CLI".dimmed());
74    println!("\nšŸ’” Gunakan 'rustbasic version' untuk informasi lebih lanjut.");
75}
76
77/// Fungsi utama untuk menangani CLI di tingkat project (dipanggil oleh project main.rs)
78pub async fn handle<M: sea_orm_migration::MigratorTrait>(cfg: &rustbasic_core::Config, args: &[String]) -> bool {
79    if args.len() < 2 {
80        return false;
81    }
82
83    let command = args[1].as_str();
84    
85    // Daftar perintah yang ditangani oleh CLI lokal project
86    let is_migration_cmd = command.starts_with("migrate") || command == "db:seed";
87    let is_storage_cmd = command == "storage:link";
88    
89    if !is_migration_cmd && !is_storage_cmd {
90        return false;
91    }
92
93    ensure_session().await;
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}