mudra-cli 0.1.0

A robust, high-performance currency converter with caching and CLI interface
Documentation
//! Performance benchmarks for the currency converter

use mudra_cli::{
    CacheConfig, ConversionRequest, CurrencyClient, CurrencyConverter, ExchangeRateService,
};
use std::time::Instant;

/// Benchmark conversion performance with and without caching
pub async fn benchmark_conversion_performance() {
    println!("🚀 Running Currency Converter Benchmarks");
    println!("==========================================");

    // This would require a real API key for meaningful results
    if std::env::var("EXCHANGE_API_KEY").is_err() {
        println!("⚠️  EXCHANGE_API_KEY not set - using synthetic benchmarks");
        benchmark_type_operations().await;
        return;
    }

    let client = CurrencyClient::from_env().unwrap();
    let service = ExchangeRateService::new(client);
    let converter = CurrencyConverter::new(service);

    println!("\n📊 Cache Performance Test");
    println!("-------------------------");

    // Benchmark: First request (cache miss)
    let start = Instant::now();
    let request = ConversionRequest::from_components(100.0, "USD", "EUR").unwrap();
    let _result = converter.convert(request).await.unwrap();
    let first_duration = start.elapsed();

    // Benchmark: Second request (cache hit)
    let start = Instant::now();
    let request = ConversionRequest::from_components(200.0, "USD", "EUR").unwrap();
    let _result = converter.convert(request).await.unwrap();
    let second_duration = start.elapsed();

    println!("First request (cache miss):  {:?}", first_duration);
    println!("Second request (cache hit):  {:?}", second_duration);
    println!(
        "Cache speedup: {:.2}x",
        first_duration.as_millis() as f64 / second_duration.as_millis() as f64
    );

    // Benchmark: Batch conversions
    println!("\n🔄 Batch Conversion Performance");
    println!("-------------------------------");

    let batch_requests = vec![
        ConversionRequest::from_components(100.0, "USD", "EUR").unwrap(),
        ConversionRequest::from_components(100.0, "USD", "GBP").unwrap(),
        ConversionRequest::from_components(100.0, "USD", "JPY").unwrap(),
        ConversionRequest::from_components(100.0, "USD", "CAD").unwrap(),
        ConversionRequest::from_components(100.0, "USD", "AUD").unwrap(),
    ];

    let start = Instant::now();
    let _results = converter.convert_batch(batch_requests).await;
    let batch_duration = start.elapsed();

    println!("Batch conversion (5 currencies): {:?}", batch_duration);
    println!("Average per conversion: {:?}", batch_duration / 5);

    // Cache statistics
    let stats = converter.exchange_service.get_cache_stats();
    println!("\n📈 Cache Statistics");
    println!("-------------------");
    println!("Total requests: {}", stats.total_requests);
    println!("Cache hits: {}", stats.hits);
    println!("Cache misses: {}", stats.misses);
    println!("Hit rate: {:.1}%", stats.hit_rate);
    println!("Cached entries: {}", stats.cached_entries);
}

/// Benchmark type system operations (no network calls)
async fn benchmark_type_operations() {
    println!("\n⚡ Type System Performance");
    println!("-------------------------");

    // Benchmark: Currency creation
    let start = Instant::now();
    for _ in 0..10000 {
        let _currency = mudra_cli::Currency::new("USD").unwrap();
    }
    let currency_duration = start.elapsed();
    println!("10,000 Currency creations: {:?}", currency_duration);
    println!("Average per creation: {:?}", currency_duration / 10000);

    // Benchmark: Money creation
    let start = Instant::now();
    for i in 0..10000 {
        let _money = mudra_cli::Money::from_code(i as f64, "USD").unwrap();
    }
    let money_duration = start.elapsed();
    println!("10,000 Money creations: {:?}", money_duration);
    println!("Average per creation: {:?}", money_duration / 10000);

    // Benchmark: ConversionRequest creation
    let start = Instant::now();
    for i in 0..10000 {
        let _request = ConversionRequest::from_components(i as f64, "USD", "EUR").unwrap();
    }
    let request_duration = start.elapsed();
    println!("10,000 ConversionRequest creations: {:?}", request_duration);
    println!("Average per creation: {:?}", request_duration / 10000);

    // Benchmark: Money rounding
    let money = mudra_cli::Money::from_code(123.456789, "USD").unwrap();
    let start = Instant::now();
    for _ in 0..100000 {
        let _rounded = money.round(2);
    }
    let rounding_duration = start.elapsed();
    println!("100,000 Money rounding operations: {:?}", rounding_duration);
    println!("Average per rounding: {:?}", rounding_duration / 100000);
}

