Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ set(INCLUDE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/include")

set(SRCS
src/Backends/Ini/IniBackend.cxx
src/Backends/String/StringBackend.cxx
src/Backends/Json/JsonBackend.cxx
src/ConfigurationInterface.cxx
src/ConfigurationFactory.cxx
Expand All @@ -95,12 +96,13 @@ set(SRCS
message(STATUS "Backends")
message(STATUS " Compiling INI backend")
message(STATUS " Compiling JSON backend")
message(STATUS " Compiling STRING backend")

# Create library
add_library(Configuration SHARED ${SRCS} $<$<BOOL:${ppconsul_FOUND}>:src/Backends/Consul/ConsulBackend.cxx>)
target_include_directories(Configuration
PUBLIC
$<INSTALL_INTERFACE:include>
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
Expand Down Expand Up @@ -140,6 +142,7 @@ set(TEST_SRCS
test/TestExamples.cxx
test/TestIni.cxx
test/TestJson.cxx
test/TestString.cxx
)

if(ppconsul_FOUND)
Expand Down
87 changes: 87 additions & 0 deletions src/Backends/String/StringBackend.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright CERN and copyright holders of ALICE O2. This software is
// distributed under the terms of the GNU General Public License v3 (GPL
// Version 3), copied verbatim in the file "COPYING".
//
// See http://alice-o2.web.cern.ch/license for full licensing information.
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// \file StringBackend.cxx
/// \brief Configuration interface for files. Port of Configuration.h & Configuration.cxx
///

#include "StringBackend.h"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#include <stdexcept>

namespace o2
{
namespace configuration
{
namespace backends
{

StringBackend::StringBackend(const std::string& s)
{
auto cfgStr = boost::trim_copy_if(s, boost::is_any_of(" \n\t"));
if (cfgStr.empty()) {
throw std::runtime_error("string cfg is empty");
}

std::vector<std::string> tokens;
boost::split(tokens, s, boost::is_any_of(";"));

for (auto& token : tokens) {
const auto equals_idx = token.find_first_of('=');
if (std::string::npos != equals_idx) {
mTree.put(boost::trim_copy(token.substr(0, equals_idx)),
boost::trim_copy(token.substr(equals_idx + 1)));
} else {
throw std::runtime_error("Not a key value pair" + token);
}
}
}

void StringBackend::putString(const std::string&, const std::string&)
{
throw std::runtime_error("String backend does not support putting values");
}

boost::optional<std::string> StringBackend::getString(const std::string& path)
{
// To use a custom separator instead of the default '.', we need to construct the path_type object explicitly
return mTree.get_optional<std::string>(decltype(mTree)::path_type(addPrefix(path), getSeparator()));
}

boost::property_tree::ptree
StringBackend::getRecursive(const std::string& path)
{
return mTree.get_child(decltype(mTree)::path_type(addPrefix(path), getSeparator()));
}

KeyValueMap StringBackend::getRecursiveMap(const std::string& path)
{
KeyValueMap map;
auto subTree = mTree.get_child(decltype(mTree)::path_type(addPrefix(path), getSeparator()));

// define lambda to recursively interate tree
using boost::property_tree::ptree;
std::function<void(const ptree&, std::string)> parse = [&](const ptree& node, std::string key) {
map[key] = std::move(node.data());
key = key.empty() ? "" : key + getSeparator();
for (auto const& it : node) {
parse(it.second, key + it.first);
}
};

parse(subTree, std::string());
return map;
}

} // namespace backends
} // namespace configuration
} // namespace o2
55 changes: 55 additions & 0 deletions src/Backends/String/StringBackend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright CERN and copyright holders of ALICE O2. This software is
// distributed under the terms of the GNU General Public License v3 (GPL
// Version 3), copied verbatim in the file "COPYING".
//
// See http://alice-o2.web.cern.ch/license for full licensing information.
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// \file StringBackend.h
/// \brief Interpret a key=value;key2=value2... etc string
///

#ifndef O2_CONFIGURATION_BACKENDS_STRINGBACKEND_H_
#define O2_CONFIGURATION_BACKENDS_STRINGBACKEND_H_

#include "../BackendBase.h"
#include <string>
#include <boost/property_tree/ptree.hpp>

namespace o2
{
namespace configuration
{
namespace backends
{

class StringBackend final : public BackendBase
{
public:
/// Interprets a string as key value pairs.
/// \param s the key=value;key2=value2 string to be used as configuration
StringBackend(const std::string& s);

/// Default destructor
virtual ~StringBackend() = default;
virtual void putString(const std::string& path,
const std::string& value) override;
virtual boost::optional<std::string>
getString(const std::string& path) override;
virtual boost::property_tree::ptree
getRecursive(const std::string& path) override;
virtual KeyValueMap getRecursiveMap(const std::string& path) override;

private:
boost::property_tree::ptree mTree;
std::string mPath;
};

} // namespace backends
} // namespace configuration
} // namespace o2

#endif // O2_CONFIGURATION_BACKENDS_STRINGBACKEND_H_
25 changes: 17 additions & 8 deletions src/ConfigurationFactory.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
///
/// \author Pascal Boeschoten, CERN

#include "Configuration/ConfigurationFactory.h"
#include "Backends/Json/JsonBackend.h"
#include "Backends/String/StringBackend.h"
#include <Backends/Ini/IniBackend.h>
#include <functional>
#include <map>
#include <stdexcept>
#include <Backends/Ini/IniBackend.h>
#include "Configuration/ConfigurationFactory.h"
#include "Backends/Json/JsonBackend.h"

#ifdef FLP_CONFIGURATION_BACKEND_CONSUL_ENABLED
# include "Backends/Consul/ConsulBackend.h"
Expand Down Expand Up @@ -50,6 +51,13 @@ auto getJson(const http::url& uri) -> UniqueConfiguration
return backend;
}

auto getString(const http::url& uri) -> UniqueConfiguration
{
auto path = uri.host + uri.path;
auto backend = std::make_unique<backends::StringBackend>(path);
return backend;
}

#ifdef FLP_CONFIGURATION_BACKEND_CONSUL_ENABLED
auto getConsul(const http::url& uri) -> UniqueConfiguration
{
Expand All @@ -76,11 +84,12 @@ auto ConfigurationFactory::getConfiguration(const std::string& uri) -> UniqueCon
throw std::runtime_error("Ill-formed URI");
}

static const std::map<std::string, std::function<UniqueConfiguration(const http::url&)>> map = {
{"ini", getIni},
{"json", getJson},
{"consul", getConsul},
};
static const std::map<std::string,
std::function<UniqueConfiguration(const http::url&)>>
map = {{"ini", getIni},
{"json", getJson},
{"consul", getConsul},
{"str", getString}};

auto iterator = map.find(parsedUrl.protocol);
if (iterator != map.end()) {
Expand Down
74 changes: 74 additions & 0 deletions test/TestString.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/// \file TestString.cxx
/// \brief String backend unit tests.
///
/// \author Adam Wegrzynek, CERN
///

#include <fstream>
#include <iostream>
#include <unordered_map>
#include "Configuration/ConfigurationFactory.h"

#define BOOST_TEST_MODULE StringBackend
#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

using namespace o2::configuration;

namespace
{

BOOST_AUTO_TEST_CASE(StringSimple)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
BOOST_CHECK_EQUAL(conf->get<std::string>("key"), "value");
BOOST_CHECK_EQUAL(conf->get<int>("key2"), 2);
BOOST_CHECK_EQUAL(conf->get<double>("key2.key3"), 3.3);
}

BOOST_AUTO_TEST_CASE(StringEntireRec)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
auto tree = conf->getRecursive();
BOOST_CHECK_EQUAL(tree.get<std::string>("key"), "value");
BOOST_CHECK_EQUAL(tree.get<int>("key2"), 2);
BOOST_CHECK_EQUAL(tree.get<double>("key2.key3"), 3.3);
}

BOOST_AUTO_TEST_CASE(StringSubRec)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
auto tree = conf->getRecursive("key2");
BOOST_CHECK_EQUAL(tree.get<double>("key3"), 3.3);
}

BOOST_AUTO_TEST_CASE(StringSimplePrefix)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
conf->setPrefix("key2");
BOOST_CHECK_EQUAL(conf->get<double>("key3"), 3.3);
}

BOOST_AUTO_TEST_CASE(StringRecMap)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
auto map = conf->getRecursiveMap("key2");
BOOST_CHECK_EQUAL(map["key3"], "3.3");
}

bool exceptionCheck(const std::runtime_error& e)
{
if (e.what() == std::string("String backend does not support putting values")) {
return true;
}
return false;
}

BOOST_AUTO_TEST_CASE(StringPutExc)
{
auto conf = ConfigurationFactory::getConfiguration("str://key=value;key2=2;key2.key3=3.3");
BOOST_CHECK_EXCEPTION(conf->put<int>("key2.key4", 4), std::runtime_error, exceptionCheck);
}

} // Anonymous namespace