A robust, high-performance currency converter built in Rust with real-time and historical exchange rates, intelligent caching, and a beautiful CLI interface.
Mudra (मुद्रा) - Sanskrit/Hindi word meaning "currency" or "money"
- 🌍 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
# 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 calledmudra
- Sign up for a free account at exchangerate-api.com
- Get your API key from the dashboard (free tier: 1,500 requests/month)
- Set your environment variable:
export EXCHANGE_API_KEY=your_api_key_here
# 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
# 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
# Compact list
mudra list
# Extended format
mudra list --extended
# Filter currencies
mudra list --filter EUR
# 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 across multiple currencies
mudra compare 100 USD EUR,GBP,JPY,CAD,AUD
# Historical comparison
mudra compare 100 USD EUR,GBP --date 2024-01-01
# View cache statistics
mudra cache stats
# Clear all cached data
mudra cache clear
# Clean up expired entries
mudra cache cleanup
mudra interactive
# Then use commands like:
# > 100 USD EUR
# > convert 50 GBP JPY
# > rates USD
# > list
# > help
# > quit
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(())
}
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(())
}
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(())
}
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CLI │ │ Library │ │ API Client │
│ (clap-based) │───▶│ (type-safe) │───▶│ (HTTP client) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Cache │ │ External API │
│ (in-memory) │ │ (exchangerate- │
└─────────────────┘ │ api.com) │
└─────────────────┘
- Type Safety: All currency codes and amounts are validated at compile time
- Error Handling: Comprehensive error types with context
- Performance: Intelligent caching with configurable TTL
- Modularity: Clean separation between CLI, library, and API layers
- Testability: Extensive unit and integration tests with mocking
- Same Currency: Return input amount (no API call)
- Direct Conversion: Use base currency rates directly
- 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
# Unit tests
cargo test
# Integration tests (requires wiremock)
cargo test --test integration_tests
# With coverage (requires cargo-tarpaulin)
cargo tarpaulin --out html
The project includes property-based tests using proptest
:
# Run property tests
cargo test property_tests
# 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"
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
- 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)
- Rust 1.70+
- API key from exchangerate-api.com
git clone https://github.com/AshishBagdane/mudra.git
cd mudra
cargo build
# 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
- Fork the repository
- Create a feature branch:
git checkout -b feature-name
- Make your changes and add tests
- Ensure all tests pass:
cargo test
- Format code:
cargo fmt
- Submit a pull request
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.
EXCHANGE_API_KEY
: Your API key (required)EXCHANGE_BASE_URL
: Custom API base URL (optional)NO_COLOR
: Disable colored output (optional)
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
}
"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
# Enable verbose logging
RUST_LOG=debug mudra convert 100 USD EUR --verbose
This project is licensed under the MIT License - see the LICENSE file for details.
- ExchangeRate-API for providing free exchange rate data
- Rust community for excellent libraries and tools
- All contributors who help improve this project
Made with ❤️ and 🦀 Rust