diff options
| -rw-r--r-- | src/util.hpp | 44 | ||||
| -rw-r--r-- | test/Jamfile.jam | 9 | ||||
| -rw-r--r-- | test/test-util.cpp | 31 |
3 files changed, 84 insertions, 0 deletions
diff --git a/src/util.hpp b/src/util.hpp index ad55604..8f2a585 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -1,6 +1,8 @@ #pragma once +#include <chrono> #include <command.h> +#include <scn/scan.h> #include <tuple> namespace WebStat { @@ -31,4 +33,46 @@ namespace WebStat { { (cmd->bindParam(firstParam++, std::forward<T>(param)), ...); } + + namespace detail { + template<typename Rep, typename Period, typename AddPeriod> + void + add(std::chrono::duration<Rep, Period> & subtotal, Rep value) + { + if constexpr (requires { subtotal += AddPeriod {value}; }) { + subtotal += AddPeriod {value}; + } + } + } + + template<typename Rep, typename Period> + std::chrono::duration<Rep, Period> + parseDuration(std::string_view input) + { + static constexpr std::initializer_list< + std::pair<void (*)(std::chrono::duration<Rep, Period> & subtotal, Rep value), std::string_view>> + DURATION_UNITS { + {detail::add<Rep, Period, std::chrono::milliseconds>, "ms"}, + {detail::add<Rep, Period, std::chrono::seconds>, "s"}, + {detail::add<Rep, Period, std::chrono::minutes>, "m"}, + {detail::add<Rep, Period, std::chrono::hours>, "h"}, + {detail::add<Rep, Period, std::chrono::days>, "d"}, + {detail::add<Rep, Period, std::chrono::weeks>, "w"}, + }; + + std::chrono::duration<Rep, Period> out {}; + auto inputSubRange = scn::ranges::subrange {input}; + + while (auto result = scn::scan<Rep, std::string>(inputSubRange, "{}{:[a-z]}")) { + const auto & [count, chars] = result->values(); + if (auto unit = std::ranges::find( + DURATION_UNITS, chars, &std::decay_t<decltype(*DURATION_UNITS.begin())>::second); + unit != DURATION_UNITS.end()) { + unit->first(out, count); + } + inputSubRange = result->range(); + } + + return out; + } } diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 6574046..98c0d9a 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -21,6 +21,15 @@ lib testing-util : <library>..//dbpp-postgresql ; +run test-util.cpp : : : + <define>BOOST_TEST_DYN_LINK + <library>testing-util + <library>..//dbpp-postgresql + <library>boost_unit_test_framework + <library>dbpptestcore + <library>stdc++fs + ; + run test-ingest.cpp : -- : ../src/schema.sql diff --git a/test/test-util.cpp b/test/test-util.cpp new file mode 100644 index 0000000..5850411 --- /dev/null +++ b/test/test-util.cpp @@ -0,0 +1,31 @@ +#define BOOST_TEST_MODULE ingest +#include <boost/test/data/test_case.hpp> +#include <boost/test/unit_test.hpp> + +#include <util.hpp> + +namespace WebStat { +} + +using DurationParserData = std::tuple<std::string_view, std::chrono::milliseconds>; + +BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1)) + +BOOST_DATA_TEST_CASE(durationParser, + boost::unit_test::data::make<DurationParserData>({ + {"", std::chrono::milliseconds {0}}, + {"123ms", std::chrono::milliseconds {123}}, + {"45s", std::chrono::seconds {45}}, + {"10m", std::chrono::minutes {10}}, + {"7h", std::chrono::hours {7}}, + {"2d", std::chrono::days {2}}, + {"7w", std::chrono::weeks {7}}, + {"1w4d3h45m10s1ms", + std::chrono::weeks {1} + std::chrono::days {4} + std::chrono::hours {3} + + std::chrono::minutes {45} + std::chrono::seconds {10} + + std::chrono::milliseconds {1}}, + }), + input, expected) +{ + BOOST_CHECK_EQUAL(expected, (WebStat::parseDuration<std::intmax_t, std::milli>(input))); +} |
