pricing_kit 0.1.2

A flexible, lightweight Rust library for pricing strategies, including calculating and managing prices, markup, commissions, and multi-currency support.
Documentation
use std::collections::HashMap;
use serde::{Deserialize, Serialize};

/// Represents a currency with a standard code and a human-readable name.
///
/// This struct is commonly used to denote monetary units in pricing,
/// conversions, and transactions. It follows the ISO 4217 currency code format
/// (e.g. "USD" for US Dollar, "IDR" for Indonesian Rupiah).
///
/// # Fields
///
/// - `code`:  
///   A 3-letter ISO currency code in uppercase (e.g. `"USD"`, `"EUR"`, `"IDR"`).
///
/// - `name`:  
///   The full name of the currency (e.g. `"US Dollar"`, `"Indonesian Rupiah"`).
///
/// # Traits
///
/// - `Serialize`, `Deserialize`: Supports JSON and other serialization formats (via `serde`)
/// - `Debug`: For logging and debugging
/// - `Clone`, `PartialEq`, `Eq`, `Hash`: Enables usage in maps, sets, and comparisons
///
/// # Example
///
/// ```code
/// let usd = Currency::new("USD", "US Dollar");
/// assert_eq!(usd.get_code(), "USD");
/// assert_eq!(usd.get_name(), "US Dollar");
/// ```
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Currency {
    code: String,
    name: String,
}

impl Currency {
    /// Creates a new `Currency` instance with the given code and name.
    ///
    /// # Arguments
    ///
    /// * `code` - The currency code (e.g., "USD", "IDR").
    /// * `name` - The currency name (e.g., "American Dollar", "Indonesian Rupiah").
    ///
    /// # Returns
    ///
    /// A `Currency` struct initialized with the given code and name.
    pub fn new(code: &str, name: &str) -> Self {
        Currency {
            code: code.to_string(),
            name: name.to_string(),
        }
    }

    /// Returns the code of the currency (e.g., "USD").
    pub fn get_code(&self) -> &str {
        &self.code
    }

    /// Returns the name of the currency (e.g., "American Dollar").
    pub fn get_name(&self) -> &str {
        &self.name
    }
}

/// A simple currency conversion utility that stores exchange rates
/// and performs conversions between different currencies.
///
/// `CurrencyConverter` maintains a mapping between currency codes (e.g., `"USD"`, `"IDR"`)
/// and their corresponding exchange rates relative to a common base (typically 1.0 for the base currency).
///
/// The converter assumes linear conversion using the formula:
///
/// ```text
/// amount_in_target = (amount / rate_from) * rate_to
/// ```
///
/// # Fields
///
/// - `exchange_rates`:  
///   A map of currency codes (`String`) to their exchange rate values (`f64`).  
///   These rates are relative to an arbitrary common base.
///
/// # Example
///
/// ```code
/// let mut converter = CurrencyConverter::new();
/// let usd = Currency::new("USD", "US Dollar");
/// let idr = Currency::new("IDR", "Indonesian Rupiah");
///
/// converter.add_exchange_rate(&usd, 1.0);      // base currency
/// converter.add_exchange_rate(&idr, 16500.0);  // 1 USD = 16500 IDR
///
/// let amount_in_idr = converter.convert(100.0, &usd, &idr);  // result: 1_650_000.0
/// ```
///
/// # Usage Notes
///
/// - Missing exchange rates will fallback to `1.0`, so always validate your inputs.
/// - To get precise results, make sure exchange rates are consistently set relative to the same base currency.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CurrencyConverter {
    exchange_rates: HashMap<String, f64>,
}

impl CurrencyConverter {
    /// Creates a new `CurrencyConverter` instance.
    ///
    /// # Returns
    ///
    /// A new `CurrencyConverter` with no exchange rates set.
    pub fn new() -> Self {
        CurrencyConverter {
            exchange_rates: HashMap::new(),
        }
    }

    /// Adds an exchange rate for a specific currency.
    ///
    /// # Arguments
    ///
    /// * `currency` - The currency to add the exchange rate for.
    /// * `rate` - The exchange rate for the given currency.
    pub fn add_exchange_rate(&mut self, currency: &Currency, rate: f64) {
        self.exchange_rates.insert(currency.get_code().to_string(), rate);
    }

    /// Converts an amount from one currency to another using the exchange rates.
    ///
    /// # Arguments
    ///
    /// * `amount` - The amount to convert.
    /// * `from` - The currency to convert from.
    /// * `to` - The currency to convert to.
    ///
    /// # Returns
    ///
    /// The converted amount in the target currency.
    pub fn convert(&self, amount: f64, from: &Currency, to: &Currency) -> f64 {
        if from.get_code() == to.get_code() {
            return amount;
        }

        let from_rate = *self.exchange_rates.get(from.get_code()).unwrap_or(&1.0);
        let to_rate = *self.exchange_rates.get(to.get_code()).unwrap_or(&1.0);

        (amount / from_rate) * to_rate
    }

    /// Retrieves the exchange rate for the specified currency from the stored exchange rates.
    ///
    /// This function looks up the exchange rate for a given currency code (e.g., `"USD"`, `"IDR"`) 
    /// and returns the rate as a `f64` value, representing the amount of the base currency equivalent 
    /// to one unit of the given currency. If the exchange rate is not found, it returns `None`.
    ///
    /// # Arguments
    ///
    /// - `currency`:  
    ///   A reference to the `Currency` for which the exchange rate is needed.
    ///
    /// # Returns
    ///
    /// - `Option<f64>`:  
    ///   - `Some(f64)` if the exchange rate exists for the given currency code.
    ///   - `None` if the currency code does not have a stored exchange rate.
    ///
    /// # Example
    ///
    /// ```code
    /// let mut converter = CurrencyConverter::new();
    /// let usd = Currency::new("USD", "US Dollar");
    /// let idr = Currency::new("IDR", "Indonesian Rupiah");
    ///
    /// // Adding exchange rates
    /// converter.add_exchange_rate(&usd, 1.0);      // Base currency
    /// converter.add_exchange_rate(&idr, 16500.0);  // 1 USD = 16,500 IDR
    ///
    /// // Get exchange rate for USD
    /// let usd_rate = converter.get_exchange_rate(&usd);
    /// assert_eq!(usd_rate, Some(1.0));  // USD rate is 1.0 (base currency)
    ///
    /// // Get exchange rate for IDR
    /// let idr_rate = converter.get_exchange_rate(&idr);
    /// assert_eq!(idr_rate, Some(16500.0));  // IDR rate is 16500
    /// ```
    ///
    /// # Notes
    ///
    /// - If the currency is not found in the exchange rates map, the method returns `None`.
    /// - The base currency (often `USD` or any standard reference) should be initialized with a rate of `1.0`.
    pub fn get_exchange_rate(&self, currency: &Currency) -> Option<f64> {
        self.exchange_rates.get(currency.get_code()).copied()
    }

}