/// Memory usage benchmark
pub async fn benchmark_memory_usage() {
    println!("\n💾 Memory Usage Analysis");
    println!("------------------------");

    // Create cache with known capacity
    let cache_config = CacheConfig {
        max_capacity: 1000,
        latest_ttl: 300,
        historical_ttl: 3600,
        enable_stats: true,
    };

    println!("Cache configuration:");
    println!("  Max capacity: {}", cache_config.max_capacity);
    println!("  Latest TTL: {}s", cache_config.latest_ttl);
    println!("  Historical TTL: {}s", cache_config.historical_ttl);

    // Size of core types
    println!("\nType sizes:");
    println!(
        "  Currency: {} bytes",
        std::mem::size_of::<mudra_cli::Currency>()
    );
    println!("  Money: {} bytes", std::mem::size_of::<mudra_cli::Money>());
    println!(
        "  ConversionRequest: {} bytes",
        std::mem::size_of::<ConversionRequest>()
    );
    println!(
        "  ConversionResult: {} bytes",
        std::mem::size_of::<mudra_cli::ConversionResult>()
    );
}

/// Cache performance stress test
pub async fn benchmark_cache_stress_test() {
    if std::env::var("EXCHANGE_API_KEY").is_err() {
        println!("⚠️  Skipping cache stress test - no API key");
        return;
    }

    println!("\n🔥 Cache Stress Test");
    println!("-------------------");

    let client = CurrencyClient::from_env().unwrap();
    let service = ExchangeRateService::new(client);
    let converter = CurrencyConverter::new(service);

    // Generate many different conversion requests
    let currencies = [
        "EUR", "GBP", "JPY", "CAD", "AUD", "CHF", "CNY", "SEK", "NOK", "DKK",
    ];
    let mut requests = Vec::new();

    for from in &currencies {
        for to in &currencies {
            if from != to {
                requests.push(ConversionRequest::from_components(100.0, from, to).unwrap());
            }
        }
    }

    println!("Generated {} conversion requests", requests.len());

    // Time the conversions
    let start = Instant::now();
    let results = converter.convert_batch(requests).await;
    let total_duration = start.elapsed();

    let successful = results.iter().filter(|r| r.is_ok()).count();
    let failed = results.len() - successful;

    println!("Completed in: {:?}", total_duration);
    println!("Successful conversions: {}", successful);
    println!("Failed conversions: {}", failed);
    println!(
        "Average per conversion: {:?}",
        total_duration / results.len() as u32
    );

    // Final cache stats
    let stats = converter.exchange_service.get_cache_stats();
    println!("Final cache hit rate: {:.1}%", stats.hit_rate);
    println!(
        "Cache efficiency: {} entries for {} currencies",
        stats.cached_entries,
        currencies.len()
    );
}

/// Main benchmark runner
#[tokio::main]
async fn main() {
    benchmark_conversion_performance().await;
    benchmark_memory_usage().await;
    benchmark_cache_stress_test().await;

    println!("\n✅ Benchmarks completed!");
    println!("\nTo run with real API:");
    println!("EXCHANGE_API_KEY=your_key cargo run --bin benchmarks");
}