/****************************************************************************** * This program test the Currency class. * * Copyright © 2021 Richard Lesh. All rights reserved. *****************************************************************************/ #include "Utils.hpp" #include #include #include #include #include #include #include #include #include #include std::locale utf8loc(std::locale(), new std::codecvt_utf8); using namespace std; class Currency { private: // Internal representation is in USD double value = 0.0; static int nonbreakingSpace; static unordered_map conversionRates; static unordered_map currencySymbols; static unordered_map currencySeparators; public: // List of legal currency codes. static vector currencyCodes; // Constructor Currency(double value, wstring currencyCode) { double conversionRate = 1.0; if (conversionRates.count(currencyCode)) { conversionRate = conversionRates.at(currencyCode); } else { throw runtime_error(Utils::wchar_to_UTF8(L"Illegal currency code: " + currencyCode)); } this->value = value / conversionRate; } // getter double get(wstring currencyCode) { double conversionRate = 1.0; if (conversionRates.count(currencyCode)) { conversionRate = conversionRates.at(currencyCode); } else { throw runtime_error(Utils::wchar_to_UTF8(L"Illegal currency code: " + currencyCode)); } return value * conversionRate; } wstring addSeparators(double d, wstring sep) noexcept { wstring const s = fmt::format(L"{0:.2f}", d); wchar_t const comma = sep[0]; wchar_t const period = sep[1]; const vector parts = Utils::split(wregex(L"\\."), s); wstring dollars = parts[0] + wstring(1, period); vector matches = {}; wregex const pattern = wregex(L"(\\d+?)(\\d{3,3}[,.].*)"); matches = Utils::regex_findfirst(pattern, dollars); while (matches.size() != 0) { dollars = matches[1] + wstring(1, comma) + matches[2]; matches = Utils::regex_findfirst(pattern, dollars); } return dollars + parts[1]; } // string conversion wstring getString(wstring currencyCode) { wstring currencySymbol = L"$"; wstring currencySeparator = L",."; if (currencySymbols.count(currencyCode)) { currencySymbol = currencySymbols.at(currencyCode); currencySeparator = currencySeparators.at(currencyCode); } wstring formatStr = L""; // If non-breaking space is first in symbol then it goes at end if (currencySymbol.length() > 1 && currencySymbol.find(nonbreakingSpace) == 0) { formatStr = L"{1:s}{0:s}"; } else { formatStr = L"{0:s}{1:s}"; } wstring const currencyValue = addSeparators(get(currencyCode), currencySeparator); return fmt::format(formatStr, currencySymbol, currencyValue); } static void setConversionRates(const unordered_map &rates) noexcept { conversionRates = rates; currencyCodes = Utils::sort(Utils::keys(conversionRates)); } }; int Currency::nonbreakingSpace = 0xA0; unordered_map Currency::conversionRates = {}; unordered_map Currency::currencySymbols = { {L"AUD", L"$\u00A0"}, {L"CAD", L"$\u00A0"}, {L"CNY", L"¥\u00A0"}, {L"EUR", L"\u00A0€"}, {L"GBP", L"£\u00A0"}, {L"INR", L"₹\u00A0"}, {L"JPY", L"¥\u00A0"}, {L"MXN", L"$\u00A0"}, {L"RUB", L"\u00A0₽"}, {L"USD", L"$"}, {L"XBT", L"\u00A0BTC"}}; unordered_map Currency::currencySeparators = { {L"AUD", L",."}, {L"CAD", L",."}, {L"CNY", L",."}, {L"EUR", L".,"}, {L"GBP", L",."}, {L"INR", L",."}, {L"JPY", L",."}, {L"MXN", L",."}, {L"RUB", L".,"}, {L"USD", L",."}, {L"XBT", L",."}}; vector Currency::currencyCodes = {}; int main(int argc, char **argv) { setlocale(LC_ALL, "en_US.UTF-8"); wcout.imbue(utf8loc); wcin.imbue(utf8loc); unordered_map todaysRates = { {L"AUD", 1.26}, {L"CAD", 1.26}, {L"CNY", 6.47}, {L"EUR", 0.82}, {L"GBP", 0.71}, {L"INR", 72.49}, {L"JPY", 105.07}, {L"MXN", 20.68}, {L"RUB", 74.29}, {L"USD", 1.0}, {L"XBT", 0.00001900} }; Currency::setConversionRates(todaysRates); wcout << L"Today's currency rates set." << endl; try { shared_ptr c(new Currency(1.0, L"USD")); for (auto code : Currency::currencyCodes) { wcout << fmt::format(L"{0:s} to {1:s}: {2:s} = {3:s}", L"USD", code, c->getString(L"USD"), c->getString(code)) << endl; } wcout << endl; c = shared_ptr(new Currency(1234.56, L"EUR")); for (auto code : Currency::currencyCodes) { wcout << fmt::format(L"{0:s} to {1:s}: {2:s} = {3:s}", L"USD", code, c->getString(L"USD"), c->getString(code)) << endl; } } catch (runtime_error ex) { wcout << L"Error: " << Utils::exceptionMessage(ex) << endl; } return 0; }