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!("Gunakan 'rustbasic' untuk melihat opsi perintah.");
61}
62
63/// Fungsi utama untuk menangani CLI di tingkat project (dipanggil oleh project main.rs)
64pub async fn handle<M: sea_orm_migration::MigratorTrait>(args: &[String]) -> bool {
65    if args.len() < 2 {
66        return false;
67    }
68
69    let command = args[1].as_str();
70    
71    // Daftar perintah yang ditangani oleh CLI lokal project
72    let is_migration_cmd = command.starts_with("migrate") || command == "db:seed";
73    let is_storage_cmd = command == "storage:link";
74    
75    if !is_migration_cmd && !is_storage_cmd {
76        return false;
77    }
78
79    ensure_session().await;
80
81    println!("{} {}", "đŸ› ī¸  RustBasic Local CLI - Command:".magenta().bold(), command.yellow());
82
83    if is_storage_cmd {
84        handle_storage_link();
85        return true;
86    }
87
88    // Gunakan fungsi connect lokal
89    let db = crate::database::connect().await;
90
91    match command {
92        "migrate" => {
93            println!("🚀 {}", "Menjalankan migrasi database...".cyan());
94            if let Err(e) = M::up(&db, None).await {
95                println!("❌ {} {}", "Gagal menjalankan migrasi:".red().bold(), e);
96            } else {
97                println!("✅ {}", "Migrasi selesai!".green().bold());
98            }
99        }
100        "migrate:refresh" => {
101            println!("🔄 {}", "Mereset dan menjalankan ulang migrasi...".cyan());
102            if let Err(e) = M::fresh(&db).await {
103                println!("❌ {} {}", "Gagal refresh migrasi:".red().bold(), e);
104            } else {
105                println!("✅ {}", "Database berhasil di-refresh!".green().bold());
106            }
107        }
108        "migrate:back" | "migrate:rollback" => {
109            println!("âŦ…ī¸  {}", "Rollback migrasi terakhir...".cyan());
110            if let Err(e) = M::down(&db, None).await {
111                println!("❌ {} {}", "Gagal rollback:".red().bold(), e);
112            } else {
113                println!("✅ {}", "Rollback berhasil!".green().bold());
114            }
115        }
116        "db:seed" => {
117            println!("🌱 {}", "Fitur db:seed membutuhkan implementasi lokal.".yellow());
118        }
119        _ => return false,
120    }
121
122    true
123}
124
125/// Membuat symbolic link dari public/storage ke storage/app/public
126fn handle_storage_link() {
127    let target = "public/storage";
128    let source = "storage/app/public";
129
130    // 1. Buat folder source jika belum ada
131    if let Err(e) = std::fs::create_dir_all(source) {
132        println!("❌ {} {}", "Gagal membuat direktori storage:".red().bold(), e);
133        return;
134    }
135
136    // 2. Cek apakah link sudah ada
137    let path = std::path::Path::new(target);
138    if path.exists() || path.is_symlink() {
139        println!("â„šī¸  {}", "Link 'public/storage' sudah ada atau berupa file/folder lain.".yellow());
140        return;
141    }
142
143    // 3. Buat symlink
144    println!("🔗 {}", "Membuat symbolic link...".cyan());
145
146    #[cfg(unix)]
147    {
148        use std::os::unix::fs::symlink;
149        // Gunakan path relatif agar tetap valid jika project dipindah
150        if let Err(e) = symlink("../storage/app/public", target) {
151            println!("❌ {} {}", "Gagal membuat symlink:".red().bold(), e);
152        } else {
153            println!("✅ {} [public/storage -> storage/app/public]", "Link storage berhasil dibuat!".green().bold());
154        }
155    }
156
157    #[cfg(windows)]
158    {
159        use std::os::windows::fs::symlink_dir;
160        if let Err(e) = symlink_dir("../storage/app/public", target) {
161            println!("❌ {} {}", "Gagal membuat symlink:".red().bold(), e);
162        } else {
163            println!("✅ {} [public/storage -> storage/app/public]", "Link storage berhasil dibuat!".green().bold());
164        }
165    }
166}