ferriclink_core/
globals.rs1use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::{Arc, RwLock};
8
9use crate::caches::BaseCache;
10use crate::errors::Result;
11
12pub struct GlobalConfig {
14 verbose: AtomicBool,
16 debug: AtomicBool,
18 llm_cache: Arc<RwLock<Option<Box<dyn BaseCache>>>>,
20}
21
22impl GlobalConfig {
23 fn new() -> Self {
25 Self {
26 verbose: AtomicBool::new(false),
27 debug: AtomicBool::new(false),
28 llm_cache: Arc::new(RwLock::new(None)),
29 }
30 }
31
32 pub fn get_verbose(&self) -> bool {
34 self.verbose.load(Ordering::SeqCst)
35 }
36
37 pub fn set_verbose(&self, value: bool) {
39 self.verbose.store(value, Ordering::SeqCst);
40 }
41
42 pub fn get_debug(&self) -> bool {
44 self.debug.load(Ordering::SeqCst)
45 }
46
47 pub fn set_debug(&self, value: bool) {
49 self.debug.store(value, Ordering::SeqCst);
50 }
51
52 pub fn get_llm_cache_ref(
54 &self,
55 ) -> Result<std::sync::RwLockReadGuard<'_, Option<Box<dyn BaseCache>>>> {
56 self.llm_cache
57 .try_read()
58 .map_err(|_| crate::errors::FerricLinkError::generic("Failed to read LLM cache"))
59 }
60
61 pub fn has_llm_cache(&self) -> bool {
63 self.llm_cache
64 .try_read()
65 .map(|cache| cache.is_some())
66 .unwrap_or(false)
67 }
68
69 pub fn set_llm_cache(&self, cache: Option<Box<dyn BaseCache>>) -> Result<()> {
71 let mut current_cache = self
72 .llm_cache
73 .write()
74 .map_err(|_| crate::errors::FerricLinkError::generic("Failed to write LLM cache"))?;
75 *current_cache = cache;
76 Ok(())
77 }
78
79 pub fn clear_llm_cache(&self) -> Result<()> {
81 self.set_llm_cache(None)
82 }
83
84 pub fn is_verbose(&self) -> bool {
86 self.get_verbose()
87 }
88
89 pub fn is_debug(&self) -> bool {
91 self.get_debug()
92 }
93
94 pub fn summary(&self) -> String {
96 format!(
97 "GlobalConfig {{ verbose: {}, debug: {}, has_llm_cache: {} }}",
98 self.get_verbose(),
99 self.get_debug(),
100 self.has_llm_cache()
101 )
102 }
103}
104
105impl Clone for GlobalConfig {
106 fn clone(&self) -> Self {
107 Self {
108 verbose: AtomicBool::new(self.get_verbose()),
109 debug: AtomicBool::new(self.get_debug()),
110 llm_cache: Arc::clone(&self.llm_cache),
111 }
112 }
113}
114
115static GLOBAL_CONFIG: std::sync::OnceLock<GlobalConfig> = std::sync::OnceLock::new();
117
118pub fn init_globals() -> Result<()> {
123 GLOBAL_CONFIG.set(GlobalConfig::new()).map_err(|_| {
124 crate::errors::FerricLinkError::generic("Failed to initialize global config")
125 })?;
126 Ok(())
127}
128
129pub fn get_globals() -> &'static GlobalConfig {
136 GLOBAL_CONFIG
137 .get()
138 .expect("Global configuration not initialized. Call init_globals() first.")
139}
140
141pub fn set_verbose(value: bool) {
151 get_globals().set_verbose(value);
152}
153
154pub fn get_verbose() -> bool {
164 get_globals().get_verbose()
165}
166
167pub fn set_debug(value: bool) {
177 get_globals().set_debug(value);
178}
179
180pub fn get_debug() -> bool {
190 get_globals().get_debug()
191}
192
193pub fn set_llm_cache(cache: Option<Box<dyn BaseCache>>) -> Result<()> {
207 get_globals().set_llm_cache(cache)
208}
209
210pub fn clear_llm_cache() -> Result<()> {
220 get_globals().clear_llm_cache()
221}
222
223pub fn is_verbose() -> bool {
233 get_globals().is_verbose()
234}
235
236pub fn is_debug() -> bool {
246 get_globals().is_debug()
247}
248
249pub fn has_llm_cache() -> bool {
259 get_globals().has_llm_cache()
260}
261
262pub fn globals_summary() -> String {
272 get_globals().summary()
273}
274
275pub fn reset_globals() -> Result<()> {
285 let globals = get_globals();
286 globals.set_verbose(false);
287 globals.set_debug(false);
288 globals.clear_llm_cache()?;
289 Ok(())
290}
291
292pub fn enable_verbose() {
298 set_verbose(true);
299}
300
301pub fn disable_verbose() {
307 set_verbose(false);
308}
309
310pub fn enable_debug() {
316 set_debug(true);
317}
318
319pub fn disable_debug() {
325 set_debug(false);
326}
327
328pub fn toggle_verbose() {
334 set_verbose(!get_verbose());
335}
336
337pub fn toggle_debug() {
343 set_debug(!get_debug());
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use crate::caches::InMemoryCache;
350
351 #[test]
352 fn test_global_config_creation() {
353 let config = GlobalConfig::new();
354 assert!(!config.get_verbose());
355 assert!(!config.get_debug());
356 assert!(!config.has_llm_cache());
357 }
358
359 #[test]
360 fn test_global_config_verbose() {
361 let config = GlobalConfig::new();
362
363 config.set_verbose(true);
364 assert!(config.get_verbose());
365 assert!(config.is_verbose());
366
367 config.set_verbose(false);
368 assert!(!config.get_verbose());
369 assert!(!config.is_verbose());
370 }
371
372 #[test]
373 fn test_global_config_debug() {
374 let config = GlobalConfig::new();
375
376 config.set_debug(true);
377 assert!(config.get_debug());
378 assert!(config.is_debug());
379
380 config.set_debug(false);
381 assert!(!config.get_debug());
382 assert!(!config.is_debug());
383 }
384
385 #[test]
386 fn test_global_config_llm_cache() {
387 let config = GlobalConfig::new();
388
389 assert!(!config.has_llm_cache());
391 assert!(config.get_llm_cache_ref().unwrap().is_none());
392
393 let cache = Box::new(InMemoryCache::new());
395 config.set_llm_cache(Some(cache)).unwrap();
396 assert!(config.has_llm_cache());
397 assert!(config.get_llm_cache_ref().unwrap().is_some());
398
399 config.clear_llm_cache().unwrap();
401 assert!(!config.has_llm_cache());
402 assert!(config.get_llm_cache_ref().unwrap().is_none());
403 }
404
405 #[test]
406 fn test_global_config_summary() {
407 let config = GlobalConfig::new();
408 let summary = config.summary();
409
410 assert!(summary.contains("verbose: false"));
411 assert!(summary.contains("debug: false"));
412 assert!(summary.contains("has_llm_cache: false"));
413 }
414
415 #[test]
416 fn test_global_config_clone() {
417 let config = GlobalConfig::new();
418 config.set_verbose(true);
419 config.set_debug(true);
420
421 let cloned = config.clone();
422 assert!(cloned.get_verbose());
423 assert!(cloned.get_debug());
424 }
425
426 #[test]
427 fn test_global_functions() {
428 init_globals().unwrap();
430
431 set_verbose(true);
433 assert!(get_verbose());
434 assert!(is_verbose());
435
436 enable_verbose();
437 assert!(get_verbose());
438
439 disable_verbose();
440 assert!(!get_verbose());
441
442 toggle_verbose();
443 assert!(get_verbose());
444
445 set_debug(true);
447 assert!(get_debug());
448 assert!(is_debug());
449
450 enable_debug();
451 assert!(get_debug());
452
453 disable_debug();
454 assert!(!get_debug());
455
456 toggle_debug();
457 assert!(get_debug());
458
459 let cache = Box::new(InMemoryCache::new());
461 set_llm_cache(Some(cache)).unwrap();
462 assert!(has_llm_cache());
463
464 clear_llm_cache().unwrap();
465 assert!(!has_llm_cache());
466
467 let summary = globals_summary();
469 assert!(summary.contains("GlobalConfig"));
470
471 reset_globals().unwrap();
473 assert!(!get_verbose());
474 assert!(!get_debug());
475 assert!(!has_llm_cache());
476 }
477
478 #[test]
479 fn test_global_functions_without_init() {
480 println!(
483 "Skipping test_global_functions_without_init - globals may already be initialized"
484 );
485 }
486
487 #[test]
488 fn test_multiple_init_calls() {
489 println!("Skipping test_multiple_init_calls - globals may already be initialized");
492 }
493}