lean_ctx/core/
runtime_flags.rs1use std::path::PathBuf;
2use std::sync::atomic::{AtomicBool, Ordering};
3use std::sync::{Mutex, OnceLock};
4
5static RAW: AtomicBool = AtomicBool::new(false);
6static COMPRESS: AtomicBool = AtomicBool::new(false);
7static QUIET: AtomicBool = AtomicBool::new(false);
8static MCP_SERVER: AtomicBool = AtomicBool::new(false);
9static HOOK_CHILD: AtomicBool = AtomicBool::new(false);
10static DASHBOARD_PROJECT: OnceLock<Mutex<Option<String>>> = OnceLock::new();
11static ALLOW_PATHS: OnceLock<Mutex<Vec<PathBuf>>> = OnceLock::new();
12
13pub struct FlagGuard {
14 flag: &'static AtomicBool,
15 previous: bool,
16}
17
18impl Drop for FlagGuard {
19 fn drop(&mut self) {
20 self.flag.store(self.previous, Ordering::Relaxed);
21 }
22}
23
24fn set_scoped(flag: &'static AtomicBool) -> FlagGuard {
25 let previous = flag.swap(true, Ordering::Relaxed);
26 FlagGuard { flag, previous }
27}
28
29pub fn enable_raw() {
30 RAW.store(true, Ordering::Relaxed);
31}
32
33pub fn enable_compress() {
34 COMPRESS.store(true, Ordering::Relaxed);
35}
36
37pub fn enable_mcp_server() {
38 MCP_SERVER.store(true, Ordering::Relaxed);
39}
40
41pub fn mark_hook_child() {
42 HOOK_CHILD.store(true, Ordering::Relaxed);
43}
44
45pub fn scoped_quiet() -> FlagGuard {
46 set_scoped(&QUIET)
47}
48
49pub fn set_dashboard_project(project: String) {
50 let slot = DASHBOARD_PROJECT.get_or_init(|| Mutex::new(None));
51 if let Ok(mut value) = slot.lock() {
52 *value = Some(project);
53 }
54}
55
56pub fn add_allow_paths(paths: Vec<PathBuf>) {
57 if paths.is_empty() {
58 return;
59 }
60 let slot = ALLOW_PATHS.get_or_init(|| Mutex::new(Vec::new()));
61 if let Ok(mut value) = slot.lock() {
62 value.extend(paths);
63 }
64}
65
66pub fn raw_enabled() -> bool {
67 RAW.load(Ordering::Relaxed) || std::env::var("LEAN_CTX_RAW").is_ok()
68}
69
70pub fn compress_enabled() -> bool {
71 COMPRESS.load(Ordering::Relaxed) || std::env::var("LEAN_CTX_COMPRESS").is_ok()
72}
73
74pub fn quiet_enabled() -> bool {
75 QUIET.load(Ordering::Relaxed)
76 || matches!(std::env::var("LEAN_CTX_QUIET"), Ok(value) if value.trim() == "1")
77}
78
79pub fn mcp_server_enabled() -> bool {
80 MCP_SERVER.load(Ordering::Relaxed)
81 || std::env::var("LEAN_CTX_MCP_SERVER").is_ok_and(|value| value == "1")
82}
83
84pub fn hook_child_enabled() -> bool {
85 HOOK_CHILD.load(Ordering::Relaxed) || std::env::var("LEAN_CTX_HOOK_CHILD").is_ok()
86}
87
88pub fn dashboard_project() -> Option<String> {
89 if let Some(value) = DASHBOARD_PROJECT
90 .get()
91 .and_then(|slot| slot.lock().ok().and_then(|value| value.clone()))
92 && !value.trim().is_empty()
93 {
94 return Some(value);
95 }
96 std::env::var("LEAN_CTX_DASHBOARD_PROJECT")
97 .ok()
98 .filter(|value| !value.trim().is_empty())
99}
100
101pub fn allow_paths() -> Vec<PathBuf> {
102 ALLOW_PATHS
103 .get()
104 .and_then(|slot| slot.lock().ok().map(|value| value.clone()))
105 .unwrap_or_default()
106}
107
108pub fn allow_path_enabled() -> bool {
109 !allow_paths().is_empty()
110 || std::env::var("LEAN_CTX_ALLOW_PATH").is_ok()
111 || std::env::var("LCTX_ALLOW_PATH").is_ok()
112}