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