use crate::entropy_analyzer::EntropyAnalyzer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionMode {
Safe,
Default,
Aggressive,
}
impl CompressionMode {
pub fn as_str(&self) -> &'static str {
match self {
Self::Safe => "safe",
Self::Default => "default",
Self::Aggressive => "aggressive",
}
}
}
pub struct ConfidenceRouter {
entropy_analyzer: EntropyAnalyzer,
}
impl Default for ConfidenceRouter {
fn default() -> Self {
Self::new()
}
}
impl ConfidenceRouter {
pub fn new() -> Self {
Self {
entropy_analyzer: EntropyAnalyzer::new(),
}
}
pub fn route(&self, content: &str) -> CompressionMode {
if content.len() < 100 {
return CompressionMode::Default;
}
if self.is_high_risk(content) {
return CompressionMode::Safe;
}
let blocks = self.entropy_analyzer.analyze(content);
if blocks.is_empty() {
return CompressionMode::Default;
}
let avg_entropy: f64 = blocks.iter().map(|b| b.entropy).sum::<f64>() / blocks.len() as f64;
if avg_entropy < 2.5 {
return CompressionMode::Aggressive;
}
if avg_entropy > 4.5 {
return CompressionMode::Safe;
}
CompressionMode::Default
}
fn is_high_risk(&self, content: &str) -> bool {
let lower = content.to_lowercase();
if lower.contains("stack trace") || lower.contains("traceback")
|| lower.contains("at line ") || lower.contains("panicked at")
{
return true;
}
if lower.contains("alter table") || lower.contains("create table")
|| lower.contains("drop table") || lower.contains("migration")
{
return true;
}
if lower.contains("private_key") || lower.contains("secret_key")
|| lower.contains("api_key") || lower.contains("password")
|| lower.contains("-----begin") {
return true;
}
if lower.contains("terms of service") || lower.contains("privacy policy")
|| lower.contains("license agreement") || lower.contains("gdpr")
{
return true;
}
if lower.contains("apiversion:") && lower.contains("kind:")
&& (lower.contains("secret") || lower.contains("configmap"))
{
return true;
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn routes_stack_trace_to_safe() {
let router = ConfidenceRouter::new();
let trace = "thread 'main' panicked at 'index out of bounds', src/main.rs:42\nstack trace:\n 0: std::panicking::begin_panic\n 1: myapp::process\n 2: main";
assert_eq!(router.route(trace), CompressionMode::Safe);
}
#[test]
fn routes_migration_to_safe() {
let router = ConfidenceRouter::new();
let migration = "ALTER TABLE users ADD COLUMN email VARCHAR(255) NOT NULL;\nCREATE TABLE sessions (id UUID PRIMARY KEY, user_id INT REFERENCES users(id));";
assert_eq!(router.route(migration), CompressionMode::Safe);
}
#[test]
fn routes_repetitive_logs_to_aggressive() {
let router = ConfidenceRouter::new();
let logs = "// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n// comment\n";
let mode = router.route(logs);
assert!(mode == CompressionMode::Aggressive || mode == CompressionMode::Default);
}
#[test]
fn routes_normal_code_to_default() {
let router = ConfidenceRouter::new();
let code = "fn process(items: &[Item]) -> Result<Vec<Output>, Error> {\n let mut results = Vec::new();\n for item in items {\n let output = transform(item)?;\n results.push(output);\n }\n Ok(results)\n}";
let mode = router.route(code);
assert!(mode == CompressionMode::Default || mode == CompressionMode::Safe);
}
#[test]
fn short_content_is_default() {
let router = ConfidenceRouter::new();
assert_eq!(router.route("hello"), CompressionMode::Default);
}
#[test]
fn pem_key_routes_to_safe() {
let router = ConfidenceRouter::new();
let key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP\nQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz==\n-----END RSA PRIVATE KEY-----\n";
assert_eq!(router.route(key), CompressionMode::Safe);
}
}