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 mod publisher;
8pub use scaffolding::*;
9pub use database::*;
10pub use monitoring::*;
11pub use builder::*;
12pub use utils::*;
13
14use std::env;
15use rustbasic_core::dotenvy::dotenv;
16use rustbasic_core::colored::*;
17use std::future::Future;
18use std::pin::Pin;
19
20pub type AsyncHook = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
21
22pub async fn run_cli<F, G>(migrate_fn: F, seed_fn: G) 
23where 
24    F: Fn(String) -> Pin<Box<dyn Future<Output = Result<(), String>>>>,
25    G: Fn() -> Pin<Box<dyn Future<Output = ()>>>
26{
27    let args: Vec<String> = env::args().collect();
28
29    if args.len() < 2 {
30        print_help();
31        return;
32    }
33
34    let command = &args[1];
35
36    // .env hanya diwajibkan untuk perintah selain 'new'
37    if command != "new" {
38        let _ = dotenv();
39        ensure_session().await;
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!("Gunakan 'rustbasic' untuk melihat opsi perintah.");
63}
64
65/// Fungsi utama untuk menangani CLI di tingkat project (dipanggil oleh project main.rs)
66pub async fn handle<M: rustbasic_core::MigratorTrait + Send + Sync + 'static>(args: &[String]) -> bool {
67    if args.len() < 2 {
68        return false;
69    }
70
71    let command = args[1].as_str();
72    
73    // Daftar perintah yang ditangani oleh CLI lokal project
74    let is_migration_cmd = command.starts_with("migrate") || command == "db:seed";
75    let is_storage_cmd = command == "storage:link";
76    let is_build_cmd = command == "build";
77    let is_server_cmd = command == "server" || command == "serve";
78
79    // Server --android / --desktop
80    if is_server_cmd {
81        let run_android = args.iter().any(|arg| arg == "--android");
82        let run_desktop = args.iter().any(|arg| arg == "--desktop");
83        if run_android || run_desktop {
84            builder::run_native(run_android, run_desktop);
85            return true;
86        }
87        return false; // Fall through ke standard web server
88    }
89
90    if !is_migration_cmd && !is_storage_cmd && !is_build_cmd {
91        return false;
92    }
93
94    // Skip logger banner for non-server commands
95    println!("đŸ› ī¸  RustBasic Local CLI - Command: {}", command);
96
97    if is_build_cmd {
98        builder::build_interactive(args);
99        return true;
100    }
101
102    if is_storage_cmd {
103        handle_storage_link();
104        return true;
105    }
106
107    ensure_session().await;
108
109    // Hubungkan ke database
110    let db = crate::database::connect().await;
111
112    match command {
113        "migrate" => {
114            println!("🚀 {}", "Menjalankan migrasi database...".cyan());
115            if let Err(e) = M::up(&db, None).await {
116                println!("❌ {} {}", "Gagal menjalankan migrasi:".red().bold(), e);
117            } else {
118                println!("✅ {}", "Migrasi selesai!".green().bold());
119            }
120        }
121        "migrate:refresh" => {
122            println!("🔄 {}", "Mereset dan menjalankan ulang migrasi...".cyan());
123            if let Err(e) = M::fresh(&db).await {
124                println!("❌ {} {}", "Gagal refresh migrasi:".red().bold(), e);
125            } else {
126                println!("✅ {}", "Database berhasil di-refresh!".green().bold());
127            }
128        }
129        "migrate:back" | "migrate:rollback" => {
130            println!("âŦ…ī¸  {}", "Rollback migrasi terakhir...".cyan());
131            if let Err(e) = M::down(&db, None).await {
132                println!("❌ {} {}", "Gagal rollback:".red().bold(), e);
133            } else {
134                println!("✅ {}", "Rollback berhasil!".green().bold());
135            }
136        }
137        "db:seed" => {
138            println!("🌱 {}", "Fitur db:seed membutuhkan implementasi lokal.".yellow());
139        }
140        _ => return false,
141    }
142
143    true
144}
145
146/// Membuat symbolic link dari public/storage ke storage/app/public
147fn handle_storage_link() {
148    let target = "public/storage";
149    let source = "storage/app/public";
150
151    // 1. Buat folder source jika belum ada
152    if let Err(e) = std::fs::create_dir_all(source) {
153        println!("❌ {} {}", "Gagal membuat direktori storage:".red().bold(), e);
154        return;
155    }
156
157    // 2. Cek apakah link sudah ada
158    let path = std::path::Path::new(target);
159    if path.exists() || path.is_symlink() {
160        println!("â„šī¸  {}", "Link 'public/storage' sudah ada atau berupa file/folder lain.".yellow());
161        return;
162    }
163
164    // 3. Buat symlink
165    println!("🔗 {}", "Membuat symbolic link...".cyan());
166
167    #[cfg(unix)]
168    {
169        use std::os::unix::fs::symlink;
170        // Gunakan path relatif agar tetap valid jika project dipindah
171        if let Err(e) = symlink("../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
178    #[cfg(windows)]
179    {
180        use std::os::windows::fs::symlink_dir;
181        if let Err(e) = symlink_dir("../storage/app/public", target) {
182            println!("❌ {} {}", "Gagal membuat symlink:".red().bold(), e);
183        } else {
184            println!("✅ {} [public/storage -> storage/app/public]", "Link storage berhasil dibuat!".green().bold());
185        }
186    }
187}