my_libs/utils.rs
1// src/utils.rs
2
3use serde::{Serialize, de::DeserializeOwned};
4use std::fs;
5use std::io::{self, Write};
6use std::path::Path;
7use std::process::Command;
8
9// 📚 EDU (Generics):
10// <T> - To oznacza "Dla dowolnego typu T" (jak w TypeScript).
11// W Rust `where` pozwala na dodatkowe ograniczenia typów.
12// - Serialize = Typ T musi umieć się zamienić na tekst.
13// - DeserializeOwned = Typ T musi umieć się stworzyć z tekstu
14// - Default = Typ T musi mieć domyślną wartość (np. pusty obiekt).
15
16/// 📥 Generyczna funkcja do ładowania dowolnego pliku TOML.
17/// Odpowiednik TS: function loadToml<T>(path: string): T
18pub fn load_toml<T>(path: &Path) -> T
19where
20 T: DeserializeOwned + Default,
21{
22 if path.exists() {
23 let content = fs::read_to_string(path).unwrap_or_default();
24 toml::from_str(&content).unwrap_or_default()
25 } else {
26 T::default()
27 }
28}
29
30/// 💾 Generyczna funkcja do zapisywania dowolnego obiektu do TOML.
31/// Automatycznie tworzy foldery, jeśli ich brakuje!
32pub fn save_toml<T>(path: &Path, data: &T)
33where
34 T: Serialize,
35{
36 // 1. Jeśli plik ma być w folderze (np. config/x.toml), upewnij się, że folder istnieje.
37 if let Some(parent) = path.parent() {
38 fs::create_dir_all(parent).expect("Nie udało się utworzyć folderu");
39 }
40
41 // 2. Serializacja i zapis
42 let content = toml::to_string_pretty(data).expect("Błąd formatowania TOML");
43 fs::write(path, content).expect("Błąd zapisu pliku");
44}
45
46/// 🚀 Uruchamia nową instancję programu (binarkę) w nowym oknie terminala.
47/// SRP: Ta funkcja martwi się o to JAK otworzyć okno, a nie KIEDY.
48/*pub fn open_terminal_window(bin_name: &str) {
49 #[cfg(target_os = "windows")]
50 {
51 Command::new("cmd")
52 .args([
53 "/C", // Zamknij proces cmd uruchamiający, ale zostaw okno
54 "start", // Polecenie Windows "start" otwiera nowe okno
55 "cargo", "run", "--bin", bin_name,
56 ])
57 .spawn()
58 .expect("❌ Fail Fast: Nie udało się uruchomić procesu potomnego!")
59 .wait() // 👈 DODANO: Czekamy aż 'cmd' skończy odpalać okno (trwa to milisekundy)
60 .expect("Błąd oczekiwania na proces");
61 }
62
63 #[cfg(not(target_os = "windows"))]
64 {
65 println!(
66 "⚠️ Fail Fast: Twój system nie wspiera automatycznego otwierania okien w tym kodzie."
67 );
68 }
69}
70*/
71pub fn open_terminal_window(bin_name: &str) {
72 #[cfg(target_os = "windows")]
73 {
74 // Budujemy komendę PowerShell.
75 // 1. "pwsh" -> Uruchamiamy proces-matkę.
76 // 2. "-Command" -> Każe mu wykonać polecenie Start-Process.
77 // 3. "Start-Process pwsh" -> Otwiera NOWE okno z PowerShellem.
78 // 4. "-ArgumentList" -> Przekazuje parametry do tego nowego okna.
79 // 5. "-NoExit" -> KLUCZOWE: Nie zamykaj okna po wykonaniu (odpowiednik cmd /K).
80
81 let ps_command = format!(
82 //"Start-Process pwsh -ArgumentList '-NoExit', '-Command', 'cargo run --release --bin {}; if ($LASTEXITCODE -eq 0) {{ exit }}'",
83 //"Start-Process pwsh -ArgumentList '-NoExit', '-Command', 'cargo run --release --bin {}'",
84 "Start-Process pwsh -ArgumentList '-Command', 'cargo run --release --bin {}'",
85 bin_name
86 );
87
88 Command::new("pwsh")
89 .arg("-Command")
90 .arg(ps_command)
91 .spawn()
92 .expect("❌ Błąd: Nie znaleziono 'pwsh'. Upewnij się, że PowerShell Core jest w PATH.")
93 .wait()
94 .expect("Błąd oczekiwania na proces startowy");
95 }
96
97 #[cfg(not(target_os = "windows"))]
98 {
99 println!("⚠️ System nie wspiera automatycznego otwierania okien.");
100 }
101}
102
103/// 🛑 Zatrzymuje program i czeka na wciśnięcie Enter.
104/// Przydatne, żeby zobaczyć błędy przed zamknięciem okna.
105pub fn wait_for_enter() {
106 print!("\n🔴 Naciśnij ENTER, aby zamknąć okno...");
107 // flush() wymaga zaimportowanego traita 'Write' (linia 4)
108 io::stdout().flush().unwrap();
109 let mut buffer = String::new();
110 io::stdin().read_line(&mut buffer).unwrap_or_default();
111}
112
113/// Zabija proces o podanej nazwie (np. "editor")
114pub fn kill_process(bin_name: &str) {
115 #[cfg(target_os = "windows")]
116 {
117 let process_name = format!("{}.exe", bin_name);
118
119 // Uruchamiamy taskkill
120 // /F - Force (wymuś zamknięcie)
121 // /IM - Image Name (nazwa pliku obrazu)
122 // /T - Tree (zabij też procesy potomne, jeśli jakieś stworzył)
123 let _ = Command::new("taskkill")
124 .args(["/F", "/IM", &process_name, "/T"])
125 .output(); // .output() czeka na wykonanie, ale ignorujemy wynik (czy się udało czy nie)
126
127 println!("💀 Wysłano sygnał zamknięcia dla {}", process_name);
128 }
129
130 #[cfg(not(target_os = "windows"))]
131 {
132 println!("⚠️ Zabijanie procesów zaimplementowane tylko dla Windows.");
133 }
134}