Skip to content

AshishBagdane/mudra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🪙 Mudra - Currency Converter

A robust, high-performance currency converter built in Rust with real-time and historical exchange rates, intelligent caching, and a beautiful CLI interface.

Build Status License: MIT Rust

Mudra (मुद्रा) - Sanskrit/Hindi word meaning "currency" or "money"

✨ Features

  • 🌍 Real-time exchange rates from multiple currency providers
  • 📊 Historical data support with date-specific conversions
  • High-performance caching with configurable TTL
  • 🎯 Type-safe currency handling with compile-time validation
  • 🖥️ Beautiful CLI interface with colored output and progress indicators
  • 📱 Interactive mode for continuous currency conversions
  • 🔄 Batch processing for multiple conversions
  • 🧪 Comprehensive error handling with detailed messages
  • 📈 Performance monitoring with cache statistics
  • 🛡️ Production-ready with extensive testing

🚀 Quick Start

Installation

# Install from crates.io
cargo install mudra-cli

# Or build from source
git clone https://github.com/AshishBagdane/mudra.git
cd mudra
cargo build --release

# Or download pre-built binaries from GitHub releases
# https://github.com/AshishBagdane/mudra/releases

Note: The package name is mudra-cli but the binary is called mudra

Get an API Key

  1. Sign up for a free account at exchangerate-api.com
  2. Get your API key from the dashboard (free tier: 1,500 requests/month)
  3. Set your environment variable:
export EXCHANGE_API_KEY=your_api_key_here

Basic Usage

# Convert 100 USD to EUR
mudra convert 100 USD EUR
# Output: 100.00 USD = 85.23 EUR

# Get current exchange rates for USD
mudra rates USD

# Historical conversion
mudra convert 100 USD EUR --date 2024-01-01

# Interactive mode
mudra interactive

# Compare across multiple currencies
mudra compare 100 USD EUR,GBP,JPY,CAD

📖 Documentation

CLI Commands

Convert Currency

# Basic conversion
mudra convert <amount> <from> <to>

# With custom precision
mudra convert 100 USD EUR --precision 4

# Historical conversion
mudra convert 100 USD EUR --date 2024-01-01

# Verbose output
mudra convert 100 USD EUR --verbose

List Currencies

# Compact list
mudra list

# Extended format
mudra list --extended

# Filter currencies
mudra list --filter EUR

Exchange Rates

# All rates for USD
mudra rates USD

# Specific currencies only
mudra rates USD --currencies EUR,GBP,JPY

# Historical rates
mudra rates USD --date 2024-01-01

# Limited results
mudra rates USD --limit 10

Compare Currencies

# Compare across multiple currencies
mudra compare 100 USD EUR,GBP,JPY,CAD,AUD

# Historical comparison
mudra compare 100 USD EUR,GBP --date 2024-01-01

Cache Management

# View cache statistics
mudra cache stats

# Clear all cached data
mudra cache clear

# Clean up expired entries
mudra cache cleanup

Interactive Mode

mudra interactive

# Then use commands like:
# > 100 USD EUR
# > convert 50 GBP JPY
# > rates USD
# > list
# > help
# > quit

🏗️ Library Usage

Basic Example

use currency_converter::{CurrencyConverter, ConversionRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create converter (requires EXCHANGE_API_KEY environment variable)
    let converter = CurrencyConverter::from_env()?;

    // Convert 100 USD to EUR
    let request = ConversionRequest::from_components(100.0, "USD", "EUR")?;
    let result = converter.convert(request).await?;

    println!("Conversion: {}", result.summary());
    println!("Rate: {:.6}", result.exchange_rate);
    println!("Result: {}", result.result);

    Ok(())
}

Advanced Example with Caching

use currency_converter::{
    CurrencyConverter, ExchangeRateService, CurrencyClient,
    Config, CacheConfig, ConversionRequest
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Custom configuration
    let config = Config::new()
        .with_api_key("your-api-key")
        .with_timeout(std::time::Duration::from_secs(10));

    // Custom cache configuration
    let cache_config = CacheConfig {
        max_capacity: 500,
        latest_ttl: 600,      // 10 minutes
        historical_ttl: 7200, // 2 hours
        enable_stats: true,
    };

    // Create client and service
    let client = CurrencyClient::with_config(config)?;
    let service = ExchangeRateService::with_cache_config(client, cache_config);
    let converter = CurrencyConverter::new(service);

    // Batch conversion
    let requests = vec![
        ConversionRequest::from_components(100.0, "USD", "EUR")?,
        ConversionRequest::from_components(100.0, "USD", "GBP")?,
        ConversionRequest::from_components(100.0, "USD", "JPY")?,
    ];

    let results = converter.convert_batch(requests).await;

    for (i, result) in results.iter().enumerate() {
        match result {
            Ok(conversion) => println!("Conversion {}: {}", i + 1, conversion.summary()),
            Err(e) => println!("Error {}: {}", i + 1, e),
        }
    }

    // Check cache performance
    let stats = converter.exchange_service.get_cache_stats();
    println!("Cache hit rate: {:.1}%", stats.hit_rate);

    Ok(())
}

Historical Data Example

