Unverified Commit d3611685 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #842

Close #839
parents 3ecdb1c9 af9bd7c9
......@@ -343,7 +343,7 @@ private:
actor_system_config& set_impl(string_view name, config_value value);
void extract_config_file_path(string_list& args);
error extract_config_file_path(string_list& args);
/// Adjusts the content of the configuration, e.g., for ensuring backwards
/// compatibility with older options.
......
......@@ -23,6 +23,7 @@
#include "caf/fwd.hpp"
#include "caf/string_view.hpp"
#include "caf/string_algorithms.hpp"
namespace caf {
......@@ -123,4 +124,35 @@ private:
mutable void* value_;
};
/// Finds `config_option` string with a matching long name in (`first`, `last`],
/// where each entry is a pointer to a string. Returns a `ForwardIterator` to
/// the match and a `caf::string_view` of the option value if the entry is found
/// and a `ForwardIterator` to `last` with an empty `string_view` otherwise.
template <class ForwardIterator, class Sentinel>
std::pair<ForwardIterator, string_view>
find_by_long_name(const config_option& x, ForwardIterator first, Sentinel last) {
auto long_name = x.long_name();
for (; first != last; ++first) {
string_view str{*first};
// Make sure this is a long option starting with "--".
if (!starts_with(str, "--"))
continue;
str.remove_prefix(2);
// Skip optional "caf#" prefix.
if (starts_with(str, "caf#"))
str.remove_prefix(4);
// Make sure we are dealing with the right key.
if (!starts_with(str, long_name))
continue;
// Make sure the key is followed by an assignment.
str.remove_prefix(long_name.size());
if (!starts_with(str, "="))
continue;
// Remove leading '=' and return the value.
str.remove_prefix(1);
return {first, str};
}
return {first, string_view{}};
}
} // namespace caf
......@@ -114,6 +114,8 @@ enum class sec : uint8_t {
bad_function_call = 40,
/// Feature is disabled in the actor system config.
feature_disabled,
/// Failed to open file.
cannot_open_file,
};
/// @relates sec
......
......@@ -24,6 +24,7 @@
#include <sstream>
#include "caf/config.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/gcd.hpp"
......@@ -34,6 +35,12 @@
namespace caf {
namespace {
constexpr const char* default_config_file = "caf-application.ini";
} // namespace anonymous
actor_system_config::~actor_system_config() {
// nop
}
......@@ -44,7 +51,7 @@ actor_system_config::~actor_system_config() {
actor_system_config::actor_system_config()
: cli_helptext_printed(false),
slave_mode(false),
config_file_path("caf-application.ini"),
config_file_path(default_config_file),
slave_mode_fun(nullptr) {
// add `vector<T>` and `stream<T>` for each statically known type
add_message_type_impl<stream<actor>>("stream<@actor>");
......@@ -67,7 +74,8 @@ actor_system_config::actor_system_config()
opt_group{custom_options_, "global"}
.add<bool>("help,h?", "print help text to STDERR and exit")
.add<bool>("long-help", "print long help text to STDERR and exit")
.add<bool>("dump-config", "print configuration to STDERR and exit");
.add<bool>("dump-config", "print configuration to STDERR and exit")
.add<string>(config_file_path, "config-file", "set config file (default: caf-application.ini)");
opt_group{custom_options_, "stream"}
.add<timespan>(stream_desired_batch_complexity, "desired-batch-complexity",
"processing time per batch")
......@@ -285,8 +293,11 @@ error actor_system_config::parse(string_list args, const char* ini_file_cstr) {
if (ini_file_cstr != nullptr)
config_file_path = ini_file_cstr;
// CLI arguments always win.
extract_config_file_path(args);
if (auto err = extract_config_file_path(args))
return err;
std::ifstream ini{config_file_path};
if (config_file_path != default_config_file && !ini)
return make_error(sec::cannot_open_file, config_file_path);
return parse(std::move(args), ini);
}
......@@ -365,41 +376,23 @@ std::string actor_system_config::render_pec(uint8_t x, atom_value,
meta::omittable_if_empty(), xs);
}
void actor_system_config::extract_config_file_path(string_list& args) {
static constexpr const char needle[] = "--caf#config-file=";
auto last = args.end();
auto i = std::find_if(args.begin(), last, [](const std::string& arg) {
return arg.compare(0, sizeof(needle) - 1, needle) == 0;
});
if (i == last)
return;
auto arg_begin = i->begin() + strlen(needle);
auto arg_end = i->end();
if (arg_begin == arg_end) {
// Missing value.
// TODO: print warning?
return;
error actor_system_config::extract_config_file_path(string_list& args) {
auto ptr = custom_options_.qualified_name_lookup("global.config-file");
CAF_ASSERT(ptr != nullptr);
string_list::iterator i;
string_view path;
std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
if (i == args.end())
return none;
if (path.empty()) {
args.erase(i);
return make_error(pec::missing_argument, std::string{*i});
}
if (*arg_begin == '"') {
detail::parser::state<std::string::const_iterator> res;
res.i = arg_begin;
res.e = arg_end;
struct consumer {
std::string result;
void value(std::string&& x) {
result = std::move(x);
}
};
consumer f;
detail::parser::read_string(res, f);
if (res.code == pec::success)
config_file_path = std::move(f.result);
// TODO: else print warning?
} else {
// We support unescaped strings for convenience on the CLI.
config_file_path = std::string{arg_begin, arg_end};
}
args.erase(i);
auto evalue = ptr->parse(path);
if (!evalue)
return std::move(evalue.error());
ptr->store(*evalue);
return none;
}
error actor_system_config::adjust_content() {
......
......@@ -64,8 +64,10 @@ const char* sec_strings[] = {
"no_downstream_stages_defined",
"stream_init_failed",
"invalid_stream_state",
"unhandled_stream_error",
"bad_function_call",
"feature_disabled",
"cannot_open_file",
};
} // namespace <anonymous>
......
......@@ -199,3 +199,31 @@ CAF_TEST(flat CLI parsing with nested categories) {
CAF_CHECK_EQUAL(x.full_name(), "foo.goo.bar");
CAF_CHECK_EQUAL(x.has_flat_cli_name(), true);
}
CAF_TEST(find by long opt) {
auto needle = make_config_option<std::string>("?foo", "bar,b", "test option");
auto check = [&](std::vector<string> args, bool found_opt, bool has_opt) {
auto res = find_by_long_name(needle, std::begin(args), std::end(args));
CAF_CHECK_EQUAL(res.first != std::end(args), found_opt);
if (has_opt)
CAF_CHECK_EQUAL(res.second, "val2");
else
CAF_CHECK(res.second.empty());
};
// Well formed, find val2.
check({"--foo=val1", "--bar=val2", "--baz=val3"}, true, true);
// Dashes missing, no match.
check({"--foo=val1", "bar=val2", "--baz=val3"}, false, false);
// Equal missing.
check({"--fooval1", "--barval2", "--bazval3"}, false, false);
// Option value missing.
check({"--foo=val1", "--bar=", "--baz=val3"}, true, false);
// With prefix 'caf#'.
check({"--caf#foo=val1", "--caf#bar=val2", "--caf#baz=val3"}, true, true);
// Option not included.
check({"--foo=val1", "--b4r=val2", "--baz=val3"}, false, false);
// Option not included, with prefix.
check({"--caf#foo=val1", "--caf#b4r=val2", "--caf#baz=val3"}, false, false);
// No options to look through.
check({}, false, false);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment