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