From 4c6160d1abd17409d1c875cac03e9e1663d2d84b Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Mon, 27 Jul 2020 21:59:59 +0300 Subject: [PATCH] [booking] price formatting is implemented. --- map/booking_utils.cpp | 51 ++++++++++++++++++++--- map/booking_utils.hpp | 10 ++++- map/framework.cpp | 5 ++- map/map_tests/booking_filter_test.cpp | 59 +++++++++++++++++++++++++-- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/map/booking_utils.cpp b/map/booking_utils.cpp index b6e632a7ed..51a011ca79 100644 --- a/map/booking_utils.cpp +++ b/map/booking_utils.cpp @@ -6,6 +6,8 @@ #include "indexer/feature_decl.hpp" +#include "platform/localization.hpp" + #include "base/stl_helpers.hpp" #include @@ -25,9 +27,10 @@ void SortTransform(search::Results const & results, std::vector const & std::vector> featuresWithPrices; featuresWithPrices.reserve(results.GetCount()); + PriceFormatter formatter; for (size_t i = 0; i < results.GetCount(); ++i) { - auto priceFormatted = FormatPrice(extras[i].m_price, extras[i].m_currency); + auto priceFormatted = formatter.Format(extras[i].m_price, extras[i].m_currency); featuresWithPrices.emplace_back(results[i].GetFeatureID(), std::move(priceFormatted)); } @@ -138,8 +141,9 @@ filter::TasksRawInternal MakeInternalTasks(filter::Tasks const & filterTasks, std::vector sortedPrices; sortedPrices.reserve(extras.size()); + PriceFormatter formatter; for (size_t i = 0; i < extras.size(); ++i) - sortedPrices.emplace_back(FormatPrice(extras[i].m_price, extras[i].m_currency)); + sortedPrices.emplace_back(formatter.Format(extras[i].m_price, extras[i].m_currency)); GetPlatform().RunTask(Platform::Thread::Gui, [&searchMarks, type, sortedFeatures, sortedPrices = std::move(sortedPrices)]() mutable @@ -165,9 +169,46 @@ filter::TasksRawInternal MakeInternalTasks(filter::Tasks const & filterTasks, return tasksInternal; } -std::string FormatPrice(double price, std::string const & currency) +std::string PriceFormatter::Format(double price, std::string const & currency) { - // TODO(a): add price formatting. - return std::to_string(static_cast(price)) + " " + currency; + if (currency != m_currencyCode) + { + m_currencySymbol = platform::GetCurrencySymbol(currency); + m_currencyCode = currency; + } + auto const priceStr = std::to_string(static_cast(price)); + + auto constexpr kComma = ','; + size_t constexpr kCommaStep = 3; + auto constexpr kEllipsizeSymbol = u8"\u2026"; + size_t constexpr kEllipsizeAfter = 7; + size_t constexpr kNoEllipsizeLimit = 10; + + size_t const firstComma = priceStr.size() % kCommaStep; + size_t commaCount = 0; + size_t const fullLength = priceStr.size() + priceStr.size() / kCommaStep; + bool const needEllipsize = fullLength > kNoEllipsizeLimit; + std::string result; + for (size_t i = 0; i < priceStr.size(); ++i) + { + if (i == firstComma + commaCount * kCommaStep) + { + if (i != 0) + result += kComma; + ++commaCount; + } + + if (needEllipsize && result.size() > kEllipsizeAfter) + { + result += kEllipsizeSymbol; + break; + } + + result += priceStr[i]; + } + + result += " " + m_currencySymbol; + + return result; } } // namespace booking diff --git a/map/booking_utils.hpp b/map/booking_utils.hpp index 46af8d133a..258feedb8e 100644 --- a/map/booking_utils.hpp +++ b/map/booking_utils.hpp @@ -10,5 +10,13 @@ filter::TasksInternal MakeInternalTasks(filter::Tasks const & filterTasks, SearchMarks & searchMarks, bool inViewport); filter::TasksRawInternal MakeInternalTasks(filter::Tasks const & filterTasks, SearchMarks & searchMarks); -std::string FormatPrice(double price, std::string const & currency); +class PriceFormatter +{ +public: + std::string Format(double price, std::string const & currency); + +private: + std::string m_currencyCode; + std::string m_currencySymbol; +}; } // namespace booking diff --git a/map/framework.cpp b/map/framework.cpp index f5a6ef58ff..d9821c154d 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -3909,6 +3909,7 @@ void Framework::ShowViewportSearchResults(search::Results::ConstIter begin, if (!id.IsValid()) return; + booking::PriceFormatter formatter; for (auto const & filterResult : filtersResults) { auto const & features = filterResult.m_featuresSorted; @@ -3924,8 +3925,8 @@ void Framework::ShowViewportSearchResults(search::Results::ConstIter begin, if (found && !filterResult.m_extras.empty()) { auto const index = std::distance(features.cbegin(), it); - auto price = booking::FormatPrice(filterResult.m_extras[index].m_price, - filterResult.m_extras[index].m_currency); + auto price = formatter.Format(filterResult.m_extras[index].m_price, + filterResult.m_extras[index].m_currency); mark.SetPrice(std::move(price)); } } diff --git a/map/map_tests/booking_filter_test.cpp b/map/map_tests/booking_filter_test.cpp index 30d7964830..c893e81402 100644 --- a/map/map_tests/booking_filter_test.cpp +++ b/map/map_tests/booking_filter_test.cpp @@ -5,18 +5,21 @@ #include "map/booking_availability_filter.hpp" #include "map/booking_filter_processor.hpp" +#include "map/booking_utils.hpp" + +#include "search/result.hpp" #include "partners_api/booking_api.hpp" -#include "search/result.hpp" +#include "storage/country_info_getter.hpp" #include "indexer/data_source.hpp" #include "indexer/feature_meta.hpp" -#include "storage/country_info_getter.hpp" - #include "platform/platform.hpp" +#include "base/string_utils.hpp" + #include #include #include @@ -356,4 +359,54 @@ UNIT_CLASS_TEST(TestMwmEnvironment, BookingFilter_ApplyFilterOntoWithFeatureIds) TEST(!availabilityExtras.empty(), ()); TEST_EQUAL(availabilityExtras.size(), filteredResults.size(), ()); } + +UNIT_TEST(Booking_PriceFormatter) +{ + booking::PriceFormatter formatter; + + { + auto const result = formatter.Format(1, "USD"); + TEST(strings::StartsWith(result, "1 "), ()); + } + { + auto const result = formatter.Format(12, "USD"); + TEST(strings::StartsWith(result, "12 "), ()); + } + { + auto const result = formatter.Format(123, "USD"); + TEST(strings::StartsWith(result, "123 "), ()); + } + { + auto const result = formatter.Format(1234, "USD"); + TEST(strings::StartsWith(result, "1,234 "), (result)); + } + { + auto const result = formatter.Format(12345, "USD"); + TEST(strings::StartsWith(result, "12,345 "), (result)); + } + { + auto const result = formatter.Format(123456, "USD"); + TEST(strings::StartsWith(result, "123,456 "), (result)); + } + { + auto const result = formatter.Format(1234567, "USD"); + TEST(strings::StartsWith(result, "1,234,567 "), (result)); + } + { + auto const result = formatter.Format(12345678, "USD"); + TEST(strings::StartsWith(result, "12,345,678 "), (result)); + } + { + auto const result = formatter.Format(123456789, "USD"); + TEST(strings::StartsWith(result, std::string("123,456,") + u8"\u2026" + " "), (result)); + } + { + auto const result = formatter.Format(1234567891, "USD"); + TEST(strings::StartsWith(result, std::string("1,234,56") + u8"\u2026" + " "), (result)); + } + { + auto const result = formatter.Format(12345678911, "USD"); + TEST(strings::StartsWith(result, std::string("12,345,6") + u8"\u2026" + " "), (result)); + } +} } // namespace