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: ...@@ -343,7 +343,7 @@ private:
actor_system_config& set_impl(string_view name, config_value value); 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 /// Adjusts the content of the configuration, e.g., for ensuring backwards
/// compatibility with older options. /// compatibility with older options.
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
#include "caf/string_algorithms.hpp"
namespace caf { namespace caf {
...@@ -123,4 +124,35 @@ private: ...@@ -123,4 +124,35 @@ private:
mutable void* value_; 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 } // namespace caf
...@@ -114,6 +114,8 @@ enum class sec : uint8_t { ...@@ -114,6 +114,8 @@ enum class sec : uint8_t {
bad_function_call = 40, bad_function_call = 40,
/// Feature is disabled in the actor system config. /// Feature is disabled in the actor system config.
feature_disabled, feature_disabled,
/// Failed to open file.
cannot_open_file,
}; };
/// @relates sec /// @relates sec
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <sstream> #include <sstream>
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp" #include "caf/config_option_adder.hpp"
#include "caf/defaults.hpp" #include "caf/defaults.hpp"
#include "caf/detail/gcd.hpp" #include "caf/detail/gcd.hpp"
...@@ -34,6 +35,12 @@ ...@@ -34,6 +35,12 @@
namespace caf { namespace caf {
namespace {
constexpr const char* default_config_file = "caf-application.ini";
} // namespace anonymous
actor_system_config::~actor_system_config() { actor_system_config::~actor_system_config() {
// nop // nop
} }
...@@ -44,7 +51,7 @@ actor_system_config::~actor_system_config() { ...@@ -44,7 +51,7 @@ actor_system_config::~actor_system_config() {
actor_system_config::actor_system_config() actor_system_config::actor_system_config()
: cli_helptext_printed(false), : cli_helptext_printed(false),
slave_mode(false), slave_mode(false),
config_file_path("caf-application.ini"), config_file_path(default_config_file),
slave_mode_fun(nullptr) { slave_mode_fun(nullptr) {
// add `vector<T>` and `stream<T>` for each statically known type // add `vector<T>` and `stream<T>` for each statically known type
add_message_type_impl<stream<actor>>("stream<@actor>"); add_message_type_impl<stream<actor>>("stream<@actor>");
...@@ -67,7 +74,8 @@ actor_system_config::actor_system_config() ...@@ -67,7 +74,8 @@ actor_system_config::actor_system_config()
opt_group{custom_options_, "global"} opt_group{custom_options_, "global"}
.add<bool>("help,h?", "print help text to STDERR and exit") .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>("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"} opt_group{custom_options_, "stream"}
.add<timespan>(stream_desired_batch_complexity, "desired-batch-complexity", .add<timespan>(stream_desired_batch_complexity, "desired-batch-complexity",
"processing time per batch") "processing time per batch")
...@@ -285,8 +293,11 @@ error actor_system_config::parse(string_list args, const char* ini_file_cstr) { ...@@ -285,8 +293,11 @@ error actor_system_config::parse(string_list args, const char* ini_file_cstr) {
if (ini_file_cstr != nullptr) if (ini_file_cstr != nullptr)
config_file_path = ini_file_cstr; config_file_path = ini_file_cstr;
// CLI arguments always win. // 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}; 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); return parse(std::move(args), ini);
} }
...@@ -365,41 +376,23 @@ std::string actor_system_config::render_pec(uint8_t x, atom_value, ...@@ -365,41 +376,23 @@ std::string actor_system_config::render_pec(uint8_t x, atom_value,
meta::omittable_if_empty(), xs); meta::omittable_if_empty(), xs);
} }
void actor_system_config::extract_config_file_path(string_list& args) { error actor_system_config::extract_config_file_path(string_list& args) {
static constexpr const char needle[] = "--caf#config-file="; auto ptr = custom_options_.qualified_name_lookup("global.config-file");
auto last = args.end(); CAF_ASSERT(ptr != nullptr);
auto i = std::find_if(args.begin(), last, [](const std::string& arg) { string_list::iterator i;
return arg.compare(0, sizeof(needle) - 1, needle) == 0; string_view path;
}); std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
if (i == last) if (i == args.end())
return; return none;
auto arg_begin = i->begin() + strlen(needle); if (path.empty()) {
auto arg_end = i->end(); args.erase(i);
if (arg_begin == arg_end) { return make_error(pec::missing_argument, std::string{*i});
// Missing value.
// TODO: print warning?
return;
} }
if (*arg_begin == '"') { auto evalue = ptr->parse(path);
detail::parser::state<std::string::const_iterator> res; if (!evalue)
res.i = arg_begin; return std::move(evalue.error());
res.e = arg_end; ptr->store(*evalue);
struct consumer { return none;
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);
} }
error actor_system_config::adjust_content() { error actor_system_config::adjust_content() {
......
...@@ -64,8 +64,10 @@ const char* sec_strings[] = { ...@@ -64,8 +64,10 @@ const char* sec_strings[] = {
"no_downstream_stages_defined", "no_downstream_stages_defined",
"stream_init_failed", "stream_init_failed",
"invalid_stream_state", "invalid_stream_state",
"unhandled_stream_error",
"bad_function_call", "bad_function_call",
"feature_disabled", "feature_disabled",
"cannot_open_file",
}; };
} // namespace <anonymous> } // namespace <anonymous>
......
...@@ -199,3 +199,31 @@ CAF_TEST(flat CLI parsing with nested categories) { ...@@ -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.full_name(), "foo.goo.bar");
CAF_CHECK_EQUAL(x.has_flat_cli_name(), true); 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