From c90efda16d9dd6342f65ed115b60b83ad2dc18c0 Mon Sep 17 00:00:00 2001 From: Pavla Kratochvilova Date: Thu, 30 Nov 2023 14:07:37 +0100 Subject: [PATCH] modules: Add module info command --- dnf5/commands/module/module.cpp | 2 +- dnf5/commands/module/module_info.cpp | 10 +- dnf5/commands/module/module_info.hpp | 12 ++- dnf5/commands/module/module_list.cpp | 26 ++++- dnf5/commands/module/module_list.hpp | 6 ++ include/libdnf5-cli/output/moduleinfo.hpp | 123 ++++++++++++++++++++++ include/libdnf5-cli/output/modulelist.hpp | 5 +- 7 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 include/libdnf5-cli/output/moduleinfo.hpp diff --git a/dnf5/commands/module/module.cpp b/dnf5/commands/module/module.cpp index e245a09808..a6438f9736 100644 --- a/dnf5/commands/module/module.cpp +++ b/dnf5/commands/module/module.cpp @@ -52,7 +52,7 @@ void ModuleCommand::register_subcommands() { query_commands_group->set_header("Query Commands:"); cmd.register_group(query_commands_group); register_subcommand(std::make_unique(get_context()), query_commands_group); - // register_subcommand(std::make_unique(get_context()), query_commands_group); + register_subcommand(std::make_unique(get_context()), query_commands_group); // register_subcommand(std::make_unique(get_context()), query_commands_group); // stream management commands diff --git a/dnf5/commands/module/module_info.cpp b/dnf5/commands/module/module_info.cpp index 2201dacbdf..4fecbc623a 100644 --- a/dnf5/commands/module/module_info.cpp +++ b/dnf5/commands/module/module_info.cpp @@ -19,13 +19,13 @@ along with libdnf. If not, see . #include "module_info.hpp" +#include +#include + namespace dnf5 { -void ModuleInfoCommand::set_argument_parser() { - auto & cmd = *get_argument_parser_command(); - cmd.set_description("Print details about module streams"); +void ModuleInfoCommand::print(const libdnf5::module::ModuleQuery & query) { + libdnf5::cli::output::print_moduleinfo_table(query.list()); } -void ModuleInfoCommand::run() {} - } // namespace dnf5 diff --git a/dnf5/commands/module/module_info.hpp b/dnf5/commands/module/module_info.hpp index d03a5b4ccb..80bb6e1ed2 100644 --- a/dnf5/commands/module/module_info.hpp +++ b/dnf5/commands/module/module_info.hpp @@ -20,15 +20,19 @@ along with libdnf. If not, see . #ifndef DNF5_COMMANDS_MODULE_MODULE_INFO_HPP #define DNF5_COMMANDS_MODULE_MODULE_INFO_HPP +#include "module_list.hpp" + #include +#include namespace dnf5 { -class ModuleInfoCommand : public Command { +class ModuleInfoCommand : public ModuleListCommand { public: - explicit ModuleInfoCommand(Context & context) : Command(context, "info") {} - void set_argument_parser() override; - void run() override; + explicit ModuleInfoCommand(Context & context) : ModuleListCommand(context, "info") {} + +private: + void print(const libdnf5::module::ModuleQuery & query) override; }; } // namespace dnf5 diff --git a/dnf5/commands/module/module_list.cpp b/dnf5/commands/module/module_list.cpp index 2689219b38..66d6e9f54b 100644 --- a/dnf5/commands/module/module_list.cpp +++ b/dnf5/commands/module/module_list.cpp @@ -21,6 +21,9 @@ along with libdnf. If not, see . #include #include +#include + +#include namespace dnf5 { @@ -44,15 +47,22 @@ void ModuleListCommand::configure() { void ModuleListCommand::run() { libdnf5::module::ModuleQuery query(get_context().base, true); auto module_specs_str = module_specs->get_value(); + std::set unmatched_module_spec; + if (module_specs_str.size() > 0) { for (const auto & module_spec : module_specs_str) { + bool module_spec_matched = false; for (const auto & nsvcap : libdnf5::module::Nsvcap::parse(module_spec)) { libdnf5::module::ModuleQuery nsvcap_query(get_context().base, false); nsvcap_query.filter_nsvca(nsvcap, libdnf5::sack::QueryCmp::GLOB); if (!nsvcap_query.empty()) { query |= nsvcap_query; + module_spec_matched = true; } } + if (!module_spec_matched) { + unmatched_module_spec.insert(module_spec); + } } } else { query = libdnf5::module::ModuleQuery(get_context().base, false); @@ -64,8 +74,20 @@ void ModuleListCommand::run() { query.filter_disabled(); } - output::print_modulelist_table(query.list()); - output::print_modulelist_table_hint(); + print(query); + if (!query.empty()) { + output::print_modulelist_table_hint(); + } + + if (!unmatched_module_spec.empty()) { + for (auto const & module_spec : unmatched_module_spec) { + std::cerr << libdnf5::utils::sformat(_("No matches found for \"{}\"."), module_spec) << std::endl; + } + } +} + +void ModuleListCommand::print(const libdnf5::module::ModuleQuery & query) { + libdnf5::cli::output::print_modulelist_table(query.list()); } } // namespace dnf5 diff --git a/dnf5/commands/module/module_list.hpp b/dnf5/commands/module/module_list.hpp index ed65362a98..6c67b2350b 100644 --- a/dnf5/commands/module/module_list.hpp +++ b/dnf5/commands/module/module_list.hpp @@ -23,6 +23,7 @@ along with libdnf. If not, see . #include "arguments.hpp" #include +#include namespace dnf5 { @@ -33,10 +34,15 @@ class ModuleListCommand : public Command { void configure() override; void run() override; +protected: + ModuleListCommand(Context & context, const std::string & name) : Command(context, name) {} + private: std::unique_ptr enabled{nullptr}; std::unique_ptr disabled{nullptr}; std::unique_ptr module_specs{nullptr}; + + virtual void print(const libdnf5::module::ModuleQuery & query); }; } // namespace dnf5 diff --git a/include/libdnf5-cli/output/moduleinfo.hpp b/include/libdnf5-cli/output/moduleinfo.hpp new file mode 100644 index 0000000000..dfd481575e --- /dev/null +++ b/include/libdnf5-cli/output/moduleinfo.hpp @@ -0,0 +1,123 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + + +#ifndef LIBDNF_CLI_OUTPUT_MODULEINFO_HPP +#define LIBDNF_CLI_OUTPUT_MODULEINFO_HPP + +#include "key_value_table.hpp" +#include "utils/string.hpp" + +#include "libdnf5-cli/tty.hpp" + +#include +#include +#include + +#include +#include + +namespace libdnf5::cli::output { + + +class ModuleInfo : public KeyValueTable { +public: + template + void add_module_item(ModuleItem & module_item); + +private: + void add_multiline_value(const char * key, const std::vector & multiline_value); +}; + + +void ModuleInfo::add_multiline_value(const char * key, const std::vector & values) { + if (values.empty()) { + add_line(key, ""); + return; + } + auto it = values.begin(); + // put the first item at the same line as description + add_line(key, it->c_str()); + it++; + + // put the remaining items on separate lines + for (; it != values.end(); it++) { + add_line("", it->c_str()); + } +} + +std::vector dependency_strings(std::vector dependencies) { + std::vector dependency_strings; + for (module::ModuleDependency & dependency : dependencies) { + dependency_strings.push_back(dependency.to_string()); + } + return dependency_strings; +} + +template +void ModuleInfo::add_module_item(ModuleItem & module_item) { + std::vector profile_names; + for (const module::ModuleProfile & profile : module_item.get_profiles()) { + profile_names.push_back(profile.get_name() + (profile.is_default() ? " [d]" : "")); + } + + std::vector dependency_strings; + for (module::ModuleDependency & dependency : module_item.get_module_dependencies()) { + dependency_strings.push_back(dependency.to_string()); + } + + // Get stream string (append [d] and [e] or [x] if needed) + const module::ModuleStatus & status = module_item.get_status(); + std::string stream_string = module_item.is_default() ? "[d]" : ""; + if (status == module::ModuleStatus::ENABLED) { + stream_string.append("[e]"); + } else if (status == module::ModuleStatus::DISABLED) { + stream_string.append("[x]"); + } + stream_string = stream_string.empty() ? module_item.get_stream() : module_item.get_stream() + " " + stream_string; + + add_line("Name", module_item.get_name()); + add_line("Stream", stream_string); + add_line("Version", module_item.get_version_str()); + add_line("Context", module_item.get_context()); + add_line("Architecture", module_item.get_arch()); + add_line("Profiles", utils::string::join(profile_names, ", ")); + add_line("Default profiles", utils::string::join(module_item.get_default_profiles(), ", ")); + add_line("Repo", module_item.get_repo_id()); + add_line("Summary", module_item.get_summary()); + add_line("Description", module_item.get_description()); + add_multiline_value("Requires", dependency_strings); + add_multiline_value("Artifacts", module_item.get_artifacts()); +} + + +template +void print_moduleinfo_table(Query & module_list) { + for (auto & module_item : module_list) { + libdnf5::cli::output::ModuleInfo module_info; + module_info.add_module_item(module_item); + module_info.print(); + std::cout << std::endl; + } +} + + +} // namespace libdnf5::cli::output + +#endif // LIBDNF_CLI_OUTPUT_MODULEINFO_HPP diff --git a/include/libdnf5-cli/output/modulelist.hpp b/include/libdnf5-cli/output/modulelist.hpp index 8958587896..c5032db204 100644 --- a/include/libdnf5-cli/output/modulelist.hpp +++ b/include/libdnf5-cli/output/modulelist.hpp @@ -39,7 +39,7 @@ namespace libdnf5::cli::output { enum { COL_MODULE_NAME, COL_MODULE_STREAM, COL_MODULE_PROFILES, COL_MODULE_SUMMARY }; -const std::string MODULELIST_TABLE_HINT = _("\nHint: [d]efault, [e]nabled, [x]disabled, [i]nstalled"); +const std::string MODULELIST_TABLE_HINT = _("Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled"); static struct libscols_table * create_modulelist_table() { @@ -108,6 +108,9 @@ void print_modulelist_table(Query & module_list) { } scols_sort_table(table, scols_table_get_column(table, COL_MODULE_NAME)); scols_print_table(table); + if (!module_list.empty()) { + std::cout << std::endl; + } scols_unref_table(table); }