use currency_converter::{ExchangeRateService, api::types::HistoricalConversionRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let service = ExchangeRateService::from_env()?;

    // Historical conversion
    let request = HistoricalConversionRequest {
        amount: 100.0,
        from: "USD".to_string(),
        to: "EUR".to_string(),
        date: "2024-01-01".to_string(),
    };

    let result = service.convert_historical(request).await?;
    println!("Historical rate on 2024-01-01: {:.6}", result.conversion_rate);

    // Get historical rates
    let rates = service.get_historical_rates("USD", "2024-01-01").await?;
    println!("USD rates on {}: {:?}", rates.get_date_string(), rates.conversion_rates);

    Ok(())
}

🏗️ Architecture

Core Components

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│       CLI       │    │    Library      │    │   API Client    │
│   (clap-based)  │───▶│   (type-safe)   │───▶│  (HTTP client)  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                │                        │
                                ▼                        ▼
                       ┌─────────────────┐    ┌─────────────────┐
                       │      Cache      │    │  External API   │
                       │   (in-memory)   │    │ (exchangerate-  │
                       └─────────────────┘    │     api.com)    │
                                              └─────────────────┘

Key Design Principles

  1. Type Safety: All currency codes and amounts are validated at compile time
  2. Error Handling: Comprehensive error types with context
  3. Performance: Intelligent caching with configurable TTL
  4. Modularity: Clean separation between CLI, library, and API layers
  5. Testability: Extensive unit and integration tests with mocking

Conversion Algorithm

  1. Same Currency: Return input amount (no API call)
  2. Direct Conversion: Use base currency rates directly
  3. Cross Conversion: Convert via USD as intermediate currency
// Example: GBP → JPY via USD
// Step 1: Get USD rates (contains both GBP and JPY)
// Step 2: Calculate cross rate: JPY_rate / GBP_rate
// Step 3: Apply rate to amount

🧪 Testing

Run All Tests

# Unit tests
cargo test

# Integration tests (requires wiremock)
cargo test --test integration_tests

# With coverage (requires cargo-tarpaulin)
cargo tarpaulin --out html

Property-Based Testing

The project includes property-based tests using proptest:

# Run property tests
cargo test property_tests

Performance Benchmarks

# Run benchmarks (requires API key for realistic results)
EXCHANGE_API_KEY=your_key cargo run --bin benchmarks

# Memory usage analysis
cargo run --bin benchmarks 2>&1 | grep "bytes"

🚀 Performance

Benchmarks

Typical performance on modern hardware:

  • Type creation: ~10ns per Currency/Money instance
  • Cache hit: ~1-5ms for cached conversions
  • Cache miss: ~100-500ms (network dependent)
  • Batch conversion: ~50-200ms for 10 currencies
  • Memory usage: ~50KB baseline, ~1KB per cached entry

Cache Effectiveness

  • Hit rate: 85-95% in typical usage
  • TTL: 5 minutes for latest rates, 1 hour for historical
  • Capacity: 1000 entries (configurable)
  • Eviction: LRU (Least Recently Used)

🛠️ Development

Prerequisites

Build from Source

git clone https://github.com/AshishBagdane/mudra.git
cd mudra
cargo build

Development Commands

# Check code
cargo check

# Format code
cargo fmt

# Lint code
cargo clippy

# Run tests
cargo test

# Build release
cargo build --release

# Run mudra
./target/release/mudra --help

# Build documentation
cargo doc --open

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Make your changes and add tests
  4. Ensure all tests pass: cargo test
  5. Format code: cargo fmt
  6. Submit a pull request

📊 Supported Currencies

The converter supports 168+ currencies including:

  • Major: USD, EUR, GBP, JPY, CHF, CAD, AUD
  • Asian: CNY, KRW, INR, SGD, HKD, THB, MYR
  • European: SEK, NOK, DKK, PLN, CZK, HUF
  • Americas: BRL, MXN, ARS, CLP, COP, PEN
  • Others: ZAR, RUB, TRY, ILS, SAR, AED

Use currency_converter list to see all supported currencies.

⚙️ Configuration

Environment Variables

  • EXCHANGE_API_KEY: Your API key (required)
  • EXCHANGE_BASE_URL: Custom API base URL (optional)
  • NO_COLOR: Disable colored output (optional)

Cache Configuration

CacheConfig {
    max_capacity: 1000,        // Maximum cached entries
    latest_ttl: 300,           // Latest rates TTL (seconds)
    historical_ttl: 3600,      // Historical rates TTL (seconds)
    enable_stats: true,        // Enable statistics collection
}

🐛 Troubleshooting

Common Issues

"API key not found"

export EXCHANGE_API_KEY=your_actual_api_key

"Invalid currency code"

  • Use 3-letter ISO codes (USD, EUR, GBP)
  • Check currency_converter list for supported currencies

"Network timeout"

  • Check internet connection
  • Increase timeout with custom configuration

"Rate limit exceeded"

  • Free tier: 1,500 requests/month
  • Upgrade plan or wait for reset

Debug Mode

# Enable verbose logging
RUST_LOG=debug mudra convert 100 USD EUR --verbose

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • ExchangeRate-API for providing free exchange rate data
  • Rust community for excellent libraries and tools
  • All contributors who help improve this project

🔗 Links


Made with ❤️ and 🦀 Rust

About

Rust based currency converter - OpenSource

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages