#include "fmtmacros.hpp"
#include <core/wincompat.hpp>
+#include <util/expected_utils.hpp>
#include <util/path_utils.hpp>
#include <util/string_utils.hpp>
m_compression = parse_bool(value, env_var_key, negate);
break;
- case ConfigItem::compression_level: {
- m_compression_level =
- Util::parse_signed(value, INT8_MIN, INT8_MAX, "compression_level");
+ case ConfigItem::compression_level:
+ m_compression_level = util::value_or_throw<Error>(
+ util::parse_signed(value, INT8_MIN, INT8_MAX, "compression_level"));
break;
- }
case ConfigItem::cpp_extension:
m_cpp_extension = value;
break;
case ConfigItem::max_files:
- m_max_files = Util::parse_unsigned(value, nullopt, nullopt, "max_files");
+ m_max_files = util::value_or_throw<Error>(
+ util::parse_unsigned(value, nullopt, nullopt, "max_files"));
break;
case ConfigItem::max_size:
duration);
}
- return factor * parse_unsigned(duration.substr(0, duration.length() - 1));
-}
-
-int64_t
-parse_signed(const std::string& value,
- optional<int64_t> min_value,
- optional<int64_t> max_value,
- string_view description)
-{
- std::string stripped_value = util::strip_whitespace(value);
-
- size_t end = 0;
- long long result = 0;
- bool failed = false;
- try {
- // Note: sizeof(long long) is guaranteed to be >= sizeof(int64_t)
- result = std::stoll(stripped_value, &end, 10);
- } catch (std::exception&) {
- failed = true;
- }
- if (failed || end != stripped_value.size()) {
- throw Error("invalid integer: \"{}\"", stripped_value);
- }
-
- int64_t min = min_value ? *min_value : INT64_MIN;
- int64_t max = max_value ? *max_value : INT64_MAX;
- if (result < min || result > max) {
- throw Error("{} must be between {} and {}", description, min, max);
+ const auto value =
+ util::parse_unsigned(duration.substr(0, duration.length() - 1));
+ if (value) {
+ return factor * *value;
+ } else {
+ throw Error(value.error());
}
- return result;
}
uint64_t
return static_cast<uint64_t>(result);
}
-uint64_t
-parse_unsigned(const std::string& value,
- optional<uint64_t> min_value,
- optional<uint64_t> max_value,
- string_view description,
- int base)
-{
- std::string stripped_value = util::strip_whitespace(value);
-
- size_t end = 0;
- unsigned long long result = 0;
- bool failed = false;
- if (util::starts_with(stripped_value, "-")) {
- failed = true;
- } else {
- try {
- // Note: sizeof(unsigned long long) is guaranteed to be >=
- // sizeof(uint64_t)
- result = std::stoull(stripped_value, &end, base);
- } catch (std::exception&) {
- failed = true;
- }
- }
- if (failed || end != stripped_value.size()) {
- const auto base_info = base == 8 ? "octal " : "";
- throw Error(
- "invalid unsigned {}integer: \"{}\"", base_info, stripped_value);
- }
-
- uint64_t min = min_value ? *min_value : 0;
- uint64_t max = max_value ? *max_value : UINT64_MAX;
- if (result < min || result > max) {
- throw Error("{} must be between {} and {}", description, min, max);
- }
- return result;
-}
-
bool
read_fd(int fd, DataReceiver data_receiver)
{
// into seconds. Throws `Error` on error.
uint64_t parse_duration(const std::string& duration);
-// Parse a string into a signed integer.
-//
-// Throws `Error` if `value` cannot be parsed as an int64_t or if the value
-// falls out of the range [`min_value`, `max_value`]. `min_value` and
-// `max_value` default to min and max values of int64_t. `description` is
-// included in the error message for range violations.
-int64_t parse_signed(const std::string& value,
- nonstd::optional<int64_t> min_value = nonstd::nullopt,
- nonstd::optional<int64_t> max_value = nonstd::nullopt,
- nonstd::string_view description = "integer");
-
// Parse a "size value", i.e. a string that can end in k, M, G, T (10-based
// suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility, K
// is also recognized as a synonym of k. Throws `Error` on parse error.
uint64_t parse_size(const std::string& value);
-// Parse a string into an unsigned integer.
-//
-// Throws `Error` if `value` cannot be parsed as an uint64_t with base `base`,
-// or if the value falls out of the range [`min_value`, `max_value`].
-// `min_value` and `max_value` default to min and max values of uint64_t.
-// `description` is included in the error message for range violations.
-uint64_t parse_unsigned(const std::string& value,
- nonstd::optional<uint64_t> min_value = nonstd::nullopt,
- nonstd::optional<uint64_t> max_value = nonstd::nullopt,
- nonstd::string_view description = "integer",
- int base = 10);
-
// Read data from `fd` until end of file and call `data_receiver` with the read
// data. Returns whether reading was successful, i.e. whether the read(2) call
// did not return -1.
#include <core/types.hpp>
#include <core/wincompat.hpp>
+#include <util/expected_utils.hpp>
#include <util/path_utils.hpp>
#include <util/string_utils.hpp>
break;
case 'F': { // --max-files
- auto files = Util::parse_unsigned(arg);
+ auto files = util::value_or_throw<Error>(util::parse_unsigned(arg));
Config::set_value_in_file(
ctx.config.primary_config_path(), "max_files", arg);
if (files == 0) {
if (arg == "uncompressed") {
wanted_level = nullopt;
} else {
- wanted_level =
- Util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level");
+ wanted_level = util::value_or_throw<Error>(
+ util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level"));
}
ProgressBar progress_bar("Recompressing...");
#include <Digest.hpp>
#include <Logging.hpp>
-#include <Util.hpp>
#include <ccache.hpp>
#include <exceptions.hpp>
#include <fmtmacros.hpp>
+#include <util/expected_utils.hpp>
#include <util/string_utils.hpp>
#include <third_party/httplib.h>
if (it == attributes.end()) {
return default_value;
} else {
- auto timeout_in_ms =
- Util::parse_unsigned(it->second, 1, 1000 * 3600, "timeout");
+ const auto timeout_in_ms = util::value_or_throw<Error>(
+ util::parse_unsigned(it->second, 1, 1000 * 3600, "timeout"));
return std::chrono::milliseconds{timeout_in_ms};
}
}
#include <Digest.hpp>
#include <Logging.hpp>
#include <fmtmacros.hpp>
+#include <util/expected_utils.hpp>
#include <util/string_utils.hpp>
#include <hiredis/hiredis.h>
if (it == attributes.end()) {
return default_value;
} else {
- return Util::parse_unsigned(it->second, 1, 1000 * 3600, "timeout");
+ return util::value_or_throw<Error>(
+ util::parse_unsigned(it->second, 1, 1000 * 3600, "timeout"));
}
}
ASSERT(m_url.scheme() == "redis");
const std::string host = m_url.host().empty() ? "localhost" : m_url.host();
- const uint32_t port =
- m_url.port().empty() ? DEFAULT_PORT
- : Util::parse_unsigned(m_url.port(), 1, 65535, "port");
+ const uint32_t port = m_url.port().empty()
+ ? DEFAULT_PORT
+ : util::value_or_throw<::Error>(util::parse_unsigned(
+ m_url.port(), 1, 65535, "port"));
ASSERT(m_url.path().empty() || m_url.path()[0] == '/');
const uint32_t db_number =
- m_url.path().empty()
- ? 0
- : Util::parse_unsigned(m_url.path().substr(1),
- 0,
- std::numeric_limits<uint32_t>::max(),
- "db number");
+ m_url.path().empty() ? 0
+ : util::value_or_throw<::Error>(util::parse_unsigned(
+ m_url.path().substr(1),
+ 0,
+ std::numeric_limits<uint32_t>::max(),
+ "db number"));
const auto connect_timeout = milliseconds_to_timeval(m_connect_timeout);
#include "string_utils.hpp"
#include <FormatNonstdStringView.hpp>
-#include <Util.hpp>
#include <fmtmacros.hpp>
#include <cctype>
namespace util {
+nonstd::expected<int64_t, std::string>
+parse_signed(const std::string& value,
+ const nonstd::optional<int64_t> min_value,
+ const nonstd::optional<int64_t> max_value,
+ const nonstd::string_view description)
+{
+ const std::string stripped_value = strip_whitespace(value);
+
+ size_t end = 0;
+ long long result = 0;
+ bool failed = false;
+ try {
+ // Note: sizeof(long long) is guaranteed to be >= sizeof(int64_t)
+ result = std::stoll(stripped_value, &end, 10);
+ } catch (std::exception&) {
+ failed = true;
+ }
+ if (failed || end != stripped_value.size()) {
+ return nonstd::make_unexpected(
+ FMT("invalid integer: \"{}\"", stripped_value));
+ }
+
+ const int64_t min = min_value ? *min_value : INT64_MIN;
+ const int64_t max = max_value ? *max_value : INT64_MAX;
+ if (result < min || result > max) {
+ return nonstd::make_unexpected(
+ FMT("{} must be between {} and {}", description, min, max));
+ } else {
+ return result;
+ }
+}
+
nonstd::expected<mode_t, std::string>
parse_umask(const std::string& value)
{
- try {
- return Util::parse_unsigned(value, 0, 0777, "umask", 8);
- } catch (const Error& e) {
- return nonstd::make_unexpected(e.what());
+ return util::parse_unsigned(value, 0, 0777, "umask", 8);
+}
+
+nonstd::expected<uint64_t, std::string>
+parse_unsigned(const std::string& value,
+ const nonstd::optional<uint64_t> min_value,
+ const nonstd::optional<uint64_t> max_value,
+ const nonstd::string_view description,
+ const int base)
+{
+ const std::string stripped_value = strip_whitespace(value);
+
+ size_t end = 0;
+ unsigned long long result = 0;
+ bool failed = false;
+ if (starts_with(stripped_value, "-")) {
+ failed = true;
+ } else {
+ try {
+ // Note: sizeof(unsigned long long) is guaranteed to be >=
+ // sizeof(uint64_t)
+ result = std::stoull(stripped_value, &end, base);
+ } catch (std::exception&) {
+ failed = true;
+ }
+ }
+ if (failed || end != stripped_value.size()) {
+ const auto base_info = base == 8 ? "octal " : "";
+ return nonstd::make_unexpected(
+ FMT("invalid unsigned {}integer: \"{}\"", base_info, stripped_value));
+ }
+
+ const uint64_t min = min_value ? *min_value : 0;
+ const uint64_t max = max_value ? *max_value : UINT64_MAX;
+ if (result < min || result > max) {
+ return nonstd::make_unexpected(
+ FMT("{} must be between {} and {}", description, min, max));
+ } else {
+ return result;
}
}
return string.ends_with(suffix);
}
+// Parse a string into a signed integer.
+//
+// Return an error string if `value` cannot be parsed as an int64_t or if the
+// value falls out of the range [`min_value`, `max_value`]. `min_value` and
+// `max_value` default to min and max values of int64_t. `description` is
+// included in the error message for range violations.
+nonstd::expected<int64_t, std::string>
+parse_signed(const std::string& value,
+ nonstd::optional<int64_t> min_value = nonstd::nullopt,
+ nonstd::optional<int64_t> max_value = nonstd::nullopt,
+ nonstd::string_view description = "integer");
+
// Parse `value` (an octal integer).
nonstd::expected<mode_t, std::string> parse_umask(const std::string& value);
+// Parse a string into an unsigned integer.
+//
+// Returns an error string if `value` cannot be parsed as an uint64_t with base
+// `base`, or if the value falls out of the range [`min_value`, `max_value`].
+// `min_value` and `max_value` default to min and max values of uint64_t.
+// `description` is included in the error message for range violations.
+nonstd::expected<uint64_t, std::string>
+parse_unsigned(const std::string& value,
+ nonstd::optional<uint64_t> min_value = nonstd::nullopt,
+ nonstd::optional<uint64_t> max_value = nonstd::nullopt,
+ nonstd::string_view description = "integer",
+ int base = 10);
+
// Percent-decode[1] `string`.
//
// [1]: https://en.wikipedia.org/wiki/Percent-encoding
"invalid suffix (supported: d (day) and s (second)): \"2\"");
}
-TEST_CASE("Util::parse_signed")
-{
- CHECK(Util::parse_signed("0") == 0);
- CHECK(Util::parse_signed("2") == 2);
- CHECK(Util::parse_signed("-17") == -17);
- CHECK(Util::parse_signed("42") == 42);
- CHECK(Util::parse_signed("0666") == 666);
- CHECK(Util::parse_signed(" 777 ") == 777);
-
- CHECK_THROWS_WITH(Util::parse_signed(""), "invalid integer: \"\"");
- CHECK_THROWS_WITH(Util::parse_signed("x"), "invalid integer: \"x\"");
- CHECK_THROWS_WITH(Util::parse_signed("0x"), "invalid integer: \"0x\"");
- CHECK_THROWS_WITH(Util::parse_signed("0x4"), "invalid integer: \"0x4\"");
-
- // Custom description not used for invalid value.
- CHECK_THROWS_WITH(Util::parse_signed("apple", nullopt, nullopt, "banana"),
- "invalid integer: \"apple\"");
-
- // Boundary values.
- CHECK_THROWS_WITH(Util::parse_signed("-9223372036854775809"),
- "invalid integer: \"-9223372036854775809\"");
- CHECK(Util::parse_signed("-9223372036854775808") == INT64_MIN);
- CHECK(Util::parse_signed("9223372036854775807") == INT64_MAX);
- CHECK_THROWS_WITH(Util::parse_signed("9223372036854775808"),
- "invalid integer: \"9223372036854775808\"");
-
- // Min and max values.
- CHECK_THROWS_WITH(Util::parse_signed("-2", -1, 1),
- "integer must be between -1 and 1");
- CHECK(Util::parse_signed("-1", -1, 1) == -1);
- CHECK(Util::parse_signed("0", -1, 1) == 0);
- CHECK(Util::parse_signed("1", -1, 1) == 1);
- CHECK_THROWS_WITH(Util::parse_signed("2", -1, 1),
- "integer must be between -1 and 1");
-
- // Custom description used for boundary violation.
- CHECK_THROWS_WITH(Util::parse_signed("0", 1, 2, "banana"),
- "banana must be between 1 and 2");
-}
-
TEST_CASE("Util::parse_size")
{
CHECK(Util::parse_size("0") == 0);
CHECK_THROWS_WITH(Util::parse_size("10x"), "invalid size: \"10x\"");
}
-TEST_CASE("Util::parse_unsigned")
-{
- CHECK(Util::parse_unsigned("0") == 0);
- CHECK(Util::parse_unsigned("2") == 2);
- CHECK(Util::parse_unsigned("42") == 42);
- CHECK(Util::parse_unsigned("0666") == 666);
- CHECK(Util::parse_unsigned(" 777 ") == 777);
-
- CHECK_THROWS_WITH(Util::parse_unsigned(""), "invalid unsigned integer: \"\"");
- CHECK_THROWS_WITH(Util::parse_unsigned("x"),
- "invalid unsigned integer: \"x\"");
- CHECK_THROWS_WITH(Util::parse_unsigned("0x"),
- "invalid unsigned integer: \"0x\"");
- CHECK_THROWS_WITH(Util::parse_unsigned("0x4"),
- "invalid unsigned integer: \"0x4\"");
-
- // Custom description not used for invalid value.
- CHECK_THROWS_WITH(Util::parse_unsigned("apple", nullopt, nullopt, "banana"),
- "invalid unsigned integer: \"apple\"");
-
- // Boundary values.
- CHECK_THROWS_WITH(Util::parse_unsigned("-1"),
- "invalid unsigned integer: \"-1\"");
- CHECK(Util::parse_unsigned("0") == 0);
- CHECK(Util::parse_unsigned("18446744073709551615") == UINT64_MAX);
- CHECK_THROWS_WITH(Util::parse_unsigned("18446744073709551616"),
- "invalid unsigned integer: \"18446744073709551616\"");
-
- // Base
- CHECK(Util::parse_unsigned("0666", nullopt, nullopt, "", 8) == 0666);
- CHECK(Util::parse_unsigned("0666", nullopt, nullopt, "", 10) == 666);
- CHECK(Util::parse_unsigned("0666", nullopt, nullopt, "", 16) == 0x666);
-}
-
TEST_CASE("Util::read_file and Util::write_file")
{
TestContext test_context;
CHECK_FALSE(util::ends_with("x", "xy"));
}
+TEST_CASE("util::parse_signed")
+{
+ CHECK(*util::parse_signed("0") == 0);
+ CHECK(*util::parse_signed("2") == 2);
+ CHECK(*util::parse_signed("-17") == -17);
+ CHECK(*util::parse_signed("42") == 42);
+ CHECK(*util::parse_signed("0666") == 666);
+ CHECK(*util::parse_signed(" 777 ") == 777);
+
+ CHECK(util::parse_signed("").error() == "invalid integer: \"\"");
+ CHECK(util::parse_signed("x").error() == "invalid integer: \"x\"");
+ CHECK(util::parse_signed("0x").error() == "invalid integer: \"0x\"");
+ CHECK(util::parse_signed("0x4").error() == "invalid integer: \"0x4\"");
+
+ // Custom description not used for invalid value.
+ CHECK(util::parse_signed("apple", nonstd::nullopt, nonstd::nullopt, "banana")
+ .error()
+ == "invalid integer: \"apple\"");
+
+ // Boundary values.
+ CHECK(util::parse_signed("-9223372036854775809").error()
+ == "invalid integer: \"-9223372036854775809\"");
+ CHECK(*util::parse_signed("-9223372036854775808") == INT64_MIN);
+ CHECK(*util::parse_signed("9223372036854775807") == INT64_MAX);
+ CHECK(util::parse_signed("9223372036854775808").error()
+ == "invalid integer: \"9223372036854775808\"");
+
+ // Min and max values.
+ CHECK(util::parse_signed("-2", -1, 1).error()
+ == "integer must be between -1 and 1");
+ CHECK(*util::parse_signed("-1", -1, 1) == -1);
+ CHECK(*util::parse_signed("0", -1, 1) == 0);
+ CHECK(*util::parse_signed("1", -1, 1) == 1);
+ CHECK(util::parse_signed("2", -1, 1).error()
+ == "integer must be between -1 and 1");
+
+ // Custom description used for boundary violation.
+ CHECK(util::parse_signed("0", 1, 2, "banana").error()
+ == "banana must be between 1 and 2");
+}
+
TEST_CASE("util::parse_umask")
{
CHECK(util::parse_umask("1") == 01u);
== "invalid unsigned octal integer: \"088\"");
}
+TEST_CASE("util::parse_unsigned")
+{
+ CHECK(*util::parse_unsigned("0") == 0);
+ CHECK(*util::parse_unsigned("2") == 2);
+ CHECK(*util::parse_unsigned("42") == 42);
+ CHECK(*util::parse_unsigned("0666") == 666);
+ CHECK(*util::parse_unsigned(" 777 ") == 777);
+
+ CHECK(util::parse_unsigned("").error() == "invalid unsigned integer: \"\"");
+ CHECK(util::parse_unsigned("x").error() == "invalid unsigned integer: \"x\"");
+ CHECK(util::parse_unsigned("0x").error()
+ == "invalid unsigned integer: \"0x\"");
+ CHECK(util::parse_unsigned("0x4").error()
+ == "invalid unsigned integer: \"0x4\"");
+
+ // Custom description not used for invalid value.
+ CHECK(
+ util::parse_unsigned("apple", nonstd::nullopt, nonstd::nullopt, "banana")
+ .error()
+ == "invalid unsigned integer: \"apple\"");
+
+ // Boundary values.
+ CHECK(util::parse_unsigned("-1").error()
+ == "invalid unsigned integer: \"-1\"");
+ CHECK(*util::parse_unsigned("0") == 0);
+ CHECK(*util::parse_unsigned("18446744073709551615") == UINT64_MAX);
+ CHECK(util::parse_unsigned("18446744073709551616").error()
+ == "invalid unsigned integer: \"18446744073709551616\"");
+
+ // Base
+ CHECK(*util::parse_unsigned("0666", nonstd::nullopt, nonstd::nullopt, "", 8)
+ == 0666);
+ CHECK(*util::parse_unsigned("0666", nonstd::nullopt, nonstd::nullopt, "", 10)
+ == 666);
+ CHECK(*util::parse_unsigned("0666", nonstd::nullopt, nonstd::nullopt, "", 16)
+ == 0x666);
+}
+
TEST_CASE("util::percent_decode")
{
CHECK(util::percent_decode("") == "");