From 6559b1412beddcad6176b65ea7fe6d95b4cc3970 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Tue, 9 Jul 2024 01:35:01 +0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20gucc:=20add=20parser=20for=20net?= =?UTF-8?q?/package=20profiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gucc/CMakeLists.txt | 3 +- gucc/include/gucc/package_profiles.hpp | 37 +++++++++ gucc/meson.build | 1 + gucc/src/package_profiles.cpp | 82 ++++++++++++++++++++ gucc/tests/meson.build | 8 ++ gucc/tests/unit-package_profiles.cpp | 103 +++++++++++++++++++++++++ 6 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 gucc/include/gucc/package_profiles.hpp create mode 100644 gucc/src/package_profiles.cpp create mode 100644 gucc/tests/unit-package_profiles.cpp diff --git a/gucc/CMakeLists.txt b/gucc/CMakeLists.txt index a121ef9..b7f8f7e 100644 --- a/gucc/CMakeLists.txt +++ b/gucc/CMakeLists.txt @@ -30,13 +30,14 @@ add_library(${PROJECT_NAME} SHARED src/mtab.cpp include/gucc/mtab.hpp src/umount_partitions.cpp include/gucc/umount_partitions.hpp src/hwclock.cpp include/gucc/hwclock.hpp + src/package_profiles.cpp include/gucc/package_profiles.hpp #src/chwd_profiles.cpp src/chwd_profiles.hpp #src/disk.cpp src/disk.hpp ) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_DIR}/include) -target_link_libraries(${PROJECT_NAME} PUBLIC project_warnings project_options spdlog::spdlog fmt::fmt range-v3::range-v3) +target_link_libraries(${PROJECT_NAME} PUBLIC project_warnings project_options spdlog::spdlog fmt::fmt range-v3::range-v3 tomlplusplus::tomlplusplus) if(COS_INSTALLER_BUILD_TESTS) add_subdirectory(tests) diff --git a/gucc/include/gucc/package_profiles.hpp b/gucc/include/gucc/package_profiles.hpp new file mode 100644 index 0000000..3b1ef22 --- /dev/null +++ b/gucc/include/gucc/package_profiles.hpp @@ -0,0 +1,37 @@ +#ifndef PACKAGE_PROFILES_HPP +#define PACKAGE_PROFILES_HPP + +#include // for optional +#include // for string +#include // for string_view +#include // for vector + +namespace gucc::profile { + +struct BaseProfiles { + std::vector base_packages{}; + std::vector base_desktop_packages{}; +}; + +struct DesktopProfile { + std::string profile_name{}; + std::vector packages{}; +}; + +struct NetProfiles { + BaseProfiles base_profiles{}; + std::vector desktop_profiles{}; +}; + +// Parse base profiles +auto parse_base_profiles(std::string_view config_content) noexcept -> std::optional; + +// Parse desktop profiles +auto parse_desktop_profiles(std::string_view config_content) noexcept -> std::optional>; + +// Parse net profiles +auto parse_net_profiles(std::string_view config_content) noexcept -> std::optional; + +} // namespace gucc::profile + +#endif // PACKAGE_PROFILES_HPP diff --git a/gucc/meson.build b/gucc/meson.build index 08d7d37..1627016 100644 --- a/gucc/meson.build +++ b/gucc/meson.build @@ -20,6 +20,7 @@ gucc_lib = library('gucc', 'src/mtab.cpp', 'src/umount_partitions.cpp', 'src/hwclock.cpp', + 'src/package_profiles.cpp', ], include_directories : [include_directories('include')], dependencies: deps diff --git a/gucc/src/package_profiles.cpp b/gucc/src/package_profiles.cpp new file mode 100644 index 0000000..1dfb292 --- /dev/null +++ b/gucc/src/package_profiles.cpp @@ -0,0 +1,82 @@ +#include "gucc/package_profiles.hpp" + +#include + +#define TOML_EXCEPTIONS 0 // disable exceptions +#include + +using namespace std::string_view_literals; + +namespace { + +inline void parse_toml_array(const toml::array* arr, std::vector& vec) noexcept { + for (const auto& node_el : *arr) { + auto elem = node_el.value().value(); + vec.emplace_back(elem); + } +} + +} // namespace + +namespace gucc::profile { + +auto parse_base_profiles(std::string_view config_content) noexcept -> std::optional { + toml::parse_result netprof = toml::parse(config_content); + if (netprof.failed()) { + spdlog::error("Failed to parse profiles: {}", netprof.error().description()); + return std::nullopt; + } + const auto& netprof_table = std::move(netprof).table(); + + BaseProfiles base_profiles{}; + parse_toml_array(netprof_table["base-packages"]["packages"].as_array(), base_profiles.base_packages); + parse_toml_array(netprof_table["base-packages"]["desktop"]["packages"].as_array(), base_profiles.base_desktop_packages); + return std::make_optional(std::move(base_profiles)); +} + +auto parse_desktop_profiles(std::string_view config_content) noexcept -> std::optional> { + toml::parse_result netprof = toml::parse(config_content); + if (netprof.failed()) { + spdlog::error("Failed to parse profiles: {}", netprof.error().description()); + return std::nullopt; + } + const auto& netprof_table = std::move(netprof).table(); + + std::vector desktop_profiles{}; + + auto desktop_table = netprof_table["desktop"].as_table(); + for (auto&& [key, value] : *desktop_table) { + auto value_table = *value.as_table(); + std::vector desktop_profile_packages{}; + parse_toml_array(value_table["packages"].as_array(), desktop_profile_packages); + desktop_profiles.emplace_back(DesktopProfile{.profile_name = std::string{std::string_view{key}}, .packages = std::move(desktop_profile_packages)}); + } + return std::make_optional>(std::move(desktop_profiles)); +} + +auto parse_net_profiles(std::string_view config_content) noexcept -> std::optional { + toml::parse_result netprof = toml::parse(config_content); + if (netprof.failed()) { + spdlog::error("Failed to parse profiles: {}", netprof.error().description()); + return std::nullopt; + } + const auto& netprof_table = std::move(netprof).table(); + + NetProfiles net_profiles{}; + + // parse base + parse_toml_array(netprof_table["base-packages"]["packages"].as_array(), net_profiles.base_profiles.base_packages); + parse_toml_array(netprof_table["base-packages"]["desktop"]["packages"].as_array(), net_profiles.base_profiles.base_desktop_packages); + + // parse desktop + auto desktop_table = netprof_table["desktop"].as_table(); + for (auto&& [key, value] : *desktop_table) { + auto value_table = *value.as_table(); + std::vector desktop_profile_packages{}; + parse_toml_array(value_table["packages"].as_array(), desktop_profile_packages); + net_profiles.desktop_profiles.emplace_back(DesktopProfile{.profile_name = std::string{std::string_view{key}}, .packages = std::move(desktop_profile_packages)}); + } + return std::make_optional(std::move(net_profiles)); +} + +} // namespace gucc::profile diff --git a/gucc/tests/meson.build b/gucc/tests/meson.build index c1243e3..99116c0 100644 --- a/gucc/tests/meson.build +++ b/gucc/tests/meson.build @@ -53,3 +53,11 @@ executable( link_with: [gucc_lib], include_directories: [include_directories('../include')], install: false) + +executable( + 'test-package_profiles', + files('unit-package_profiles.cpp'), + dependencies: deps, + link_with: [gucc_lib], + include_directories: [include_directories('../include')], + install: false) diff --git a/gucc/tests/unit-package_profiles.cpp b/gucc/tests/unit-package_profiles.cpp new file mode 100644 index 0000000..72af311 --- /dev/null +++ b/gucc/tests/unit-package_profiles.cpp @@ -0,0 +1,103 @@ +#include "gucc/file_utils.hpp" +#include "gucc/package_profiles.hpp" + +#include + +#include +#include +#include + +#include + +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#pragma GCC diagnostic ignored "-Wuseless-cast" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +namespace fs = std::filesystem; +using namespace std::string_view_literals; + +static constexpr auto VALID_PROFILE_TEST = R"( +[base-packages] +packages = ["a","b"] +[base-packages.desktop] +packages = ["c","d","f"] +[desktop.someprofile-1] +packages = ["ca","da","fa"] +[desktop.someprofile-2] +packages = ["cb","db","fb"] +)"sv; + +static constexpr auto INVALID_PROFILE_TEST = R"( +[base-packages] +pacages = ["a,"b"] +[base-packages.desktop +package = c","d",f"] +[desktop.someprofile-1] +pacages = ["ca,"da","fa" +[desktop.someprofile-2] +packaes = ["cb","db",fb"] +)"sv; + +int main() { + auto callback_sink = std::make_shared([](const spdlog::details::log_msg&) { + // noop + }); + spdlog::set_default_logger(std::make_shared("default", callback_sink)); + + // valid profile + { + auto base_profs = gucc::profile::parse_base_profiles(VALID_PROFILE_TEST); + assert(base_profs); + assert((base_profs->base_packages == std::vector{"a", "b"})); + assert((base_profs->base_desktop_packages == std::vector{"c", "d", "f"})); + + auto base_desktop_profs = gucc::profile::parse_desktop_profiles(VALID_PROFILE_TEST); + assert(base_desktop_profs); + assert(base_desktop_profs->size() == 2); + assert(((*base_desktop_profs)[0].profile_name == "someprofile-1")); + assert(((*base_desktop_profs)[0].packages == std::vector{"ca", "da", "fa"})); + assert(((*base_desktop_profs)[1].profile_name == "someprofile-2")); + assert(((*base_desktop_profs)[1].packages == std::vector{"cb", "db", "fb"})); + + auto net_profs = gucc::profile::parse_net_profiles(VALID_PROFILE_TEST); + assert(net_profs); + assert((net_profs->base_profiles.base_packages == std::vector{"a", "b"})); + assert((net_profs->base_profiles.base_desktop_packages == std::vector{"c", "d", "f"})); + assert(net_profs->desktop_profiles.size() == 2); + assert((net_profs->desktop_profiles[0].profile_name == "someprofile-1")); + assert((net_profs->desktop_profiles[0].packages == std::vector{"ca", "da", "fa"})); + assert((net_profs->desktop_profiles[1].profile_name == "someprofile-2")); + assert((net_profs->desktop_profiles[1].packages == std::vector{"cb", "db", "fb"})); + } + // invalid profile + { + auto base_profs = gucc::profile::parse_base_profiles(INVALID_PROFILE_TEST); + assert(!base_profs); + + auto base_desktop_profs = gucc::profile::parse_desktop_profiles(INVALID_PROFILE_TEST); + assert(!base_desktop_profs); + + auto net_profs = gucc::profile::parse_net_profiles(INVALID_PROFILE_TEST); + assert(!net_profs); + } +}