Add support to yaml config file. (#1521)

Co-authored-by: an-tao <antao2002@gmail.com>
This commit is contained in:
Hayden Zhou 2023-03-09 00:17:11 +08:00 committed by GitHub
parent 57ec87d38d
commit d4c0e063f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 278 additions and 8 deletions

View File

@ -210,6 +210,16 @@ find_package(Jsoncpp REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${JSONCPP_INCLUDE_DIRS})
# yamlcpp
find_package(yaml-cpp QUIET)
if(yaml-cpp_FOUND)
message(STATUS "yaml-cpp found")
target_link_libraries(${PROJECT_NAME} PUBLIC ${YAML_CPP_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_YAML_CPP)
else()
message(STATUS "yaml-cpp not used")
endif()
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
AND NOT WIN32)
@ -286,7 +296,10 @@ set(DROGON_SOURCES
lib/src/SlidingWindowRateLimiter.cc
lib/src/TokenBucketRateLimiter.cc
lib/src/Hodor.cc
lib/src/drogon_test.cc)
lib/src/drogon_test.cc
lib/src/ConfigAdapterManager.cc
lib/src/JsonConfigAdapter.cc
lib/src/YamlConfigAdapter.cc)
set(private_headers
lib/src/AOPAdvice.h
lib/src/CacheFile.h
@ -318,7 +331,11 @@ set(private_headers
lib/src/WebsocketControllersRouter.h
lib/src/FixedWindowRateLimiter.h
lib/src/SlidingWindowRateLimiter.h
lib/src/TokenBucketRateLimiter.h)
lib/src/TokenBucketRateLimiter.h
lib/src/ConfigAdapterManager.h
lib/src/JsonConfigAdapter.h
lib/src/YamlConfigAdapter.h
lib/src/ConfigAdapter.h)
if (NOT WIN32)
set(DROGON_SOURCES

View File

@ -40,6 +40,9 @@ endif()
if(@Hiredis_FOUND@)
find_dependency(Hiredis)
endif()
if(@yaml-cpp_FOUND)
find_dependency(yaml-cpp)
endif()
if(@BUILD_SHARED_LIBS@)
find_dependency(Threads)
endif()
@ -48,6 +51,7 @@ find_dependency(Filesystem)
find_package(Filesystem COMPONENTS Final REQUIRED)
endif()
# Our library dependencies (contains definitions for IMPORTED targets)
get_filename_component(DROGON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)

19
lib/src/ConfigAdapter.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <json/json.h>
#include <vector>
#include <string>
#include <memory>
namespace drogon
{
class ConfigAdapter
{
public:
virtual ~ConfigAdapter() = default;
virtual Json::Value getJson(const std::string &configFile) const
noexcept(false) = 0;
virtual std::vector<std::string> getExtensions() const = 0;
};
using ConfigAdapterPtr = std::shared_ptr<ConfigAdapter>;
} // namespace drogon

View File

@ -0,0 +1,46 @@
#include "ConfigAdapterManager.h"
#include "JsonConfigAdapter.h"
#include "YamlConfigAdapter.h"
#include <algorithm>
using namespace drogon;
#define REGISTER_CONFIG_ADAPTER(adapter) \
{ \
auto adapterPtr = std::make_shared<adapter>(); \
auto exts = adapterPtr->getExtensions(); \
for (auto ext : exts) \
{ \
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); \
adapters_[ext] = adapterPtr; \
} \
}
ConfigAdapterManager &ConfigAdapterManager::instance()
{
static ConfigAdapterManager instance;
return instance;
}
Json::Value ConfigAdapterManager::getJson(const std::string &configFile) const
noexcept(false)
{
auto pos = configFile.find_last_of('.');
if (pos == std::string::npos)
{
throw std::runtime_error("Invalid config file name!");
}
auto ext = configFile.substr(pos + 1);
// convert ext to lower case
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
auto it = adapters_.find(ext);
if (it == adapters_.end())
{
throw std::runtime_error("No valid parser for this config file!");
}
return it->second->getJson(configFile);
}
ConfigAdapterManager::ConfigAdapterManager()
{
REGISTER_CONFIG_ADAPTER(JsonConfigAdapter);
REGISTER_CONFIG_ADAPTER(YamlConfigAdapter);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "ConfigAdapterManager.h"
#include "ConfigAdapter.h"
#include <map>
namespace drogon
{
class ConfigAdapterManager
{
public:
static ConfigAdapterManager &instance();
Json::Value getJson(const std::string &configFile) const noexcept(false);
private:
ConfigAdapterManager();
std::map<std::string, ConfigAdapterPtr> adapters_;
};
} // namespace drogon

View File

@ -33,7 +33,10 @@
#define os_access access
#endif
#endif
#include <drogon/utils/Utilities.h>
#include "filesystem.h"
#include "ConfigAdapterManager.h"
using namespace drogon;
static bool bytesSize(std::string &sizeStr, size_t &size)
@ -101,6 +104,7 @@ static bool bytesSize(std::string &sizeStr, size_t &size)
return true;
}
}
ConfigLoader::ConfigLoader(const std::string &configFile)
{
if (os_access(drogon::utils::toNativePath(configFile).c_str(), 0) != 0)
@ -115,12 +119,8 @@ ConfigLoader::ConfigLoader(const std::string &configFile)
configFile_ = configFile;
try
{
std::ifstream infile(drogon::utils::toNativePath(configFile).c_str(),
std::ifstream::in);
if (infile)
{
infile >> configJsonRoot_;
}
auto filename = drogon::utils::toNativePath(configFile);
configJsonRoot_ = ConfigAdapterManager::instance().getJson(configFile);
}
catch (std::exception &e)
{

View File

@ -0,0 +1,21 @@
#include "JsonConfigAdapter.h"
#include <fstream>
using namespace drogon;
Json::Value JsonConfigAdapter::getJson(const std::string &configFile) const
noexcept(false)
{
Json::Value root;
Json::Reader reader;
std::ifstream in(configFile, std::ios::binary);
if (!in.is_open())
{
throw std::runtime_error("Cannot open config file!");
}
in >> root;
return root;
}
std::vector<std::string> JsonConfigAdapter::getExtensions() const
{
return {"json"};
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "ConfigAdapter.h"
namespace drogon
{
class JsonConfigAdapter : public ConfigAdapter
{
public:
JsonConfigAdapter() = default;
~JsonConfigAdapter() override = default;
Json::Value getJson(const std::string &configFile) const
noexcept(false) override;
std::vector<std::string> getExtensions() const override;
};
} // namespace drogon

View File

@ -0,0 +1,118 @@
#include "YamlConfigAdapter.h"
#ifdef HAS_YAML_CPP
#include <yaml-cpp/yaml.h>
#endif
using namespace drogon;
#ifdef HAS_YAML_CPP
namespace YAML
{
static bool yaml2json(const Node &node, Json::Value &jsonValue)
{
if (node.IsNull())
{
return false;
}
else if (node.IsScalar())
{
if (node.Tag() != "!")
{
try
{
jsonValue = node.as<int64_t>();
return true;
}
catch (const YAML::BadConversion &e)
{
}
try
{
jsonValue = node.as<double>();
return true;
}
catch (const YAML::BadConversion &e)
{
}
try
{
jsonValue = node.as<bool>();
return true;
}
catch (const YAML::BadConversion &e)
{
}
}
Json::Value v(node.Scalar());
jsonValue.swapPayload(v);
return true;
}
else if (node.IsSequence())
{
for (std::size_t i = 0; i < node.size(); i++)
{
Json::Value v;
if (yaml2json(node[i], v))
{
jsonValue.append(v);
}
else
{
return false;
}
}
return true;
}
else if (node.IsMap())
{
for (YAML::const_iterator it = node.begin(); it != node.end(); ++it)
{
Json::Value v;
if (yaml2json(it->second, v))
{
jsonValue[it->first.Scalar()] = v;
}
else
{
return false;
}
}
return true;
}
return false;
}
template <>
struct convert<Json::Value>
{
static bool decode(const Node &node, Json::Value &rhs)
{
return yaml2json(node, rhs);
};
};
} // namespace YAML
#endif
Json::Value YamlConfigAdapter::getJson(const std::string &configFile) const
noexcept(false)
{
#if HAS_YAML_CPP
// parse yaml file
YAML::Node config = YAML::LoadFile(configFile);
if (!config.IsNull())
{
return config.as<Json::Value>();
}
else
return Json::Value();
#else
throw std::runtime_error("please install yaml-cpp library");
#endif
}
std::vector<std::string> YamlConfigAdapter::getExtensions() const
{
return {"yaml", "yml"};
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "ConfigAdapter.h"
namespace drogon
{
class YamlConfigAdapter : public ConfigAdapter
{
public:
YamlConfigAdapter() = default;
~YamlConfigAdapter() override = default;
Json::Value getJson(const std::string &configFile) const
noexcept(false) override;
std::vector<std::string> getExtensions() const override;
};
} // namespace drogon