Skip to main content

lean_ctx/core/
runtime_flags.rs

1use 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}