1use serde::{Deserialize, Serialize};
9use std::sync::atomic::AtomicU8;
10
11use super::Config;
12
13static SESSION_DEGRADE_LEVEL: AtomicU8 = AtomicU8::new(0);
14
15#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
17#[serde(rename_all = "lowercase")]
18pub enum TeeMode {
19 Never,
20 #[default]
21 Failures,
22 HighCompression,
23 Always,
24}
25
26#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
30#[serde(rename_all = "lowercase")]
31pub enum TerseAgent {
32 #[default]
33 Off,
34 Lite,
35 Full,
36 Ultra,
37}
38
39impl TerseAgent {
40 pub fn from_env() -> Self {
42 match std::env::var("LEAN_CTX_TERSE_AGENT")
43 .unwrap_or_default()
44 .to_lowercase()
45 .as_str()
46 {
47 "lite" => Self::Lite,
48 "full" => Self::Full,
49 "ultra" => Self::Ultra,
50 _ => Self::Off,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
59#[serde(rename_all = "lowercase")]
60pub enum OutputDensity {
61 #[default]
62 Normal,
63 Terse,
64 Ultra,
65}
66
67impl OutputDensity {
68 pub fn from_env() -> Self {
70 match std::env::var("LEAN_CTX_OUTPUT_DENSITY")
71 .unwrap_or_default()
72 .to_lowercase()
73 .as_str()
74 {
75 "terse" => Self::Terse,
76 "ultra" => Self::Ultra,
77 _ => Self::Normal,
78 }
79 }
80}
81
82#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
87#[serde(rename_all = "snake_case")]
88pub enum ResponseVerbosity {
89 #[default]
90 Full,
91 HeadersOnly,
92}
93
94impl ResponseVerbosity {
95 pub fn effective() -> Self {
96 if let Ok(v) = std::env::var("LEAN_CTX_RESPONSE_VERBOSITY") {
97 match v.trim().to_lowercase().as_str() {
98 "headers_only" | "headers" | "minimal" => return Self::HeadersOnly,
99 "full" | "" => return Self::Full,
100 _ => {}
101 }
102 }
103 Config::load().response_verbosity
104 }
105
106 pub fn is_headers_only(&self) -> bool {
107 matches!(self, Self::HeadersOnly)
108 }
109}
110
111#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
113#[serde(rename_all = "lowercase")]
114pub enum CompressionLevel {
115 Off,
116 #[default]
123 Lite,
124 Standard,
125 Max,
126}
127
128impl CompressionLevel {
129 pub fn to_components(&self) -> (TerseAgent, OutputDensity, &'static str, bool) {
132 match self {
133 Self::Off => (TerseAgent::Off, OutputDensity::Normal, "off", false),
134 Self::Lite => (TerseAgent::Lite, OutputDensity::Terse, "off", true),
135 Self::Standard => (TerseAgent::Full, OutputDensity::Terse, "compact", true),
136 Self::Max => (TerseAgent::Ultra, OutputDensity::Ultra, "tdd", true),
137 }
138 }
139
140 pub fn from_legacy(terse_agent: &TerseAgent, output_density: &OutputDensity) -> Self {
143 match (terse_agent, output_density) {
144 (TerseAgent::Ultra, _) | (_, OutputDensity::Ultra) => Self::Max,
145 (TerseAgent::Full, _) => Self::Standard,
146 (TerseAgent::Lite, _) | (_, OutputDensity::Terse) => Self::Lite,
147 _ => Self::Off,
148 }
149 }
150
151 pub fn from_env() -> Option<Self> {
153 std::env::var("LEAN_CTX_COMPRESSION").ok().and_then(|v| {
154 match v.trim().to_lowercase().as_str() {
155 "off" => Some(Self::Off),
156 "lite" => Some(Self::Lite),
157 "standard" => Some(Self::Standard),
158 "max" => Some(Self::Max),
159 _ => None,
160 }
161 })
162 }
163
164 pub fn effective(config: &Config) -> Self {
172 if let Some(degraded) = Self::session_degrade_level() {
173 return degraded;
174 }
175 if let Some(env_level) = Self::from_env() {
176 return env_level;
177 }
178 if config.compression_level != Self::Off {
179 return config.compression_level.clone();
180 }
181 if config.ultra_compact {
182 return Self::Max;
183 }
184 let ta_env = TerseAgent::from_env();
185 let od_env = OutputDensity::from_env();
186 let ta = if ta_env == TerseAgent::Off {
187 config.terse_agent.clone()
188 } else {
189 ta_env
190 };
191 let od = if od_env == OutputDensity::Normal {
192 config.output_density.clone()
193 } else {
194 od_env
195 };
196 Self::from_legacy(&ta, &od)
197 }
198
199 pub fn session_degrade_level() -> Option<Self> {
202 match SESSION_DEGRADE_LEVEL.load(std::sync::atomic::Ordering::Relaxed) {
203 1 => Some(Self::Off),
204 2 => Some(Self::Lite),
205 _ => None,
206 }
207 }
208
209 pub fn set_session_degrade(level: &Self) {
211 let val = match level {
212 Self::Off => 1u8,
213 Self::Lite => 2u8,
214 _ => 0u8,
215 };
216 SESSION_DEGRADE_LEVEL.store(val, std::sync::atomic::Ordering::Relaxed);
217 }
218
219 pub fn clear_session_degrade() {
221 SESSION_DEGRADE_LEVEL.store(0, std::sync::atomic::Ordering::Relaxed);
222 }
223
224 pub fn from_str_label(s: &str) -> Option<Self> {
225 match s.trim().to_lowercase().as_str() {
226 "off" => Some(Self::Off),
227 "lite" => Some(Self::Lite),
228 "standard" | "std" => Some(Self::Standard),
229 "max" => Some(Self::Max),
230 _ => None,
231 }
232 }
233
234 pub fn is_active(&self) -> bool {
235 !matches!(self, Self::Off)
236 }
237
238 pub fn label(&self) -> &'static str {
239 match self {
240 Self::Off => "off",
241 Self::Lite => "lite",
242 Self::Standard => "standard",
243 Self::Max => "max",
244 }
245 }
246
247 pub fn description(&self) -> &'static str {
248 match self {
249 Self::Off => "No compression — full verbose output",
250 Self::Lite => "Light compression — concise output, basic terse filtering",
251 Self::Standard => {
252 "Standard compression — dense output, compact protocol, pattern-aware"
253 }
254 Self::Max => "Maximum compression — expert mode, TDD protocol, all layers active",
255 }
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub enum RulesScope {
262 Both,
263 Global,
264 Project,
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub enum RulesInjection {
278 Shared,
279 Dedicated,
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294pub enum PermissionInheritance {
295 Off,
296 On,
297}