}
case EVICT_OLDER_THAN: {
- evict_max_age = util::value_or_throw<Error>(util::parse_duration(arg));
+ auto duration = util::value_or_throw<Error>(util::parse_duration(arg));
+ evict_max_age =
+ std::chrono::duration_cast<std::chrono::seconds>(duration).count();
break;
}
}
}
-tl::expected<uint64_t, std::string>
+tl::expected<std::chrono::milliseconds, std::string>
parse_duration(std::string_view duration)
{
- uint64_t factor = 0;
- char last_ch = duration.empty() ? '\0' : duration[duration.length() - 1];
-
- switch (last_ch) {
- case 'd':
- factor = 24 * 60 * 60;
- break;
- case 's':
- factor = 1;
- break;
- default:
- return tl::unexpected(FMT(
- "invalid suffix (supported: d (day) and s (second)): \"{}\"", duration));
- }
-
- auto value = parse_unsigned(duration.substr(0, duration.length() - 1));
- if (!value) {
- return value;
- }
- return factor * *value;
+ if (duration.empty()) {
+ return tl::unexpected("invalid empty duration: \"\"");
+ }
+
+ uint64_t factor_ms = 0;
+ size_t suffix_len = 1;
+ char last_ch = duration.back();
+
+ // Check for two-character suffix "ms"
+ if (duration.length() >= 2 && last_ch == 's'
+ && duration[duration.length() - 2] == 'm') {
+ factor_ms = 1;
+ suffix_len = 2;
+ } else {
+ // Single-character suffixes
+ switch (last_ch) {
+ case 's':
+ factor_ms = 1000;
+ break;
+ case 'm':
+ factor_ms = 60 * 1000;
+ break;
+ case 'h':
+ factor_ms = 60 * 60 * 1000;
+ break;
+ case 'd':
+ factor_ms = 24 * 60 * 60 * 1000;
+ break;
+ default:
+ return tl::unexpected(
+ FMT("invalid suffix (supported: ms (millisecond), s (second), m "
+ "(minute), h (hour), d (day)): \"{}\"",
+ duration));
+ }
+ }
+
+ TRY_ASSIGN(
+ auto value,
+ parse_unsigned(duration.substr(0, duration.length() - suffix_len)));
+ return std::chrono::milliseconds(factor_ms * value);
}
tl::expected<int64_t, std::string>
// Returns an error string if `value` cannot be parsed as a double.
tl::expected<double, std::string> parse_double(const std::string& value);
-// Parse `duration`, an unsigned integer with d (days) or s (seconds) suffix,
-// into seconds.
-tl::expected<uint64_t, std::string> parse_duration(std::string_view duration);
+// Parse `duration`, an unsigned integer with ms (milliseconds), s (seconds), m
+// (minutes), h (hours), or d (days) suffix, into milliseconds.
+tl::expected<std::chrono::milliseconds, std::string>
+parse_duration(std::string_view duration);
// Parse a string into a signed integer.
//
TEST_CASE("util::parse_duration")
{
- CHECK(*util::parse_duration("0s") == 0);
- CHECK(*util::parse_duration("2s") == 2);
- CHECK(*util::parse_duration("1d") == 3600 * 24);
- CHECK(*util::parse_duration("2d") == 2 * 3600 * 24);
+ CHECK(*util::parse_duration("0s") == 0ms);
+ CHECK(*util::parse_duration("2s") == 2000ms);
+ CHECK(*util::parse_duration("1ms") == 1ms);
+ CHECK(*util::parse_duration("500ms") == 500ms);
+ CHECK(*util::parse_duration("1m") == 60000ms);
+ CHECK(*util::parse_duration("2m") == 120000ms);
+ CHECK(*util::parse_duration("1h") == 3600000ms);
+ CHECK(*util::parse_duration("2h") == 7200000ms);
+ CHECK(*util::parse_duration("1d") == 86400000ms);
+ CHECK(*util::parse_duration("2d") == 172800000ms);
+
+ CHECK(util::parse_duration("d").error() == "invalid unsigned integer: \"\"");
+ CHECK(util::parse_duration("xd").error()
+ == "invalid unsigned integer: \"x\"");
+ CHECK(util::parse_duration("-2d").error()
+ == "invalid unsigned integer: \"-2\"");
+
CHECK(util::parse_duration("-2").error()
- == "invalid suffix (supported: d (day) and s (second)): \"-2\"");
+ == "invalid suffix (supported: ms (millisecond), s (second), m "
+ "(minute), h (hour), d (day)): \"-2\"");
CHECK(util::parse_duration("2x").error()
- == "invalid suffix (supported: d (day) and s (second)): \"2x\"");
+ == "invalid suffix (supported: ms (millisecond), s (second), m "
+ "(minute), h (hour), d (day)): \"2x\"");
CHECK(util::parse_duration("2").error()
- == "invalid suffix (supported: d (day) and s (second)): \"2\"");
+ == "invalid suffix (supported: ms (millisecond), s (second), m "
+ "(minute), h (hour), d (day)): \"2\"");
+ CHECK(util::parse_duration("").error() == "invalid empty duration: \"\"");
}
TEST_CASE("util::parse_signed")