Commit decfb968 authored by Dominik Charousset's avatar Dominik Charousset

Merge branch '1blankz7-topic/ini-parser' into develop

parents 4de52375 f9402e2a
...@@ -61,6 +61,8 @@ set (LIBCAF_CORE_SRCS ...@@ -61,6 +61,8 @@ set (LIBCAF_CORE_SRCS
src/message_data.cpp src/message_data.cpp
src/message_handler.cpp src/message_handler.cpp
src/node_id.cpp src/node_id.cpp
src/parse_config.cpp
src/parse_ini.cpp
src/ref_counted.cpp src/ref_counted.cpp
src/response_promise.cpp src/response_promise.cpp
src/replies_to.cpp src/replies_to.cpp
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_PARSE_INI_HPP
#define CAF_DETAIL_PARSE_INI_HPP
#include <istream>
#include "caf/parse_config.hpp"
namespace caf {
namespace detail {
/// Parse the given input stream as INI formatted data and calls the consumer
/// with every key-value pair.
/// @param raw_data the INI formatted input stream
/// @param errors a stream of all errors which occure while parsing
/// @param consumer a function that consums the key-value pairs
void parse_ini(std::istream& raw_data, std::ostream& errors,
config_consumer consumer);
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_PARSE_INI_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_PARSER_CONFIG_HPP
#define CAF_PARSER_CONFIG_HPP
#include <algorithm>
#include <string>
#include "caf/variant.hpp"
namespace caf {
enum class config_format {
auto_detect,
ini
};
using config_value = variant<std::string, double, int64_t, bool>;
using config_consumer = std::function<void (std::string, config_value)>;
/// parse_config
/// @param file_name
/// @param cf
void parse_config(const std::string& file_name,
config_format cf = config_format::auto_detect);
} // namespace caf
#endif // CAF_PARSER_CONFIG_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/parse_config.hpp"
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include "caf/actor.hpp"
#include "caf/atom.hpp"
#include "caf/detail/optional_message_visitor.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/experimental/whereis.hpp"
namespace caf {
void parse_config(const std::string& file_name,
config_format cf) {
if (cf == config_format::auto_detect) {
// try to detect file format according to file extension
if (file_name.size() < 5) {
std::cerr << "filename is to short" << std::endl;
} else if (file_name.compare(file_name.size() - 4, 4, ".ini")) {
parse_config(file_name, config_format::ini);
} else {
std::cerr << "unknown config file format" << std::endl;
}
return;
}
std::ifstream raw_data(file_name);
std::stringstream error_stream;
struct consumer {
actor config_server_;
consumer() {
config_server_ = experimental::whereis(atom("ConfigServ"));
}
consumer(const consumer&) = default;
consumer& operator=(const consumer&) = default;
void operator()(std::string key, config_value value) const {
// send message to config server
}
};
consumer cons;
switch (cf) {
case config_format::ini:
detail::parse_ini(raw_data, error_stream, cons);
break;
default:
std::cerr << "no other format is supported" << std::endl;
break;
}
raw_data.close();
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/parse_ini.hpp"
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include "caf/string_algorithms.hpp"
namespace caf {
void detail::parse_ini(std::istream& raw_data, std::ostream& errors,
config_consumer consumer) {
std::string group;
std::string line;
size_t ln = 0; // line number
while (getline(raw_data, line)) {
++ln;
// get begin-of-line (bol) and end-of-line (eol), ignoring whitespaces
auto eol = find_if_not(line.rbegin(), line.rend(), ::isspace).base();
auto bol = find_if_not(line.begin(), eol, ::isspace);
// ignore empty lines and lines starting with ';' (comment)
if (bol == eol || *bol == ';')
continue;
// do we read a group name?
if (*bol == '[') {
if (*(eol - 1) != ']') {
errors << "error in line " << ln << ": missing ] at end of line"
<< std::endl;
} else {
group.assign(bol + 1, eol - 1);
}
// skip further processing of this line
continue;
}
// do we have a group name yet? (prohibit ungrouped values)
if (group.empty()) {
errors << "error in line " << ln << ": value outside of a group"
<< std::endl;
continue;
}
// position of the equal sign
auto eqs = find(bol, eol, '=');
if (eqs == eol) {
errors << "error in line " << ln << ": no '=' found" << std::endl;
continue;
}
if (bol == eqs) {
errors << "error in line " << ln << ": line starting with '='"
<< std::endl;
continue;
}
if ((eqs + 1) == eol) {
errors << "error in line " << ln << ": line ends with '='" << std::endl;
continue;
}
// our keys have the format "<group>.<config-name>"
auto key = group;
key += '.';
// ignore any whitespace between config-name and equal sign
key.insert(key.end(), bol, find_if(bol, eqs, ::isspace));
// begin-of-value, ignoreing whitespaces after '='
auto bov = find_if_not(eqs + 1, eol, ::isspace);
// auto-detect what we are dealing with
const char* true_str = "true";
const char* false_str = "false";
if (std::equal(bov, eol, true_str)) {
consumer(std::move(key), true);
} else if (std::equal(bov, eol, false_str)) {
consumer(std::move(key), false);
} else if (*bov == '"') {
// found a string, remove first and last char from string, start escaping
// string sequence
auto last_char_backslash = false;
std::string result = "";
// skip leading " and iterate up to the trailing "
++bov;
for (; bov+1 != eol; ++bov) {
if (last_char_backslash) {
switch (*bov) {
case 'n':
result += '\n';
break;
default:
result += *bov;
}
last_char_backslash = false;
} else if (*bov == '\\') {
last_char_backslash = true;
} else {
result += *bov;
}
}
if (last_char_backslash) {
errors << "error in line " << ln
<< ": trailing quotation mark was escaped" << std::endl;
}
consumer(std::move(key), std::move(result));
} else {
bool is_neg = *bov == '-';
if (is_neg && ++bov == eol) {
errors << "error in line " << ln << ": '-' is not a number"
<< std::endl;
continue;
}
auto set_ival = [&](int base) -> bool {
char* e;
int64_t res = std::strtoll(&*bov, &e, base);
if (e == &*eol) {
consumer(std::move(key), is_neg ? -res : res);
return true;
}
return false;
};
// are we dealing with a hex?
const char* hex_prefix = "0x";
if (std::equal(hex_prefix, hex_prefix + 2, bov)) {
if (! set_ival(16)) {
errors << "error in line " << ln << ": invalid hex value"
<< std::endl;
}
} else if (all_of(bov, eol, ::isdigit)) {
// check for base 8 and 10
if (*bov == '0') {
if (! set_ival(8)) {
errors << "error in line " << ln << ": invalid oct value"
<< std::endl;
}
} else {
if (! set_ival(10)) {
errors << "error in line " << ln << ": invalid decimal value"
<< std::endl;
}
}
} else {
// try to parse a double value
char* e;
double res = std::strtod(&*bov, &e);
if (e == &*eol) {
consumer(std::move(key), is_neg ? -res : res);
} else {
errors << "error in line " << ln << ": can't parse value to double"
<< std::endl;
}
}
}
}
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE ini_parser
#include "caf/test/unit_test.hpp"
#include <iostream>
#include <sstream>
#include "caf/all.hpp"
#include "caf/detail/parse_ini.hpp"
using namespace caf;
namespace {
template <class T>
bool value_is(const config_value& cv, const T& what) {
const T* ptr = get<T>(&cv);
return ptr != nullptr && *ptr == what;
}
constexpr const char* case1 = R"__(
[scheduler]
policy="work-sharing"
max-threads=2
; the middleman
[middleman]
automatic-connections=true
[nexus]
host="127.0.0.1"
port=4242
[cash]
greeting="Hi there, this is \"CASH!\"\n ~\\~ use at your own risk ~\\~"
)__";
constexpr const char* case2 = R"__(
[test]
foo=-0xff
bar=034
baz=-0.23
buzz=1E-34
)__";
} // namespace <anonymous>
CAF_TEST(simple_ini) {
std::map<std::string, config_value> values;
auto f = [&](std::string key, config_value value) {
values.emplace(std::move(key), std::move(value));
};
std::stringstream ss;
std::stringstream err;
ss << case1;
detail::parse_ini(ss, err, f);
CAF_CHECK(values.count("nexus.port") > 0);
CAF_CHECK(value_is(values["nexus.port"], int64_t{4242}));
CAF_CHECK(value_is(values["nexus.host"], std::string{"127.0.0.1"}));
CAF_CHECK(value_is(values["scheduler.policy"], std::string{"work-sharing"}));
CAF_CHECK(value_is(values["scheduler.max-threads"], int64_t{2}));
CAF_CHECK(value_is(values["middleman.automatic-connections"], bool{true}));
CAF_CHECK(values.count("cash.greeting") > 0);
CAF_CHECK(value_is(values["cash.greeting"],std::string{
"Hi there, this is \"CASH!\"\n ~\\~ use at your own risk ~\\~"
}));
}
CAF_TEST(numbers) {
std::map<std::string, config_value> values;
auto f = [&](std::string key, config_value value) {
values.emplace(std::move(key), std::move(value));
};
std::stringstream ss;
std::stringstream err;
ss << case2;
detail::parse_ini(ss, err, f);
CAF_CHECK(value_is(values["test.foo"], int64_t{-0xff}));
CAF_CHECK(value_is(values["test.bar"], int64_t{034}));
CAF_CHECK(value_is(values["test.baz"], double{-0.23}));
CAF_CHECK(value_is(values["test.buzz"], double{1E-34}));
}
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