use serde::{Deserialize, Serialize};
use std::sync::atomic::AtomicU8;
use super::Config;
static SESSION_DEGRADE_LEVEL: AtomicU8 = AtomicU8::new(0);
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TeeMode {
Never,
#[default]
Failures,
HighCompression,
Always,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TerseAgent {
#[default]
Off,
Lite,
Full,
Ultra,
}
impl TerseAgent {
pub fn from_env() -> Self {
match std::env::var("LEAN_CTX_TERSE_AGENT")
.unwrap_or_default()
.to_lowercase()
.as_str()
{
"lite" => Self::Lite,
"full" => Self::Full,
"ultra" => Self::Ultra,
_ => Self::Off,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum OutputDensity {
#[default]
Normal,
Terse,
Ultra,
}
impl OutputDensity {
pub fn from_env() -> Self {
match std::env::var("LEAN_CTX_OUTPUT_DENSITY")
.unwrap_or_default()
.to_lowercase()
.as_str()
{
"terse" => Self::Terse,
"ultra" => Self::Ultra,
_ => Self::Normal,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ResponseVerbosity {
#[default]
Full,
HeadersOnly,
}
impl ResponseVerbosity {
pub fn effective() -> Self {
if let Ok(v) = std::env::var("LEAN_CTX_RESPONSE_VERBOSITY") {
match v.trim().to_lowercase().as_str() {
"headers_only" | "headers" | "minimal" => return Self::HeadersOnly,
"full" | "" => return Self::Full,
_ => {}
}
}
Config::load().response_verbosity
}
pub fn is_headers_only(&self) -> bool {
matches!(self, Self::HeadersOnly)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum CompressionLevel {
Off,
#[default]
Lite,
Standard,
Max,
}
impl CompressionLevel {
pub fn to_components(&self) -> (TerseAgent, OutputDensity, &'static str, bool) {
match self {
Self::Off => (TerseAgent::Off, OutputDensity::Normal, "off", false),
Self::Lite => (TerseAgent::Lite, OutputDensity::Terse, "off", true),
Self::Standard => (TerseAgent::Full, OutputDensity::Terse, "compact", true),
Self::Max => (TerseAgent::Ultra, OutputDensity::Ultra, "tdd", true),
}
}
pub fn from_legacy(terse_agent: &TerseAgent, output_density: &OutputDensity) -> Self {
match (terse_agent, output_density) {
(TerseAgent::Ultra, _) | (_, OutputDensity::Ultra) => Self::Max,
(TerseAgent::Full, _) => Self::Standard,
(TerseAgent::Lite, _) | (_, OutputDensity::Terse) => Self::Lite,
_ => Self::Off,
}
}
pub fn from_env() -> Option<Self> {
std::env::var("LEAN_CTX_COMPRESSION").ok().and_then(|v| {
match v.trim().to_lowercase().as_str() {
"off" => Some(Self::Off),
"lite" => Some(Self::Lite),
"standard" => Some(Self::Standard),
"max" => Some(Self::Max),
_ => None,
}
})
}
pub fn effective(config: &Config) -> Self {
if let Some(degraded) = Self::session_degrade_level() {
return degraded;
}
if let Some(env_level) = Self::from_env() {
return env_level;
}
if config.compression_level != Self::Off {
return config.compression_level.clone();
}
if config.ultra_compact {
return Self::Max;
}
let ta_env = TerseAgent::from_env();
let od_env = OutputDensity::from_env();
let ta = if ta_env == TerseAgent::Off {
config.terse_agent.clone()
} else {
ta_env
};
let od = if od_env == OutputDensity::Normal {
config.output_density.clone()
} else {
od_env
};
Self::from_legacy(&ta, &od)
}
pub fn session_degrade_level() -> Option<Self> {
match SESSION_DEGRADE_LEVEL.load(std::sync::atomic::Ordering::Relaxed) {
1 => Some(Self::Off),
2 => Some(Self::Lite),
_ => None,
}
}
pub fn set_session_degrade(level: &Self) {
let val = match level {
Self::Off => 1u8,
Self::Lite => 2u8,
_ => 0u8,
};
SESSION_DEGRADE_LEVEL.store(val, std::sync::atomic::Ordering::Relaxed);
}
pub fn clear_session_degrade() {
SESSION_DEGRADE_LEVEL.store(0, std::sync::atomic::Ordering::Relaxed);
}
pub fn from_str_label(s: &str) -> Option<Self> {
match s.trim().to_lowercase().as_str() {
"off" => Some(Self::Off),
"lite" => Some(Self::Lite),
"standard" | "std" => Some(Self::Standard),
"max" => Some(Self::Max),
_ => None,
}
}
pub fn is_active(&self) -> bool {
!matches!(self, Self::Off)
}
pub fn label(&self) -> &'static str {
match self {
Self::Off => "off",
Self::Lite => "lite",
Self::Standard => "standard",
Self::Max => "max",
}
}
pub fn description(&self) -> &'static str {
match self {
Self::Off => "No compression — full verbose output",
Self::Lite => "Light compression — concise output, basic terse filtering",
Self::Standard => {
"Standard compression — dense output, compact protocol, pattern-aware"
}
Self::Max => "Maximum compression — expert mode, TDD protocol, all layers active",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RulesScope {
Both,
Global,
Project,
}