👷 gucc: add parser for net/package profiles

This commit is contained in:
Vladislav Nepogodin 2024-07-09 01:35:01 +04:00
parent 2b708b3888
commit 6559b1412b
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
6 changed files with 233 additions and 1 deletions

View File

@ -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)

View File

@ -0,0 +1,37 @@
#ifndef PACKAGE_PROFILES_HPP
#define PACKAGE_PROFILES_HPP
#include <optional> // for optional
#include <string> // for string
#include <string_view> // for string_view
#include <vector> // for vector
namespace gucc::profile {
struct BaseProfiles {
std::vector<std::string> base_packages{};
std::vector<std::string> base_desktop_packages{};
};
struct DesktopProfile {
std::string profile_name{};
std::vector<std::string> packages{};
};
struct NetProfiles {
BaseProfiles base_profiles{};
std::vector<DesktopProfile> desktop_profiles{};
};
// Parse base profiles
auto parse_base_profiles(std::string_view config_content) noexcept -> std::optional<BaseProfiles>;
// Parse desktop profiles
auto parse_desktop_profiles(std::string_view config_content) noexcept -> std::optional<std::vector<DesktopProfile>>;
// Parse net profiles
auto parse_net_profiles(std::string_view config_content) noexcept -> std::optional<NetProfiles>;
} // namespace gucc::profile
#endif // PACKAGE_PROFILES_HPP

View File

@ -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

View File

@ -0,0 +1,82 @@
#include "gucc/package_profiles.hpp"
#include <spdlog/spdlog.h>
#define TOML_EXCEPTIONS 0 // disable exceptions
#include <toml++/toml.h>
using namespace std::string_view_literals;
namespace {
inline void parse_toml_array(const toml::array* arr, std::vector<std::string>& vec) noexcept {
for (const auto& node_el : *arr) {
auto elem = node_el.value<std::string_view>().value();
vec.emplace_back(elem);
}
}
} // namespace
namespace gucc::profile {
auto parse_base_profiles(std::string_view config_content) noexcept -> std::optional<BaseProfiles> {
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<BaseProfiles>(std::move(base_profiles));
}
auto parse_desktop_profiles(std::string_view config_content) noexcept -> std::optional<std::vector<DesktopProfile>> {
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<DesktopProfile> desktop_profiles{};
auto desktop_table = netprof_table["desktop"].as_table();
for (auto&& [key, value] : *desktop_table) {
auto value_table = *value.as_table();
std::vector<std::string> 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::vector<DesktopProfile>>(std::move(desktop_profiles));
}
auto parse_net_profiles(std::string_view config_content) noexcept -> std::optional<NetProfiles> {
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<std::string> 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<NetProfiles>(std::move(net_profiles));
}
} // namespace gucc::profile

View File

@ -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)

View File

@ -0,0 +1,103 @@
#include "gucc/file_utils.hpp"
#include "gucc/package_profiles.hpp"
#include <cassert>
#include <filesystem>
#include <fstream>
#include <string_view>
#include <fmt/core.h>
#include <spdlog/sinks/callback_sink.h>
#include <spdlog/spdlog.h>
#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 <range/v3/range/conversion.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/split.hpp>
#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<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg&) {
// noop
});
spdlog::set_default_logger(std::make_shared<spdlog::logger>("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<std::string>{"a", "b"}));
assert((base_profs->base_desktop_packages == std::vector<std::string>{"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<std::string>{"ca", "da", "fa"}));
assert(((*base_desktop_profs)[1].profile_name == "someprofile-2"));
assert(((*base_desktop_profs)[1].packages == std::vector<std::string>{"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<std::string>{"a", "b"}));
assert((net_profs->base_profiles.base_desktop_packages == std::vector<std::string>{"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<std::string>{"ca", "da", "fa"}));
assert((net_profs->desktop_profiles[1].profile_name == "someprofile-2"));
assert((net_profs->desktop_profiles[1].packages == std::vector<std::string>{"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);
}
}