1use crate::core::constants::{DEFAULT_BUFFER_SIZE, DEFAULT_POLL_RATE};
6use crate::core::prelude::*;
7use crate::ui::color::AppColor;
8use serde::{Deserialize, Serialize};
9use std::path::{Path, PathBuf};
10
11const MIN_POLL_RATE: u64 = 16; const MAX_POLL_RATE: u64 = 1000; const MAX_TYPEWRITER_DELAY: u64 = 2000; #[derive(Debug, Serialize, Deserialize)]
18struct ConfigFile {
19 general: GeneralConfig,
20 theme: ThemeConfig,
21 prompt: PromptConfig,
22 language: LanguageConfig,
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26struct GeneralConfig {
27 max_messages: usize,
28 typewriter_delay: u64,
29 input_max_length: usize,
30 max_history: usize,
31 poll_rate: u64,
32 log_level: String,
33}
34
35#[derive(Debug, Serialize, Deserialize)]
36struct ThemeConfig {
37 input_text: String,
38 input_bg: String,
39 cursor: String,
40 output_text: String,
41 output_bg: String,
42}
43
44#[derive(Debug, Serialize, Deserialize)]
45struct PromptConfig {
46 text: String,
47 color: String,
48}
49
50#[derive(Debug, Serialize, Deserialize)]
51struct LanguageConfig {
52 current: String,
53}
54
55pub struct Config {
57 config_path: Option<String>,
58 pub max_messages: usize,
59 pub typewriter_delay: Duration,
60 pub input_max_length: usize,
61 pub max_history: usize,
62 pub poll_rate: Duration,
63 pub log_level: String,
64 pub theme: Theme,
65 pub prompt: Prompt,
66 pub language: String,
67 pub debug_info: Option<String>,
68}
69
70pub struct Theme {
71 pub input_text: AppColor,
72 pub input_bg: AppColor,
73 pub cursor: AppColor,
74 pub output_text: AppColor,
75 pub output_bg: AppColor,
76}
77
78pub struct Prompt {
79 pub text: String,
80 pub color: AppColor,
81}
82
83impl Config {
84 pub async fn load() -> Result<Self> {
85 Self::load_with_messages(true).await
86 }
87
88 pub async fn load_with_messages(show_messages: bool) -> Result<Self> {
89 for path in crate::setup::setup_toml::get_config_paths() {
91 if path.exists() {
92 match Self::from_file(&path).await {
93 Ok(config) => {
94 if config.poll_rate.as_millis() < 16 && show_messages {
96 log::warn!(
97 "⚡ PERFORMANCE: poll_rate sehr niedrig! {}",
98 config.get_performance_info()
99 );
100 }
101
102 if let Err(e) = crate::commands::lang::config::LanguageConfig::load_and_apply_from_config(&config).await {
104 if show_messages {
105 log::warn!(
106 "{}",
107 get_translation(
108 "system.config.language_set_failed",
109 &[&e.to_string()]
110 )
111 );
112 }
113 }
114
115 if show_messages {
117 log::info!(
118 "{}",
119 crate::i18n::get_command_translation(
120 "system.startup.version",
121 &[crate::core::constants::VERSION]
122 )
123 );
124 }
125
126 return Ok(config);
127 }
128 Err(_e) => {
129 continue;
130 }
131 }
132 }
133 }
134
135 if show_messages {
137 log::info!("{}", get_translation("system.config.no_existing", &[]));
138 }
139
140 match crate::setup::setup_toml::ensure_config_exists().await {
141 Ok(config_path) => {
142 match Self::from_file(&config_path).await {
143 Ok(mut config) => {
144 if show_messages {
146 let plain_msg = get_translation(
147 "system.config.new_default",
148 &[&config_path.display().to_string()],
149 );
150 log::info!("{}", plain_msg);
151 config.debug_info = Some(plain_msg);
152
153 log::info!(
154 "{}",
155 crate::i18n::get_command_translation(
156 "system.startup.version",
157 &[crate::core::constants::VERSION]
158 )
159 );
160 }
161
162 let _ = crate::commands::lang::config::LanguageConfig::load_and_apply_from_config(&config).await;
163
164 Ok(config)
165 }
166 Err(e) => {
167 if show_messages {
168 log::error!(
169 "{}",
170 get_translation("system.config.load_error", &[&format!("{:?}", e)])
171 );
172 }
173 Err(e)
174 }
175 }
176 }
177 Err(e) => {
178 if show_messages {
179 log::error!(
180 "{}",
181 get_translation("system.config.setup_failed", &[&format!("{:?}", e)])
182 );
183 }
184 Err(e)
185 }
186 }
187 }
188
189 pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
190 let content = tokio::fs::read_to_string(&path)
191 .await
192 .map_err(AppError::Io)?;
193
194 let config_file: ConfigFile = toml::from_str(&content)
195 .map_err(|e| AppError::Validation(format!("Ungültiges TOML-Format: {}", e)))?;
196
197 let original_poll_rate = config_file.general.poll_rate;
199 let original_typewriter_delay = config_file.general.typewriter_delay;
200
201 let poll_rate = Self::validate_poll_rate(original_poll_rate);
202 let typewriter_delay = Self::validate_typewriter_delay(original_typewriter_delay);
203
204 let config = Self {
205 config_path: Some(path.as_ref().to_string_lossy().into_owned()),
206 max_messages: config_file.general.max_messages,
207 typewriter_delay: Duration::from_millis(typewriter_delay),
208 input_max_length: config_file.general.input_max_length,
209 max_history: config_file.general.max_history,
210 poll_rate: Duration::from_millis(poll_rate),
211 log_level: config_file.general.log_level,
212 theme: Theme::from_config(&config_file.theme)?,
213 prompt: Prompt::from_config(&config_file.prompt)?,
214 language: config_file.language.current,
215 debug_info: None,
216 };
217
218 let values_changed =
220 original_poll_rate != poll_rate || original_typewriter_delay != typewriter_delay;
221
222 if values_changed {
223 log::warn!("🔧 Ungültige Config-Werte korrigiert und gespeichert:");
224 if original_poll_rate != poll_rate {
225 log::warn!(" poll_rate: {}ms → {}ms", original_poll_rate, poll_rate);
226 }
227 if original_typewriter_delay != typewriter_delay {
228 log::warn!(
229 " typewriter_delay: {}ms → {}ms",
230 original_typewriter_delay,
231 typewriter_delay
232 );
233 }
234
235 if let Err(e) = config.save().await {
237 log::warn!("Konnte korrigierte Config nicht speichern: {}", e);
238 } else {
239 log::info!("✅ Korrigierte Werte in Config-Datei gespeichert");
240 }
241 }
242
243 Ok(config)
244 }
245
246 fn validate_poll_rate(value: u64) -> u64 {
248 match value {
249 0 => {
250 log::warn!(
251 "poll_rate = 0 nicht erlaubt, verwende Minimum: {}ms",
252 MIN_POLL_RATE
253 );
254 MIN_POLL_RATE
255 }
256 v if v < MIN_POLL_RATE => {
257 log::warn!(
258 "poll_rate = {}ms zu schnell (Performance!), verwende Minimum: {}ms",
259 v,
260 MIN_POLL_RATE
261 );
262 MIN_POLL_RATE
263 }
264 v if v > MAX_POLL_RATE => {
265 log::warn!(
266 "poll_rate = {}ms zu langsam, verwende Maximum: {}ms",
267 v,
268 MAX_POLL_RATE
269 );
270 MAX_POLL_RATE
271 }
272 v => {
273 if v < 33 {
274 log::info!("poll_rate = {}ms (sehr schnell, aber OK)", v);
275 }
276 v
277 }
278 }
279 }
280
281 fn validate_typewriter_delay(value: u64) -> u64 {
283 match value {
284 0 => {
285 log::info!("typewriter_delay = 0 → Typewriter-Effekt deaktiviert");
286 0 }
288 v if v > MAX_TYPEWRITER_DELAY => {
289 log::warn!(
290 "typewriter_delay = {}ms zu langsam, verwende Maximum: {}ms",
291 v,
292 MAX_TYPEWRITER_DELAY
293 );
294 MAX_TYPEWRITER_DELAY
295 }
296 v => v,
297 }
298 }
299
300 pub fn get_performance_info(&self) -> String {
302 let fps = 1000.0 / self.poll_rate.as_millis() as f64;
303 let typewriter_chars_per_sec = if self.typewriter_delay.as_millis() > 0 {
304 1000.0 / self.typewriter_delay.as_millis() as f64
305 } else {
306 f64::INFINITY
307 };
308
309 format!(
310 "Performance: {:.1} FPS, Typewriter: {:.1} chars/sec",
311 fps, typewriter_chars_per_sec
312 )
313 }
314
315 pub async fn save(&self) -> Result<()> {
316 if let Some(path) = &self.config_path {
317 let config_file = ConfigFile {
318 general: GeneralConfig {
319 max_messages: self.max_messages,
320 typewriter_delay: self.typewriter_delay.as_millis() as u64,
321 input_max_length: self.input_max_length,
322 max_history: self.max_history,
323 poll_rate: self.poll_rate.as_millis() as u64,
324 log_level: self.log_level.clone(),
325 },
326 theme: ThemeConfig {
327 input_text: self.theme.input_text.to_string(),
328 input_bg: self.theme.input_bg.to_string(),
329 cursor: self.theme.cursor.to_string(),
330 output_text: self.theme.output_text.to_string(),
331 output_bg: self.theme.output_bg.to_string(),
332 },
333 prompt: PromptConfig {
334 text: self.prompt.text.clone(),
335 color: self.prompt.color.to_string(),
336 },
337 language: LanguageConfig {
338 current: self.language.clone(),
339 },
340 };
341
342 let content = toml::to_string_pretty(&config_file)
343 .map_err(|e| AppError::Validation(format!("Serialisierungsfehler: {}", e)))?;
344
345 if let Some(parent) = PathBuf::from(path).parent() {
346 if !parent.exists() {
347 tokio::fs::create_dir_all(parent)
348 .await
349 .map_err(AppError::Io)?;
350 }
351 }
352
353 tokio::fs::write(path, content)
354 .await
355 .map_err(AppError::Io)?;
356 }
357 Ok(())
358 }
359}
360
361impl Theme {
362 fn from_config(config: &ThemeConfig) -> Result<Self> {
363 Ok(Self {
364 input_text: AppColor::from_string(&config.input_text)?,
365 input_bg: AppColor::from_string(&config.input_bg)?,
366 cursor: AppColor::from_string(&config.cursor)?,
367 output_text: AppColor::from_string(&config.output_text)?,
368 output_bg: AppColor::from_string(&config.output_bg)?,
369 })
370 }
371}
372
373impl Prompt {
374 fn from_config(config: &PromptConfig) -> Result<Self> {
375 Ok(Self {
376 text: config.text.clone(),
377 color: AppColor::from_string(&config.color)?,
378 })
379 }
380}
381
382crate::impl_default!(
383 Config,
384 Self {
385 config_path: None,
386 max_messages: DEFAULT_BUFFER_SIZE,
387 typewriter_delay: Duration::from_millis(50), input_max_length: DEFAULT_BUFFER_SIZE,
389 max_history: 30,
390 poll_rate: Duration::from_millis(DEFAULT_POLL_RATE), log_level: "info".to_string(),
392 theme: Theme::default(),
393 prompt: Prompt::default(),
394 language: crate::i18n::DEFAULT_LANGUAGE.to_string(),
395 debug_info: None,
396 }
397);
398
399crate::impl_default!(
400 Theme,
401 Self {
402 input_text: AppColor::new(Color::Black),
403 input_bg: AppColor::new(Color::Black),
404 cursor: AppColor::new(Color::Black),
405 output_text: AppColor::new(Color::White),
406 output_bg: AppColor::new(Color::White),
407 }
408);
409
410crate::impl_default!(
411 Prompt,
412 Self {
413 text: "/// ".to_string(),
414 color: AppColor::new(Color::Black),
415 }
416);
417
418#[cfg(debug_assertions)]
420impl Config {
421 pub fn debug_performance_warning(&self) {
422 if self.poll_rate.as_millis() < 16 {
423 log::warn!(
424 "🔥 PERFORMANCE WARNING: poll_rate = {}ms verursacht hohe CPU-Last!",
425 self.poll_rate.as_millis()
426 );
427 log::warn!("💡 EMPFEHLUNG: Setze poll_rate auf 16-33ms für bessere Performance");
428 }
429
430 if self.typewriter_delay.as_millis() < 10 {
431 log::warn!(
432 "⚡ PERFORMANCE INFO: typewriter_delay = {}ms (sehr schnell)",
433 self.typewriter_delay.as_millis()
434 );
435 }
436
437 log::info!("📊 AKTUELLE WERTE: {}", self.get_performance_info());
438 }
439}