From c9a3ebd623bf2f968f7fbdf5bb2d7dda480b9f1c Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 18 Jan 2026 18:44:22 +0100 Subject: [PATCH] Parse command line with boost program_options --- cli/CMakeLists.txt | 14 +------ cli/command_line.cpp | 73 +++++++++++++++++++++++++++++++++++ cli/component.cpp | 89 ------------------------------------------- cli/configuration.cpp | 20 ++++++++-- cli/main.cpp | 36 +++++++++++------ 5 files changed, 116 insertions(+), 116 deletions(-) create mode 100644 cli/command_line.cpp delete mode 100644 cli/component.cpp diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 3dbb7c6..ce8005d 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -2,32 +2,22 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -FetchContent_Declare(ftxui - GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui - GIT_TAG v6.1.9 - GIT_PROGRESS TRUE - GIT_SHALLOW TRUE - EXCLUDE_FROM_ALL -) FetchContent_Declare(toml11 GIT_REPOSITORY https://github.com/ToruNiina/toml11.git GIT_TAG v4.4.0 GIT_PROGRESS TRUE ) -FetchContent_MakeAvailable(ftxui toml11) +FetchContent_MakeAvailable(toml11) find_package(Boost CONFIG COMPONENTS program_options REQUIRED) add_executable(katja-cli main.cpp) target_sources(katja-cli PUBLIC FILE_SET all_my_modules TYPE CXX_MODULES FILES - component.cpp configuration.cpp + command_line.cpp configuration.cpp ) target_include_directories(katja-cli PRIVATE ${Boost_INCLUDE_DIR}) target_link_libraries(katja-cli PUBLIC katja - PRIVATE ftxui::screen - PRIVATE ftxui::dom - PRIVATE ftxui::component PRIVATE toml11::toml11 PRIVATE Boost::program_options ) diff --git a/cli/command_line.cpp b/cli/command_line.cpp new file mode 100644 index 0000000..ff6c928 --- /dev/null +++ b/cli/command_line.cpp @@ -0,0 +1,73 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +module; + +#include +#include +#include +#include +#include + +#include + +export module katja.command_line; + +namespace katja +{ + export enum class command + { + updates, + search, + help + }; + + export struct command_line + { + const command type; + const std::vector arguments; + }; + + export class invalid_command_error : public std::runtime_error + { + const std::string command; + + public: + invalid_command_error() + : std::runtime_error("Expecting the command line to begin with a command.") + { + } + + invalid_command_error(const std::string& invalid_command) + : command{ invalid_command }, + std::runtime_error("Unknown command given on the command line.") + { + } + }; + + export command_line parse_command_line(int argc, const char **argv) + { + if (argc == 1) + { + throw invalid_command_error(); + } + std::vector arguments; + std::copy(argv + 2, argv + argc, std::back_inserter(arguments)); + + if (strcmp("updates", argv[1]) == 0) + { + return command_line{ command::updates, std::move(arguments) }; + } + if (strcmp("search", argv[1]) == 0) + { + return command_line{ command::search, std::move(arguments) }; + } + if (strcmp("help", argv[1]) == 0) + { + return command_line{ command::help, std::move(arguments) }; + } + throw invalid_command_error(argv[1]); + } +} diff --git a/cli/component.cpp b/cli/component.cpp deleted file mode 100644 index 6735077..0000000 --- a/cli/component.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -module; - -#include -#include - -#include -#include -#include - -import katja.repository; - -export module katja.component; - -export namespace katja -{ - class PackageListBase : public ftxui::ComponentBase - { - std::string title; - const std::vector packages; - std::optional selected; - - public: - PackageListBase(const std::string& title, const std::vector& packages = {}) - : title(title), packages(packages) - { - } - - ftxui::Element OnRender() override - { - std::vector lines; - - for (const auto& package_identifier : this->packages) - { - auto line = ftxui::text(package_identifier.to_string()) | color(ftxui::Color::SkyBlue2); - lines.push_back(line); - } - if (this->selected.has_value() && this->selected.value() < lines.size()) - { - lines[this->selected.value()] |= ftxui::focus; - } - std::stringstream summary; - - summary << title << '(' << packages.size() << ')'; - - return ftxui::window(ftxui::text(summary.str()), ftxui::vbox(lines) | ftxui::yframe); - } - - bool OnEvent(ftxui::Event event) override - { - if (event == ftxui::Event::ArrowDown) - { - if (!this->selected.has_value() && !this->packages.empty()) - { - this->selected = std::make_optional(0); - } - else if (this->selected.has_value() && this->selected.value() + 1 < this->packages.size()) - { - this->selected = std::make_optional(this->selected.value() + 1); - } - return true; - } - else if (event == ftxui::Event::ArrowUp) - { - if (!this->selected.has_value() && !this->packages.empty()) - { - this->selected = std::make_optional(0); - } - else if (this->selected.has_value() - && this->selected.value() < this->packages.size() - && this->selected.value() > 0) - { - this->selected = std::make_optional(this->selected.value() - 1); - } - return true; - } - return false; - } - }; - - ftxui::Component PackageList(const std::string& title, const std::vector& packages = {}) - { - return ftxui::Make(title, packages); - } -} diff --git a/cli/configuration.cpp b/cli/configuration.cpp index 45669b3..4ca36c1 100644 --- a/cli/configuration.cpp +++ b/cli/configuration.cpp @@ -7,6 +7,7 @@ module; #include #include +#include #include @@ -15,8 +16,6 @@ import katja.repository; export module katja.configuration; -import katja.component; - export namespace katja { struct repository_configuration @@ -58,17 +57,30 @@ export namespace katja return this->repositories.cend(); } }; + + class configuration_error : public std::runtime_error + { + public: + configuration_error() + : std::runtime_error("Configuration is expected to be a table of repositories.") + { + } + }; } TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(katja::repository_configuration, path); export namespace katja { - configuration read_configuration() + configuration read_configuration(const std::string& configuration_file) { - auto raw_root = toml::parse("katja.toml"); + auto raw_root = toml::parse(configuration_file); configuration result; + if (!raw_root.is_table()) + { + throw configuration_error(); + } for (const auto& [root_name, root_value] : raw_root.as_table()) { auto repository_value = toml::get(root_value); diff --git a/cli/main.cpp b/cli/main.cpp index 8a8a377..da0d1b8 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -3,18 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -module; - #include #include #include -#include +#include import katja.database; import katja.repository; import katja.sbo; import katja.configuration; +import katja.command_line; static void get_updates(std::shared_ptr repository, katja::package_database&& installed_database) { @@ -39,14 +38,11 @@ static void search_names(std::shared_ptr repository, const st int main(int argc, const char **argv) { - katja::configuration configuration = katja::read_configuration(); + katja::configuration configuration = katja::read_configuration("katja.toml"); katja::package_database installed_database = katja::read_installed_database(); - if (argc == 1) - { - return EXIT_FAILURE; - } - if (strcmp("updates", argv[1]) == 0) + katja::command_line command_line = katja::parse_command_line(argc, argv); + if (command_line.type == katja::command::updates) { for (const auto& [repository_name, repository_configuration] : configuration) { @@ -56,14 +52,32 @@ int main(int argc, const char **argv) get_updates(repository, std::move(installed_database)); } } - else if (strcmp("search", argv[1]) == 0 && argc == 3) + else if (command_line.type == katja::command::search) { + boost::program_options::options_description search_description("Search packages"); + search_description + .add_options()("name", "Find packages by name"); + + boost::program_options::positional_options_description positional_arguments; + positional_arguments.add("name", 1); + + boost::program_options::variables_map option_map; + auto command_parser = boost::program_options::command_line_parser(command_line.arguments) + .options(search_description).positional(positional_arguments); + boost::program_options::store(command_parser.run(), option_map); + boost::program_options::notify(option_map); + + if (!option_map.count("name")) + { + std::cerr << search_description; + return EXIT_FAILURE; + } for (const auto& [repository_name, repository_configuration] : configuration) { std::filesystem::path slackbuild_repository{ repository_configuration.path }; auto repository = std::make_shared(slackbuild_repository); - search_names(repository, std::string(argv[2])); + search_names(repository, option_map["name"].as()); } }