my_libs\config/
mod.rs

1// src/config/mod.rs
2
3// 📦 Zewnętrzne crate'y (odpowiednik importów z npm/deno.land)
4use serde::{Deserialize, Serialize}; // Do zamiany Struct <-> TOML/JSON
5
6// 📦 Biblioteka standardowa (std)
7use std::collections::HashMap; // Jak Map<string, string> w TS
8use std::fs; // System plików
9// use std::path::{Path, PathBuf};
10use std::path::Path;
11
12// 🧩 Moduły własne (nasze lokalne importy)
13// ⚠️ Poprawka: DatabaseConnection jest teraz w db::connect, nie w db::Database
14use crate::db::connect::DatabaseConnection;
15use crate::utils::{load_toml, save_toml};
16
17// --- STRUKTURY DANYCH  ---
18
19// 📚 EDU (TypeScript):
20// #[derive(...)] to makro. W TS musiałbyś ręcznie pisać funkcję toJson() i fromJson().
21// Tutaj Rust generuje ten kod za Ciebie podczas kompilacji.
22
23/// 🧱 1. GlobalConfig (./config/config.toml)
24/// Przechowuje informację, gdzie aktualnie szukamy baz (folder data).
25/// Odpowiednik TS: interface GlobalConfig { current_data_path: string | null }
26#[derive(Debug, Serialize, Deserialize, Default)]
27pub struct GlobalConfig {
28    pub current_data_path: Option<String>,
29}
30
31/// 🧱 2. DataIndex (data/data.toml)
32/// Lista dostępnych baz w danym folderze.
33#[derive(Debug, Serialize, Deserialize, Default)]
34pub struct DataIndex {
35    // HashMap<String, String> to w TS: Record<string, string>
36    pub databases: HashMap<String, String>,
37}
38
39/// 🧱 3. CurrentState (data/data_current.toml)
40/// Informacja, która baza jest teraz "otwarta" przez edytor.
41#[derive(Debug, Serialize, Deserialize, Default)]
42pub struct CurrentState {
43    pub active_db_name: Option<String>,
44}
45
46// --- LOGIKA OBSŁUGI ---
47
48/// ⚙️ ConfigManager - Klasa statyczna do zarządzania plikami.
49/// W Rust "metody statyczne" nie mają `&self`. Wywołujemy je ConfigManager::metoda().
50pub struct ConfigManager;
51
52impl ConfigManager {
53    // 📑 === GLOBAL CONFIG (./config/config.toml) ===
54
55    /// 💾 Wczytuje globalną konfigurację.
56    /// Zwraca GlobalConfig. Jeśli plik nie istnieje, zwraca domyślny (pusty).
57    pub fn load_global_config() -> GlobalConfig {
58        load_toml(Path::new("config/config.toml"))
59    }
60
61    /// 💾 Zapisuje globalną konfigurację
62    pub fn save_global_config(config: &GlobalConfig) {
63        save_toml(Path::new("config/config.toml"), config);
64    }
65
66    // --- 🗂️ DATA INDEX (data/data.toml) ---
67
68    /// 🧪 Wczytuje indeks baz i CZYŚCI nieistniejące wpisy (Walidacja)
69    pub fn load_and_clean_data_index(data_folder: &Path) -> DataIndex {
70        let index_path = data_folder.join("data.toml");
71
72        let mut index: DataIndex = load_toml(&index_path);
73
74        // 📚 EDU: Iterujemy i sprawdzamy, czy foldery fizycznie istnieją.
75        // Jeśli usunąłeś folder ręcznie, ten kod naprawi plik toml.
76        let mut clean_dbs = HashMap::new();
77        let mut changes_made = false;
78
79        for (name, relative_path) in index.databases {
80            let db_path = data_folder.join(&relative_path);
81            if db_path.exists() {
82                clean_dbs.insert(name, relative_path);
83            } else {
84                println!("🧹 [AUTO-CLEAN] Baza '{}' nie istnieje. Usuwam wpis.", name);
85                changes_made = true;
86            }
87        }
88
89        index.databases = clean_dbs;
90
91        if changes_made || !index_path.exists() {
92            Self::save_data_index(data_folder, &index);
93        }
94
95        index
96    }
97
98    /// 💾 Zapisuje indeks baz
99    pub fn save_data_index(data_folder: &Path, index: &DataIndex) {
100        save_toml(&data_folder.join("data.toml"), index);
101    }
102
103    // === 🗄️ ZARZĄDZANIE BAZAMI ===
104
105    /// ⚙️ Tworzy nową bazę danych FIZYCZNIE (pliki .clog, .manifest)
106    /// 📚 EDU (Async): `async fn` zwraca `Future` (jak Promise w TS).
107    /// Musisz użyć `.await` żeby to wykonać.
108    pub async fn create_new_db(
109        data_folder: &Path,
110        db_name: &str,
111    ) -> Result<(), Box<dyn std::error::Error>> {
112        let db_path = data_folder.join(db_name);
113
114        // 1. Tworzenie pustego folderu
115        if !db_path.exists() {
116            fs::create_dir_all(&db_path)?;
117            println!("✨ Utworzono folder: {:?}", db_path);
118        }
119
120        // 2. FIZYCZNA INICJALIZACJA SURREALKV
121        println!("⚙️ Inicjalizacja struktury plików SurrealKV...");
122        let db_path_str = db_path.to_str().unwrap();
123
124        // ⚠️ Poprawka: Używamy DatabaseConnection, a nie Database
125        let _temp_conn = DatabaseConnection::init(db_path_str).await?;
126        // Zmienna _temp_conn tutaj "umiera" (jest dropowana), co zamyka połączenie i zwalnia plik.
127
128        // 3. Aktualizacja indeksu
129        let mut index = Self::load_and_clean_data_index(data_folder);
130        index
131            .databases
132            .insert(db_name.to_string(), db_name.to_string());
133        Self::save_data_index(data_folder, &index);
134
135        Ok(())
136    }
137
138    /// 🚦 Ustawia aktywną bazę
139    pub fn set_active_db(data_folder: &Path, db_name: &str) {
140        let state = CurrentState {
141            active_db_name: Some(db_name.to_string()),
142        };
143        save_toml(&data_folder.join("data_current.toml"), &state);
144    }
145
146    /// 🚦 Pobiera nazwę aktywnej bazy
147    pub fn get_active_db(data_folder: &Path) -> Option<String> {
148        // 👇 UŻYWAMY UTILS (Rust sam się domyśli, że T = CurrentState)
149        let state: CurrentState = load_toml(&data_folder.join("data_current.toml"));
150        state.active_db_name
151    }
152}