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