mudra-cli

Crates.iomudra-cli
lib.rsmudra-cli
version0.1.0
created_at2025-08-19 08:11:42.406037+00
updated_at2025-08-19 08:11:42.406037+00
descriptionA robust, high-performance currency converter with caching and CLI interface
homepagehttps://github.com/AshishBagdane/mudra
repositoryhttps://github.com/AshishBagdane/mudra
max_upload_size
id1801560
size205,004
Ashish Bagdane (AshishBagdane)

documentation

README

๐Ÿช™ 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

Commit count: 2

cargo fmt