use std::io::BufRead;
use async_trait::async_trait;
#[async_trait]
pub trait CompressionPolicy: Send + Sync {
async fn should_compress(&self, tokens_before: usize, msg_count: usize) -> bool;
}
pub struct AutoCompressionPolicy;
#[async_trait]
impl CompressionPolicy for AutoCompressionPolicy {
async fn should_compress(&self, _tokens_before: usize, _msg_count: usize) -> bool {
true
}
}
pub struct UserConfirmationPolicy {
reader: std::sync::Mutex<Box<dyn BufRead + Send>>,
}
impl UserConfirmationPolicy {
pub fn new(reader: impl BufRead + Send + 'static) -> Self {
Self {
reader: std::sync::Mutex::new(Box::new(reader)),
}
}
pub fn stdin() -> Self {
Self::new(std::io::BufReader::new(std::io::stdin()))
}
}
#[async_trait]
impl CompressionPolicy for UserConfirmationPolicy {
async fn should_compress(&self, tokens_before: usize, msg_count: usize) -> bool {
println!(
"\n⏳ Compression needed (~{} tokens, {} messages). Proceed? (y/n): ",
tokens_before, msg_count
);
let mut reader = self.reader.lock().unwrap();
let mut input = String::new();
reader.read_line(&mut input).unwrap_or(0);
input.trim().to_lowercase() == "y"
}
}
pub struct RateLimitPolicy {
min_interval: std::time::Duration,
last_compression: std::sync::Mutex<Option<std::time::Instant>>,
}
impl RateLimitPolicy {
pub fn new(min_interval: std::time::Duration) -> Self {
Self {
min_interval,
last_compression: std::sync::Mutex::new(None),
}
}
}
#[async_trait]
impl CompressionPolicy for RateLimitPolicy {
async fn should_compress(&self, _tokens_before: usize, _msg_count: usize) -> bool {
let mut last = self.last_compression.lock().unwrap();
if let Some(last_time) = *last
&& last_time.elapsed() < self.min_interval
{
return false; }
*last = Some(std::time::Instant::now());
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_auto_policy_always_returns_true() {
let policy = AutoCompressionPolicy;
assert!(policy.should_compress(0, 0).await);
assert!(policy.should_compress(999_999, 1000).await);
}
#[tokio::test]
async fn test_rate_limit_policy_allows_first_call() {
let policy = RateLimitPolicy::new(std::time::Duration::from_secs(60));
assert!(policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_rate_limit_policy_blocks_second_call_within_interval() {
let policy = RateLimitPolicy::new(std::time::Duration::from_secs(60));
assert!(policy.should_compress(4000, 10).await);
assert!(!policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_rate_limit_policy_allows_after_interval() {
let policy = RateLimitPolicy::new(std::time::Duration::from_millis(20));
assert!(policy.should_compress(4000, 10).await);
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
assert!(policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_rate_limit_policy_tracks_last_compression_time() {
let policy = RateLimitPolicy::new(std::time::Duration::from_millis(20));
assert!(policy.should_compress(1000, 5).await);
assert!(!policy.should_compress(2000, 10).await);
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
assert!(policy.should_compress(3000, 15).await);
assert!(!policy.should_compress(4000, 20).await);
}
#[tokio::test]
async fn test_user_confirmation_accepts_y() {
let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"y\n".to_vec()));
assert!(policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_user_confirmation_accepts_uppercase_y() {
let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"Y\n".to_vec()));
assert!(policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_user_confirmation_rejects_n() {
let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"n\n".to_vec()));
assert!(!policy.should_compress(4000, 10).await);
}
#[tokio::test]
async fn test_user_confirmation_rejects_empty() {
let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"\n".to_vec()));
assert!(!policy.should_compress(4000, 10).await);
}
}