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}