diff --git a/dnf5-plugins/automatic_plugin/automatic.cpp b/dnf5-plugins/automatic_plugin/automatic.cpp index 8471dd8cc..b5eb4caec 100644 --- a/dnf5-plugins/automatic_plugin/automatic.cpp +++ b/dnf5-plugins/automatic_plugin/automatic.cpp @@ -24,6 +24,7 @@ along with libdnf. If not, see . #include "transaction_callbacks_simple.hpp" #include +#include #include #include #include @@ -336,19 +337,26 @@ void AutomaticCommand::run() { // print resolve logs and the transaction table to the output stream { output_stream << std::endl << _("Resolved transaction:") << std::endl; - libdnf5::cli::output::print_resolve_logs(transaction, output_stream); + libdnf5::cli::output::TransactionAdapter cli_output_transaction(*context.get_transaction()); + libdnf5::cli::output::print_resolve_logs( + static_cast(cli_output_transaction), output_stream); if (!transaction.empty()) { - libdnf5::cli::output::TransactionSummary summary; - auto tt = libdnf5::cli::output::create_transaction_table(transaction, summary); - scols_table_enable_colors(*tt, false); - scols_table_set_termwidth(*tt, 80); - char * tt_string = nullptr; - scols_print_table_to_string(*tt, &tt_string); - output_stream << tt_string << std::endl; + char * tt_string; + size_t size; + auto * fd = open_memstream(&tt_string, &size); + { + libdnf5::cli::output::TransactionTable table( + static_cast(cli_output_transaction)); + table.set_colors_enabled(false); + table.set_term_width(80); + table.set_output_stream(fd); + table.print_table(); + table.print_summary(); + } + fclose(fd); + output_stream << tt_string; free(tt_string); - - summary.print(output_stream); } } diff --git a/dnf5/commands/advisory/advisory_info.cpp b/dnf5/commands/advisory/advisory_info.cpp index af68015ec..ad833889f 100644 --- a/dnf5/commands/advisory/advisory_info.cpp +++ b/dnf5/commands/advisory/advisory_info.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "advisory_info.hpp" +#include #include #include @@ -62,7 +63,8 @@ void AdvisoryInfoCommand::process_and_print_queries( for (auto advisory : advisories) { libdnf5::cli::output::AdvisoryInfo advisory_info; - advisory_info.add_advisory(advisory); + output::AdvisoryAdapter cli_advisory(advisory); + advisory_info.add_advisory(cli_advisory); advisory_info.print(); std::cout << std::endl; } diff --git a/dnf5/commands/advisory/advisory_list.cpp b/dnf5/commands/advisory/advisory_list.cpp index b41cb4703..82aff84ae 100644 --- a/dnf5/commands/advisory/advisory_list.cpp +++ b/dnf5/commands/advisory/advisory_list.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "advisory_list.hpp" +#include #include #include #include @@ -63,12 +64,22 @@ void AdvisoryListCommand::process_and_print_queries( not_installed_pkgs = advisories.get_advisory_packages_sorted(installed_packages, libdnf5::sack::QueryCmp::GT); } + std::vector> cli_installed_pkgs; + std::vector> cli_not_installed_pkgs; if (with_bz->get_value()) { libdnf5::cli::output::print_advisorylist_references_table(not_installed_pkgs, installed_pkgs, "bugzilla"); } else if (with_cve->get_value()) { libdnf5::cli::output::print_advisorylist_references_table(not_installed_pkgs, installed_pkgs, "cve"); } else { - libdnf5::cli::output::print_advisorylist_table(not_installed_pkgs, installed_pkgs); + cli_installed_pkgs.reserve(installed_pkgs.size()); + for (const auto & obj : installed_pkgs) { + cli_installed_pkgs.emplace_back(new output::AdvisoryPackageAdapter(obj)); + } + cli_not_installed_pkgs.reserve(not_installed_pkgs.size()); + for (const auto & obj : not_installed_pkgs) { + cli_not_installed_pkgs.emplace_back(new output::AdvisoryPackageAdapter(obj)); + } + libdnf5::cli::output::print_advisorylist_table(cli_not_installed_pkgs, cli_installed_pkgs); } } diff --git a/dnf5/commands/environment/environment_info.cpp b/dnf5/commands/environment/environment_info.cpp index 7a9e0eeb3..622cb942e 100644 --- a/dnf5/commands/environment/environment_info.cpp +++ b/dnf5/commands/environment/environment_info.cpp @@ -19,11 +19,14 @@ along with libdnf. If not, see . #include "environment_info.hpp" +#include #include #include #include #include +#include + namespace dnf5 { using namespace libdnf5::cli; @@ -67,7 +70,8 @@ void EnvironmentInfoCommand::run() { } for (auto environment : query.list()) { - libdnf5::cli::output::print_environmentinfo_table(environment); + libdnf5::cli::output::EnvironmentAdapter cli_env(environment); + libdnf5::cli::output::print_environmentinfo_table(cli_env); std::cout << '\n'; } } diff --git a/dnf5/commands/environment/environment_list.cpp b/dnf5/commands/environment/environment_list.cpp index e2522da23..5d875bf6b 100644 --- a/dnf5/commands/environment/environment_list.cpp +++ b/dnf5/commands/environment/environment_list.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "environment_list.hpp" +#include #include #include #include @@ -66,7 +67,11 @@ void EnvironmentListCommand::run() { query.filter_installed(false); } - libdnf5::cli::output::print_environmentlist_table(query.list()); + std::vector> cli_envs; + for (auto & env : query.list()) { + cli_envs.emplace_back(new libdnf5::cli::output::EnvironmentAdapter(env)); + } + libdnf5::cli::output::print_environmentlist_table(cli_envs); } } // namespace dnf5 diff --git a/dnf5/commands/group/group_info.cpp b/dnf5/commands/group/group_info.cpp index bb211f6a0..40dce3f0f 100644 --- a/dnf5/commands/group/group_info.cpp +++ b/dnf5/commands/group/group_info.cpp @@ -19,15 +19,19 @@ along with libdnf. If not, see . #include "group_info.hpp" +#include #include +#include + namespace dnf5 { using namespace libdnf5::cli; void GroupInfoCommand::print(const libdnf5::comps::GroupQuery & query) { for (auto group : query.list()) { - libdnf5::cli::output::print_groupinfo_table(group); + libdnf5::cli::output::GroupAdapter cli_group(group); + libdnf5::cli::output::print_groupinfo_table(cli_group); std::cout << '\n'; } } diff --git a/dnf5/commands/group/group_list.cpp b/dnf5/commands/group/group_list.cpp index 03ec0d2dd..b28f48582 100644 --- a/dnf5/commands/group/group_list.cpp +++ b/dnf5/commands/group/group_list.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "group_list.hpp" +#include #include #include #include @@ -90,7 +91,12 @@ void GroupListCommand::run() { } void GroupListCommand::print(const libdnf5::comps::GroupQuery & query) { - libdnf5::cli::output::print_grouplist_table(query.list()); + std::vector> items; + items.reserve(query.list().size()); + for (auto & obj : query.list()) { + items.emplace_back(new libdnf5::cli::output::GroupAdapter(obj)); + } + libdnf5::cli::output::print_grouplist_table(items); } } // namespace dnf5 diff --git a/dnf5/commands/module/module_info.cpp b/dnf5/commands/module/module_info.cpp index 18eedd836..001f50d1b 100644 --- a/dnf5/commands/module/module_info.cpp +++ b/dnf5/commands/module/module_info.cpp @@ -19,13 +19,19 @@ along with libdnf. If not, see . #include "module_info.hpp" +#include #include #include namespace dnf5 { void ModuleInfoCommand::print(const libdnf5::module::ModuleQuery & query) { - libdnf5::cli::output::print_moduleinfo_table(query.list()); + std::vector> items; + items.reserve(query.list().size()); + for (auto & obj : query.list()) { + items.emplace_back(new libdnf5::cli::output::ModuleItemAdapter(obj)); + } + libdnf5::cli::output::print_moduleinfo_table(items); } void ModuleInfoCommand::print_hint() { diff --git a/dnf5/commands/module/module_list.cpp b/dnf5/commands/module/module_list.cpp index 66318ffde..75972a73f 100644 --- a/dnf5/commands/module/module_list.cpp +++ b/dnf5/commands/module/module_list.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "module_list.hpp" +#include #include #include #include @@ -87,7 +88,12 @@ void ModuleListCommand::run() { } void ModuleListCommand::print(const libdnf5::module::ModuleQuery & query) { - libdnf5::cli::output::print_modulelist_table(query.list()); + std::vector> items; + items.reserve(query.list().size()); + for (auto & obj : query.list()) { + items.emplace_back(new output::ModuleItemAdapter(obj)); + } + libdnf5::cli::output::print_modulelist_table(items); } void ModuleListCommand::print_hint() { diff --git a/dnf5/commands/provides/provides.cpp b/dnf5/commands/provides/provides.cpp index 389e1296e..5a055afee 100644 --- a/dnf5/commands/provides/provides.cpp +++ b/dnf5/commands/provides/provides.cpp @@ -16,13 +16,13 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libdnf. If not, see . */ -#include "libdnf5-cli/output/provides.hpp" - #include "provides.hpp" #include "libdnf5/common/sack/query_cmp.hpp" #include "libdnf5/conf/option_string.hpp" +#include +#include #include #include @@ -116,7 +116,8 @@ void ProvidesCommand::run() { auto matched = filter_spec(spec, full_package_query); for (auto package : matched.first) { if (matched.second != libdnf5::cli::output::ProvidesMatchedBy::NO_MATCH) { - libdnf5::cli::output::print_provides_table(package, spec.c_str(), matched.second); + libdnf5::cli::output::PackageAdapter cli_package(package); + libdnf5::cli::output::print_provides_table(cli_package, spec.c_str(), matched.second); any_match = true; } else { unmatched_specs.insert(spec); diff --git a/dnf5/commands/repo/repo_info.cpp b/dnf5/commands/repo/repo_info.cpp index 6f9331f64..b2cd3a347 100644 --- a/dnf5/commands/repo/repo_info.cpp +++ b/dnf5/commands/repo/repo_info.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "repo_info.hpp" +#include #include #include @@ -26,60 +27,68 @@ along with libdnf. If not, see . namespace dnf5 { -struct RepoInfoWrapper { +class RepoInfoWrapper : public libdnf5::cli::output::IRepoInfo { public: RepoInfoWrapper(libdnf5::repo::Repo & repo, uint64_t size, uint64_t pkgs, uint64_t available_pkgs) - : repo(repo), + : repo(&repo), size(size), pkgs(pkgs), available_pkgs(available_pkgs) {} - std::string get_id() const { return repo.get_id(); } - std::string get_name() const { return repo.get_name(); } - std::string get_type() const { return repo.type_to_string(repo.get_type()); } - bool is_enabled() const { return repo.is_enabled(); } - int get_priority() const { return repo.get_config().get_priority_option().get_value(); } - int get_cost() const { return repo.get_config().get_cost_option().get_value(); } - std::vector get_baseurl() const { return repo.get_config().get_baseurl_option().get_value(); } - std::string get_metalink() const { - auto & option = repo.get_config().get_metalink_option(); + std::string get_id() const override { return repo->get_id(); } + std::string get_name() const override { return repo->get_name(); } + std::string get_type() const override { return libdnf5::repo::Repo::type_to_string(repo->get_type()); } + bool is_enabled() const override { return repo->is_enabled(); } + int get_priority() const override { return repo->get_config().get_priority_option().get_value(); } + int get_cost() const override { return repo->get_config().get_cost_option().get_value(); } + std::vector get_baseurl() const override { + return repo->get_config().get_baseurl_option().get_value(); + } + std::string get_metalink() const override { + auto & option = repo->get_config().get_metalink_option(); if (option.empty()) { return ""; } else { return option.get_value(); } } - std::string get_mirrorlist() const { - auto & option = repo.get_config().get_mirrorlist_option(); + std::string get_mirrorlist() const override { + auto & option = repo->get_config().get_mirrorlist_option(); if (option.empty()) { return ""; } else { return option.get_value(); } } - int get_metadata_expire() const { return repo.get_config().get_metadata_expire_option().get_value(); } - std::vector get_excludepkgs() const { return repo.get_config().get_excludepkgs_option().get_value(); } - std::vector get_includepkgs() const { - return repo.get_config().get_includepkgs_option().get_value(); + int get_metadata_expire() const override { return repo->get_config().get_metadata_expire_option().get_value(); } + std::vector get_excludepkgs() const override { + return repo->get_config().get_excludepkgs_option().get_value(); + } + std::vector get_includepkgs() const override { + return repo->get_config().get_includepkgs_option().get_value(); ; } - bool get_skip_if_unavailable() const { return repo.get_config().get_skip_if_unavailable_option().get_value(); } - std::vector get_gpgkey() const { return repo.get_config().get_gpgkey_option().get_value(); } - bool get_gpgcheck() const { return repo.get_config().get_gpgcheck_option().get_value(); } - bool get_repo_gpgcheck() const { return repo.get_config().get_repo_gpgcheck_option().get_value(); } - std::string get_repo_file_path() const { return repo.get_repo_file_path(); } - std::string get_revision() const { return repo.get_revision(); } - std::vector get_content_tags() const { return repo.get_content_tags(); } - std::vector> get_distro_tags() const { return repo.get_distro_tags(); } - int64_t get_timestamp() const { return repo.get_timestamp(); } - int get_max_timestamp() const { return repo.get_max_timestamp(); } - uint64_t get_size() const { return size; } - uint64_t get_pkgs() const { return pkgs; } - uint64_t get_available_pkgs() const { return available_pkgs; } - std::vector get_mirrors() const { return repo.get_mirrors(); } + bool get_skip_if_unavailable() const override { + return repo->get_config().get_skip_if_unavailable_option().get_value(); + } + std::vector get_gpgkey() const override { return repo->get_config().get_gpgkey_option().get_value(); } + bool get_gpgcheck() const override { return repo->get_config().get_gpgcheck_option().get_value(); } + bool get_repo_gpgcheck() const override { return repo->get_config().get_repo_gpgcheck_option().get_value(); } + std::string get_repo_file_path() const override { return repo->get_repo_file_path(); } + std::string get_revision() const override { return repo->get_revision(); } + std::vector get_content_tags() const override { return repo->get_content_tags(); } + std::vector> get_distro_tags() const override { + return repo->get_distro_tags(); + } + int64_t get_timestamp() const override { return repo->get_timestamp(); } + int get_max_timestamp() const override { return repo->get_max_timestamp(); } + uint64_t get_size() const override { return size; } + uint64_t get_pkgs() const override { return pkgs; } + uint64_t get_available_pkgs() const override { return available_pkgs; } + std::vector get_mirrors() const override { return repo->get_mirrors(); } private: - libdnf5::repo::Repo & repo; + libdnf5::repo::Repo * repo; uint64_t size; uint64_t pkgs; uint64_t available_pkgs; @@ -110,10 +119,10 @@ void RepoInfoCommand::print(const libdnf5::repo::RepoQuery & query, [[maybe_unus repo_size += pkg.get_download_size(); } - libdnf5::cli::output::RepoInfo repo_info; - auto repo_wrapper = RepoInfoWrapper(*repo, repo_size, pkgs.size(), available_pkgs.size()); - repo_info.add_repo(repo_wrapper); - repo_info.print(); + libdnf5::cli::output::RepoInfo repo_info_table; + RepoInfoWrapper repo_wrapper(*repo, repo_size, pkgs.size(), available_pkgs.size()); + repo_info_table.add_repo(repo_wrapper); + repo_info_table.print(); std::cout << std::endl; } } diff --git a/dnf5/commands/repo/repo_list.cpp b/dnf5/commands/repo/repo_list.cpp index 4b9c6dcf4..d41fc5ad7 100644 --- a/dnf5/commands/repo/repo_list.cpp +++ b/dnf5/commands/repo/repo_list.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "repo_list.hpp" +#include #include namespace dnf5 { @@ -74,7 +75,14 @@ void RepoListCommand::run() { } void RepoListCommand::print(const libdnf5::repo::RepoQuery & query, bool with_status) { - libdnf5::cli::output::print_repolist_table(query, with_status, libdnf5::cli::output::COL_REPO_ID); + std::vector> cli_repos; + auto & repos = query.get_data(); + cli_repos.reserve(query.size()); + for (const auto & repo : repos) { + cli_repos.emplace_back(new libdnf5::cli::output::RepoAdapter(repo)); + } + + libdnf5::cli::output::print_repolist_table(cli_repos, with_status, libdnf5::cli::output::COL_REPO_ID); } } // namespace dnf5 diff --git a/dnf5/main.cpp b/dnf5/main.cpp index 1019a1bf3..1699c4461 100644 --- a/dnf5/main.cpp +++ b/dnf5/main.cpp @@ -54,8 +54,10 @@ along with libdnf. If not, see . #include #include #include +#include #include #include +#include #include #include #include @@ -1115,7 +1117,9 @@ int main(int argc, char * argv[]) try { download_callbacks->set_number_widget_visible(true); download_callbacks->set_show_total_bar_limit(0); - if (!libdnf5::cli::output::print_transaction_table(*context.get_transaction())) { + libdnf5::cli::output::TransactionAdapter cli_output_transaction(*context.get_transaction()); + if (!libdnf5::cli::output::print_transaction_table( + static_cast(cli_output_transaction))) { return static_cast(libdnf5::cli::ExitCode::SUCCESS); } diff --git a/dnf5daemon-client/commands/advisory/advisory_info.cpp b/dnf5daemon-client/commands/advisory/advisory_info.cpp index 805439440..0ce52e279 100644 --- a/dnf5daemon-client/commands/advisory/advisory_info.cpp +++ b/dnf5daemon-client/commands/advisory/advisory_info.cpp @@ -21,6 +21,7 @@ along with libdnf. If not, see . #include "../../wrappers/dbus_advisory_wrapper.hpp" +#include #include #include @@ -30,7 +31,8 @@ namespace dnfdaemon::client { void AdvisoryInfoCommand::process_and_print_queries(const std::vector & advisories) { for (const auto & advisory : advisories) { libdnf5::cli::output::AdvisoryInfo advisory_info; - advisory_info.add_advisory(advisory); + libdnf5::cli::output::AdvisoryAdapter cli_advisory(advisory); + advisory_info.add_advisory(cli_advisory); advisory_info.print(); std::cout << std::endl; } diff --git a/dnf5daemon-client/commands/advisory/advisory_list.cpp b/dnf5daemon-client/commands/advisory/advisory_list.cpp index c35b5625b..91befe9ae 100644 --- a/dnf5daemon-client/commands/advisory/advisory_list.cpp +++ b/dnf5daemon-client/commands/advisory/advisory_list.cpp @@ -21,6 +21,7 @@ along with libdnf. If not, see . #include "../../wrappers/dbus_advisory_wrapper.hpp" +#include #include #include @@ -28,8 +29,8 @@ along with libdnf. If not, see . namespace dnfdaemon::client { void AdvisoryListCommand::process_and_print_queries(const std::vector & advisories) { - std::vector installed_pkgs; - std::vector not_installed_pkgs; + std::vector> installed_pkgs; + std::vector> not_installed_pkgs; for (const auto & advisory : advisories) { // TODO(mblaha): filter the output according contains_pkgs? @@ -45,9 +46,9 @@ void AdvisoryListCommand::process_and_print_queries(const std::vector. #include #include +#include #include #include #include @@ -68,7 +69,8 @@ void TransactionCommand::run_transaction() { ctx.reset_download_cb(); // print the transaction to the user and ask for confirmation - if (!libdnf5::cli::output::print_transaction_table(dbus_goal_wrapper)) { + libdnf5::cli::output::TransactionAdapter cli_output_transaction(dbus_goal_wrapper); + if (!libdnf5::cli::output::print_transaction_table(cli_output_transaction)) { return; } diff --git a/dnf5daemon-client/commands/group/group_list.cpp b/dnf5daemon-client/commands/group/group_list.cpp index 88344c291..fed539531 100644 --- a/dnf5daemon-client/commands/group/group_list.cpp +++ b/dnf5daemon-client/commands/group/group_list.cpp @@ -22,6 +22,7 @@ along with libdnf. If not, see . #include "context.hpp" #include "wrappers/dbus_group_wrapper.hpp" +#include #include #include #include @@ -84,17 +85,20 @@ void GroupListCommand::run() { .storeResultsTo(raw_groups); std::vector groups{}; + std::vector> cli_groups; for (auto & group : raw_groups) { groups.push_back(DbusGroupWrapper(group)); + cli_groups.emplace_back(new libdnf5::cli::output::GroupAdapter(DbusGroupWrapper(group))); } if (command == "info") { for (auto & group : groups) { - libdnf5::cli::output::print_groupinfo_table(group); + libdnf5::cli::output::GroupAdapter cli_group(group); + libdnf5::cli::output::print_groupinfo_table(cli_group); std::cout << '\n'; } } else { - libdnf5::cli::output::print_grouplist_table(groups); + libdnf5::cli::output::print_grouplist_table(cli_groups); } } diff --git a/dnf5daemon-client/commands/repolist/repolist.cpp b/dnf5daemon-client/commands/repolist/repolist.cpp index a1b8ef4d8..265ab0a66 100644 --- a/dnf5daemon-client/commands/repolist/repolist.cpp +++ b/dnf5daemon-client/commands/repolist/repolist.cpp @@ -88,6 +88,21 @@ void RepolistCommand::set_argument_parser() { cmd.register_positional_arg(repos); } + +class CliRepoAdapter : public libdnf5::cli::output::IRepo { +public: + CliRepoAdapter(const DbusRepoWrapper * repo) : repo{repo} {} + + std::string get_id() const override { return repo->get_id(); } + + bool is_enabled() const override { return repo->is_enabled(); } + + std::string get_name() const override { return repo->get_name(); } + +private: + const DbusRepoWrapper * repo; +}; + void RepolistCommand::run() { auto & ctx = get_context(); @@ -146,8 +161,15 @@ void RepolistCommand::run() { if (command == "repolist") { // print the output table bool with_status = enable_disable_option->get_value() == "all"; - libdnf5::cli::output::print_repolist_table( - DbusQueryRepoWrapper(repositories), with_status, libdnf5::cli::output::COL_REPO_ID); + + std::vector> cli_repos; + auto repo_query = DbusQueryRepoWrapper(repositories); + cli_repos.reserve(repo_query.get_data().size()); + for (const auto & repo : repo_query.get_data()) { + cli_repos.emplace_back(new CliRepoAdapter(repo.get())); + } + + libdnf5::cli::output::print_repolist_table(cli_repos, with_status, libdnf5::cli::output::COL_REPO_ID); } else { // repoinfo command diff --git a/dnf5daemon-client/commands/repoquery/repoquery.cpp b/dnf5daemon-client/commands/repoquery/repoquery.cpp index 8c8711d59..fbc65c092 100644 --- a/dnf5daemon-client/commands/repoquery/repoquery.cpp +++ b/dnf5daemon-client/commands/repoquery/repoquery.cpp @@ -24,6 +24,7 @@ along with libdnf. If not, see . #include #include +#include #include #include #include @@ -146,10 +147,11 @@ void RepoqueryCommand::run() { for (auto & raw_package : packages) { --num_packages; DbusPackageWrapper package(raw_package); + libdnf5::cli::output::PackageAdapter cli_pkg(package); if (info_option->get_value()) { auto out = libdnf5::cli::output::PackageInfoSections(); out.setup_cols(); - out.add_package(package); + out.add_package(cli_pkg); out.print(); if (num_packages) { std::cout << std::endl; diff --git a/dnf5daemon-client/wrappers/dbus_repo_wrapper.hpp b/dnf5daemon-client/wrappers/dbus_repo_wrapper.hpp index 04b2966bc..b8dc2a07f 100644 --- a/dnf5daemon-client/wrappers/dbus_repo_wrapper.hpp +++ b/dnf5daemon-client/wrappers/dbus_repo_wrapper.hpp @@ -21,12 +21,13 @@ along with libdnf. If not, see . #define DNF5DAEMON_CLIENT_WRAPPERS_DBUS_REPO_WRAPPER_HPP #include +#include #include namespace dnfdaemon::client { -class DbusRepoWrapper { +class DbusRepoWrapper : public libdnf5::cli::output::IRepoInfo { public: explicit DbusRepoWrapper(dnfdaemon::KeyValueMap & rawdata) : rawdata(rawdata){}; diff --git a/dnf5daemon-client/wrappers/dbus_transaction_package_wrapper.hpp b/dnf5daemon-client/wrappers/dbus_transaction_package_wrapper.hpp index 0111e0727..c63ad88ca 100644 --- a/dnf5daemon-client/wrappers/dbus_transaction_package_wrapper.hpp +++ b/dnf5daemon-client/wrappers/dbus_transaction_package_wrapper.hpp @@ -39,6 +39,7 @@ class DbusTransactionPackageWrapper { transaction_item_attrs(std::get<3>(dti)), package(std::get<4>(dti)) {} + const DbusPackageWrapper & get_package() const noexcept { return package; } DbusPackageWrapper & get_package() noexcept { return package; } libdnf5::transaction::TransactionItemAction get_action() const noexcept { return action; } libdnf5::transaction::TransactionItemReason get_reason() const noexcept { return reason; } diff --git a/include/libdnf5-cli/output/advisoryinfo.hpp b/include/libdnf5-cli/output/advisoryinfo.hpp index 9b8621c4d..1c3b787af 100644 --- a/include/libdnf5-cli/output/advisoryinfo.hpp +++ b/include/libdnf5-cli/output/advisoryinfo.hpp @@ -21,74 +21,22 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_ADVISORYINFO_HPP #define LIBDNF5_CLI_OUTPUT_ADVISORYINFO_HPP -#include "utils/string.hpp" - -#include "libdnf5-cli/output/key_value_table.hpp" - -#include - +#include "interfaces/advisory.hpp" namespace libdnf5::cli::output { - -class AdvisoryInfo : public KeyValueTable { +class AdvisoryInfo { public: - template - void add_advisory(AdvisoryClass & advisory); -}; + AdvisoryInfo(); + ~AdvisoryInfo(); + void add_advisory(IAdvisory & advisory); + void print(); -template -void AdvisoryInfo::add_advisory(AdvisoryClass & advisory) { - add_line("Name", advisory.get_name(), "bold"); - add_line("Title", advisory.get_title()); - add_line("Severity", advisory.get_severity()); - add_line("Type", advisory.get_type()); - add_line("Status", advisory.get_status()); - add_line("Vendor", advisory.get_vendor()); - add_line("Issued", libdnf5::utils::string::format_epoch(advisory.get_buildtime())); - add_lines("Description", libdnf5::utils::string::split(advisory.get_description(), "\n")); - add_line("Message", advisory.get_message()); - add_line("Rights", advisory.get_rights()); - - // References - for (auto reference : advisory.get_references()) { - auto group_reference = add_line("Reference", ""); - add_line("Title", reference.get_title(), "bold", group_reference); - add_line("Id", reference.get_id(), nullptr, group_reference); - add_line("Type", reference.get_type_cstring(), nullptr, group_reference); - add_line("Url", reference.get_url(), nullptr, group_reference); - } - - // Collections - for (auto collection : advisory.get_collections()) { - auto group_collection = add_line("Collection", ""); - - // Modules - auto modules = collection.get_modules(); - if (!modules.empty()) { - auto module_iter = modules.begin(); - add_line("Modules", module_iter->get_nsvca(), nullptr, group_collection); - module_iter++; - while (module_iter != modules.end()) { - add_line("", module_iter->get_nsvca(), nullptr, group_collection); - module_iter++; - } - } - - // Packages - auto packages = collection.get_packages(); - if (!packages.empty()) { - auto package_iter = packages.begin(); - add_line("Packages", package_iter->get_nevra(), nullptr, group_collection); - package_iter++; - while (package_iter != packages.end()) { - add_line("", package_iter->get_nevra(), nullptr, group_collection); - package_iter++; - } - } - } -} +private: + class Impl; + std::unique_ptr p_impl; +}; } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/advisorylist.hpp b/include/libdnf5-cli/output/advisorylist.hpp index b3c44f073..45da2240e 100644 --- a/include/libdnf5-cli/output/advisorylist.hpp +++ b/include/libdnf5-cli/output/advisorylist.hpp @@ -21,56 +21,16 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_ADVISORYLIST_HPP #define LIBDNF5_CLI_OUTPUT_ADVISORYLIST_HPP +#include "interfaces/advisory.hpp" + #include #include -#include namespace libdnf5::cli::output { -struct libscols_table * create_advisorylist_table(std::string column_id_name); - -void sort_advisorylist_table(libscols_table * table); - -void add_line_into_advisorylist_table( - struct libscols_table * table, - const char * name, - const char * type, - const char * severity, - const char * package, - unsigned long long buildtime, - bool installed); - -template void print_advisorylist_table( - std::vector & advisory_package_list_not_installed, - std::vector & advisory_package_list_installed) { - struct libscols_table * table = create_advisorylist_table("Name"); - for (auto adv_pkg : advisory_package_list_not_installed) { - auto advisory = adv_pkg.get_advisory(); - add_line_into_advisorylist_table( - table, - advisory.get_name().c_str(), - advisory.get_type().c_str(), - advisory.get_severity().c_str(), - adv_pkg.get_nevra().c_str(), - advisory.get_buildtime(), - false); - } - for (auto adv_pkg : advisory_package_list_installed) { - auto advisory = adv_pkg.get_advisory(); - add_line_into_advisorylist_table( - table, - advisory.get_name().c_str(), - advisory.get_type().c_str(), - advisory.get_severity().c_str(), - adv_pkg.get_nevra().c_str(), - advisory.get_buildtime(), - true); - } - sort_advisorylist_table(table); - scols_print_table(table); - scols_unref_table(table); -} + std::vector> & advisory_package_list_not_installed, + std::vector> & advisory_package_list_installed); void print_advisorylist_references_table( std::vector & advisory_package_list_not_installed, diff --git a/include/libdnf5-cli/output/environmentinfo.hpp b/include/libdnf5-cli/output/environmentinfo.hpp index 6e995bc0e..60a26953d 100644 --- a/include/libdnf5-cli/output/environmentinfo.hpp +++ b/include/libdnf5-cli/output/environmentinfo.hpp @@ -21,98 +21,11 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_ENVIRONMENTINFO_HPP #define LIBDNF5_CLI_OUTPUT_ENVIRONMENTINFO_HPP -#include "libdnf5-cli/tty.hpp" - -// TODO(lukash) include from common in a public libdnf-cli header -#include "utils/string.hpp" - -#include - +#include "interfaces/comps.hpp" namespace libdnf5::cli::output { - -static void add_line_into_environmentinfo_table( - struct libscols_table * table, const char * key, const char * value, const char * color) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, 0, key); - scols_line_set_data(ln, 1, value); - - if (color && strcmp(color, "") != 0) { - auto cell_value = scols_line_get_cell(ln, 1); - scols_cell_set_color(cell_value, color); - } -} - - -static void add_line_into_environmentinfo_table(struct libscols_table * table, const char * key, const char * value) { - add_line_into_environmentinfo_table(table, key, value, ""); -} - - -static void add_groups(struct libscols_table * table, std::vector groups, const char * group_type_desc) { - if (groups.empty()) { - // don't even print the group type description - return; - } - - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, 0, group_type_desc); - - auto it = groups.begin(); - // put the first group at the same line as description - scols_line_set_data(ln, 1, it->c_str()); - it++; - - // put the remaining group on separate lines - for (; it != groups.end(); it++) { - struct libscols_line * group_ln = scols_table_new_line(table, ln); - scols_line_set_data(group_ln, 1, it->c_str()); - } -} - - -template -static struct libscols_table * create_environmentinfo_table(EnvironmentType & environment) { - struct libscols_table * table = scols_new_table(); - scols_table_enable_noheadings(table, 1); - scols_table_set_column_separator(table, " : "); - scols_table_new_column(table, "key", 20, SCOLS_FL_TREE); - struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); - scols_column_set_safechars(cl, "\n"); - scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, true); - } - auto sy = scols_new_symbols(); - scols_symbols_set_branch(sy, " "); - scols_symbols_set_right(sy, " "); - scols_symbols_set_vertical(sy, ""); - scols_table_set_symbols(table, sy); - scols_unref_symbols(sy); - - add_line_into_environmentinfo_table(table, "Id", environment.get_environmentid().c_str(), "bold"); - add_line_into_environmentinfo_table(table, "Name", environment.get_name().c_str()); - add_line_into_environmentinfo_table(table, "Description", environment.get_description().c_str()); - add_line_into_environmentinfo_table(table, "Order", environment.get_order().c_str()); - add_line_into_environmentinfo_table(table, "Installed", environment.get_installed() ? "True" : "False"); - add_line_into_environmentinfo_table( - table, "Repositories", libdnf5::utils::string::join(environment.get_repos(), ", ").c_str()); - - add_groups(table, environment.get_groups(), "Required groups"); - add_groups(table, environment.get_optional_groups(), "Optional groups"); - - return table; -} - - -template -void print_environmentinfo_table(EnvironmentType & environment) { - struct libscols_table * table = create_environmentinfo_table(environment); - scols_print_table(table); - scols_unref_table(table); -} - +void print_environmentinfo_table(IEnvironment & environment); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/environmentlist.hpp b/include/libdnf5-cli/output/environmentlist.hpp index 1fbc63b94..0d3f610f4 100644 --- a/include/libdnf5-cli/output/environmentlist.hpp +++ b/include/libdnf5-cli/output/environmentlist.hpp @@ -21,59 +21,11 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_ENVIRONMENTLIST_HPP #define LIBDNF5_CLI_OUTPUT_ENVIRONMENTLIST_HPP -#include "libdnf5-cli/tty.hpp" - -#include +#include "interfaces/comps.hpp" namespace libdnf5::cli::output { - -// environment list table columns -enum { COL_ENVIRONMENT_ID, COL_ENVIRONMENT_NAME, COL_INSTALLED }; - - -static struct libscols_table * create_environmentlist_table() { - struct libscols_table * table = scols_new_table(); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, 1); - } - struct libscols_column * cl = scols_table_new_column(table, "ID", 20, 0); - scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); - scols_table_new_column(table, "Name", 0.5, SCOLS_FL_TRUNC); - scols_table_new_column(table, "Installed", 0.1, SCOLS_FL_RIGHT); - return table; -} - - -static void add_line_into_environmentlist_table( - struct libscols_table * table, const char * id, const char * name, bool installed) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, COL_ENVIRONMENT_ID, id); - scols_line_set_data(ln, COL_ENVIRONMENT_NAME, name); - scols_line_set_data(ln, COL_INSTALLED, installed ? "yes" : "no"); - if (installed) { - struct libscols_cell * cl = scols_line_get_cell(ln, COL_INSTALLED); - scols_cell_set_color(cl, "green"); - } -} - - -template -void print_environmentlist_table(Query & environment_list) { - struct libscols_table * table = create_environmentlist_table(); - for (auto environment : environment_list) { - add_line_into_environmentlist_table( - table, - environment.get_environmentid().c_str(), - environment.get_name().c_str(), - environment.get_installed()); - } - auto cl = scols_table_get_column(table, COL_ENVIRONMENT_ID); - scols_sort_table(table, cl); - scols_print_table(table); - scols_unref_table(table); -} - +void print_environmentlist_table(std::vector> & environment_list); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/groupinfo.hpp b/include/libdnf5-cli/output/groupinfo.hpp index 3d50691a7..21369a844 100644 --- a/include/libdnf5-cli/output/groupinfo.hpp +++ b/include/libdnf5-cli/output/groupinfo.hpp @@ -21,116 +21,11 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_GROUPINFO_HPP #define LIBDNF5_CLI_OUTPUT_GROUPINFO_HPP -#include "libdnf5-cli/tty.hpp" - -// TODO(lukash) include from common in a public libdnf-cli header -#include "utils/string.hpp" - -#include -#include - +#include "interfaces/comps.hpp" namespace libdnf5::cli::output { - -static void add_line_into_groupinfo_table( - struct libscols_table * table, const char * key, const char * value, const char * color) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, 0, key); - scols_line_set_data(ln, 1, value); - - if (color && strcmp(color, "") != 0) { - auto cell_value = scols_line_get_cell(ln, 1); - scols_cell_set_color(cell_value, color); - } -} - - -static void add_line_into_groupinfo_table(struct libscols_table * table, const char * key, const char * value) { - add_line_into_groupinfo_table(table, key, value, ""); -} - - -template -static void add_packages( - struct libscols_table * table, - GroupType & group, - libdnf5::comps::PackageType pkg_type, - const char * pkg_type_desc) { - std::set packages; - - // we don't mind iterating through all packages in every add_packages() call, - // because performance is not an issue here - for (auto & package : group.get_packages()) { - if (package.get_type() == pkg_type) { - packages.emplace(package.get_name()); - } - } - - if (packages.empty()) { - // don't even print the package type description - return; - } - - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, 0, pkg_type_desc); - - auto it = packages.begin(); - // put the first package at the same line as description - scols_line_set_data(ln, 1, it->c_str()); - it++; - - // put the remaining packages on separate lines - for (; it != packages.end(); it++) { - struct libscols_line * pkg_ln = scols_table_new_line(table, ln); - scols_line_set_data(pkg_ln, 1, it->c_str()); - } -} - - -template -static struct libscols_table * create_groupinfo_table(GroupType & group) { - struct libscols_table * table = scols_new_table(); - scols_table_enable_noheadings(table, 1); - scols_table_set_column_separator(table, " : "); - scols_table_new_column(table, "key", 20, SCOLS_FL_TREE); - struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); - scols_column_set_safechars(cl, "\n"); - scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, true); - } - auto sy = scols_new_symbols(); - scols_symbols_set_branch(sy, " "); - scols_symbols_set_right(sy, " "); - scols_symbols_set_vertical(sy, ""); - scols_table_set_symbols(table, sy); - scols_unref_symbols(sy); - - add_line_into_groupinfo_table(table, "Id", group.get_groupid().c_str(), "bold"); - add_line_into_groupinfo_table(table, "Name", group.get_name().c_str()); - add_line_into_groupinfo_table(table, "Description", group.get_description().c_str()); - add_line_into_groupinfo_table(table, "Installed", group.get_installed() ? "yes" : "no"); - add_line_into_groupinfo_table(table, "Order", group.get_order().c_str()); - add_line_into_groupinfo_table(table, "Langonly", group.get_langonly().c_str()); - add_line_into_groupinfo_table(table, "Uservisible", group.get_uservisible() ? "yes" : "no"); - add_line_into_groupinfo_table(table, "Repositories", libdnf5::utils::string::join(group.get_repos(), ", ").c_str()); - - add_packages(table, group, libdnf5::comps::PackageType::MANDATORY, "Mandatory packages"); - add_packages(table, group, libdnf5::comps::PackageType::DEFAULT, "Default packages"); - add_packages(table, group, libdnf5::comps::PackageType::OPTIONAL, "Optional packages"); - add_packages(table, group, libdnf5::comps::PackageType::CONDITIONAL, "Conditional packages"); - - return table; -} - -template -void print_groupinfo_table(GroupType & group) { - struct libscols_table * table = create_groupinfo_table(group); - scols_print_table(table); - scols_unref_table(table); -} - +void print_groupinfo_table(IGroup & group); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/grouplist.hpp b/include/libdnf5-cli/output/grouplist.hpp index 7e8b66c71..2fb0ed15b 100644 --- a/include/libdnf5-cli/output/grouplist.hpp +++ b/include/libdnf5-cli/output/grouplist.hpp @@ -21,56 +21,11 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_GROUPLIST_HPP #define LIBDNF5_CLI_OUTPUT_GROUPLIST_HPP -#include "libdnf5-cli/tty.hpp" - -#include +#include "interfaces/comps.hpp" namespace libdnf5::cli::output { - -// group list table columns -enum { COL_GROUP_ID, COL_GROUP_NAME, COL_INSTALLED }; - - -static struct libscols_table * create_grouplist_table() { - struct libscols_table * table = scols_new_table(); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, 1); - } - struct libscols_column * cl = scols_table_new_column(table, "ID", 20, 0); - scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); - scols_table_new_column(table, "Name", 0.5, SCOLS_FL_TRUNC); - scols_table_new_column(table, "Installed", 0.1, SCOLS_FL_RIGHT); - return table; -} - - -static void add_line_into_grouplist_table( - struct libscols_table * table, const char * id, const char * name, bool installed) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, COL_GROUP_ID, id); - scols_line_set_data(ln, COL_GROUP_NAME, name); - scols_line_set_data(ln, COL_INSTALLED, installed ? "yes" : "no"); - if (installed) { - struct libscols_cell * cl = scols_line_get_cell(ln, COL_INSTALLED); - scols_cell_set_color(cl, "green"); - } -} - - -template -void print_grouplist_table(Query & group_list) { - struct libscols_table * table = create_grouplist_table(); - for (auto group : group_list) { - add_line_into_grouplist_table( - table, group.get_groupid().c_str(), group.get_name().c_str(), group.get_installed()); - } - auto cl = scols_table_get_column(table, COL_GROUP_ID); - scols_sort_table(table, cl); - scols_print_table(table); - scols_unref_table(table); -} - +void print_grouplist_table(std::vector> & group_list); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/moduleinfo.hpp b/include/libdnf5-cli/output/moduleinfo.hpp index 96671f712..f9423caef 100644 --- a/include/libdnf5-cli/output/moduleinfo.hpp +++ b/include/libdnf5-cli/output/moduleinfo.hpp @@ -21,100 +21,18 @@ 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 "interfaces/module.hpp" -#include "libdnf5-cli/tty.hpp" - -#include -#include -#include -#include - -#include -#include +#include +#include namespace libdnf5::cli::output { +void print_module_item(IModuleItem & module_item); -const std::string MODULEINFO_TABLE_HINT = _("Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled, [a]ctive"); - - -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); -}; - - -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]"); - } - if (module_item.is_active()) { - stream_string.append("[a]"); - } - stream_string = stream_string.empty() ? module_item.get_stream() : module_item.get_stream() + " " + stream_string; - - // Trim summary and description - auto summary = module_item.get_summary(); - libdnf5::utils::string::trim(summary); - auto description = module_item.get_description(); - libdnf5::utils::string::trim(description); - - 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_multiline_value("Summary", libdnf5::utils::string::split(summary, "\n")); - add_multiline_value("Description", libdnf5::utils::string::split(description, "\n")); - add_multiline_value("Requires", dependency_strings); - add_multiline_value("Artifacts", module_item.get_artifacts()); -} - - -template -void print_moduleinfo_table(Query & module_list) { - std::map module_map; - for (const auto & module_item : module_list) { - module_map.emplace(module_item.get_full_identifier() + ":" + module_item.get_repo_id(), module_item); - } - for (auto & module_item : module_map) { - libdnf5::cli::output::ModuleInfo module_info; - module_info.add_module_item(module_item.second); - module_info.print(); - std::cout << std::endl; - } -} - - -void print_moduleinfo_table_hint() { - std::cout << MODULEINFO_TABLE_HINT << std::endl; -} +void print_moduleinfo_table(std::vector> & module_list); +void print_moduleinfo_table_hint(); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/modulelist.hpp b/include/libdnf5-cli/output/modulelist.hpp index ed8db7541..46414a337 100644 --- a/include/libdnf5-cli/output/modulelist.hpp +++ b/include/libdnf5-cli/output/modulelist.hpp @@ -21,103 +21,16 @@ along with libdnf. If not, see . #ifndef LIBDNF_CLI_OUTPUT_MODULELIST_HPP #define LIBDNF_CLI_OUTPUT_MODULELIST_HPP -#include "utils/string.hpp" +#include "interfaces/module.hpp" -#include "libdnf5-cli/tty.hpp" - -#include -#include -#include - -#include -#include +#include +#include namespace libdnf5::cli::output { +void print_modulelist_table(const std::vector> & module_list); -// module list table columns -enum { COL_MODULE_NAME, COL_MODULE_STREAM, COL_MODULE_PROFILES, COL_MODULE_SUMMARY }; - - -const std::string MODULELIST_TABLE_HINT = _("Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled"); - - -static struct libscols_table * create_modulelist_table() { - struct libscols_table * table = scols_new_table(); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, 1); - } - struct libscols_column * cl = scols_table_new_column(table, "Name", 0.15, 0); - scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); - scols_table_new_column(table, "Stream", 0.15, SCOLS_FL_TRUNC); - scols_table_new_column(table, "Profiles", 0.3, SCOLS_FL_WRAP); - scols_table_new_column(table, "Summary", 0.4, SCOLS_FL_WRAP); - scols_table_enable_maxout(table, 1); - return table; -} - - -static void add_line_into_modulelist_table( - struct libscols_table * table, - const char * name, - const char * stream, - const char * profiles, - const char * summary) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, COL_MODULE_NAME, name); - scols_line_set_data(ln, COL_MODULE_STREAM, stream); - scols_line_set_data(ln, COL_MODULE_PROFILES, profiles); - scols_line_set_data(ln, COL_MODULE_SUMMARY, summary); -} - - -template -void print_modulelist_table(Query & module_list) { - struct libscols_table * table = create_modulelist_table(); - std::set> name_stream_pairs; - for (auto & module_item : module_list) { - const std::string & name = module_item.get_name(); - const std::string & stream = module_item.get_stream(); - if (!name_stream_pairs.contains(make_pair(name, stream))) { - // 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() ? stream : stream + " " + stream_string; - - // Get profile strings (append [d] or [i] if needed) - std::vector profile_strings; - for (const auto & profile : module_item.get_profiles()) { - // TODO(pkratoch): Also show "[i]" for installed profiles - profile_strings.push_back(profile.is_default() ? profile.get_name() + " [d]" : profile.get_name()); - } - - add_line_into_modulelist_table( - table, - name.c_str(), - stream_string.c_str(), - utils::string::join(profile_strings, ", ").c_str(), - module_item.get_summary().c_str()); - name_stream_pairs.emplace(make_pair(name, stream)); - } - } - 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); -} - - -void print_modulelist_table_hint() { - std::cout << MODULELIST_TABLE_HINT << std::endl; -} - +void print_modulelist_table_hint(); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/package_info_sections.hpp b/include/libdnf5-cli/output/package_info_sections.hpp index 07bbe843f..a103ab386 100644 --- a/include/libdnf5-cli/output/package_info_sections.hpp +++ b/include/libdnf5-cli/output/package_info_sections.hpp @@ -21,86 +21,29 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_PACKAGE_INFO_SECTIONS_HPP #define LIBDNF5_CLI_OUTPUT_PACKAGE_INFO_SECTIONS_HPP +#include "interfaces/package.hpp" #include "package_list_sections.hpp" -#include "utils/string.hpp" - -#include "libdnf5-cli/utils/units.hpp" - -#include -#include - -#include namespace libdnf5::cli::output { class PackageInfoSections : public PackageListSections { public: - PackageInfoSections() : PackageListSections() {} - template + PackageInfoSections(); + ~PackageInfoSections(); + bool add_package( - Package pkg, + IPackage & pkg, const std::string & heading = "", const std::unique_ptr & colorizer = nullptr, - const std::vector & obsoletes = {}) { - struct libscols_line * first_line = add_line("Name", pkg.get_name()); - if (colorizer) { - scols_line_set_color(first_line, colorizer->get_pkg_color(pkg).c_str()); - } - - add_line("Epoch", pkg.get_epoch()); - add_line("Version", pkg.get_version()); - add_line("Release", pkg.get_release()); - add_line("Architecture", pkg.get_arch()); + const std::vector & obsoletes = {}); - if (!obsoletes.empty()) { - auto iterator = obsoletes.begin(); - add_line("Obsoletes", iterator->get_full_nevra()); - ++iterator; - for (; iterator != obsoletes.end(); ++iterator) { - add_line("", iterator->get_full_nevra()); - } - } - - if (!pkg.is_installed()) { - add_line("Download size", utils::units::format_size_aligned(static_cast(pkg.get_download_size()))); - } - add_line("Installed size", utils::units::format_size_aligned(static_cast(pkg.get_install_size()))); - if (pkg.get_arch() != "src") { - add_line("Source", pkg.get_sourcerpm()); - } - if (pkg.is_installed()) { - add_line("From repository", pkg.get_from_repo_id()); - } else { - add_line("Repository", pkg.get_repo_id()); - } - add_line("Summary", pkg.get_summary()); - add_line("URL", pkg.get_url()); - add_line("License", pkg.get_license()); - - auto lines = libdnf5::utils::string::split(pkg.get_description(), "\n"); - auto iterator = lines.begin(); - add_line("Description", *iterator); - ++iterator; - for (; iterator != lines.end(); ++iterator) { - add_line("", *iterator); - } - struct libscols_line * last_line = add_line("Vendor", pkg.get_vendor()); - - // for info output keep each package as a separate section, which means - // an empty line is printed between packages resulting in better readability - sections.emplace_back(heading, first_line, last_line); - return true; - } - bool virtual add_section( + bool add_section( const std::string & heading, const libdnf5::rpm::PackageSet & pkg_set, const std::unique_ptr & colorizer = nullptr, const std::map> & obsoletes = {}) override; void setup_cols() override; - -private: - struct libscols_line * add_line(const std::string & key, const std::string & value); }; } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/package_list_sections.hpp b/include/libdnf5-cli/output/package_list_sections.hpp index a6f6eae20..afcdc2bee 100644 --- a/include/libdnf5-cli/output/package_list_sections.hpp +++ b/include/libdnf5-cli/output/package_list_sections.hpp @@ -23,10 +23,13 @@ along with libdnf. If not, see . #include "pkg_colorizer.hpp" +#include #include -#include +#include +#include #include +#include namespace libdnf5::cli::output { @@ -45,22 +48,20 @@ class PackageListSections { /// @param colorizer Optional class to select color for packages in output /// @param obsoletes Optional map of obsoleted packages by obsoleter /// @return Returns `true` in case at least one package was added, `false` otherwise - bool virtual add_section( + virtual bool add_section( const std::string & heading, const libdnf5::rpm::PackageSet & pkg_set, const std::unique_ptr & colorizer = nullptr, const std::map> & obsoletes = {}); /// Setup table columns - void virtual setup_cols(); + virtual void setup_cols(); protected: - struct libscols_table * table = nullptr; - // keeps track of the first and the last line of sections - std::vector> sections; + class Impl; + std::unique_ptr p_impl; }; - } // namespace libdnf5::cli::output #endif // LIBDNF5_CLI_OUTPUT_PACKAGE_LIST_SECTIONS_HPP diff --git a/include/libdnf5-cli/output/pkg_colorizer.hpp b/include/libdnf5-cli/output/pkg_colorizer.hpp index bdfcb5ac8..8b200b2ec 100644 --- a/include/libdnf5-cli/output/pkg_colorizer.hpp +++ b/include/libdnf5-cli/output/pkg_colorizer.hpp @@ -21,15 +21,15 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_PKG_COLORIZER_HPP #define LIBDNF5_CLI_OUTPUT_PKG_COLORIZER_HPP -#include +#include "interfaces/package.hpp" + #include -#include #include +#include namespace libdnf5::cli::output { - class PkgColorizer { public: /// Class is used to compute output color of the package based on the package @@ -50,24 +50,7 @@ class PkgColorizer { /// Compute a color for the package. /// @param package A package for which color is needed. /// @return Escape sequence for the color. - template - std::string get_pkg_color(const Package & package) { - auto base_pkg = base_na_version.find(package.get_na()); - std::string color = ""; - if (base_pkg == base_na_version.end()) { - color = color_not_found; - } else { - auto vercmp = libdnf5::rpm::evrcmp(package, base_pkg->second); - if (vercmp < 0) { - color = color_lt; - } else if (vercmp == 0) { - color = color_eq; - } else { - color = color_gt; - } - } - return color; - } + std::string get_pkg_color(const IPackage & package); private: std::string to_escape(const std::string & color); @@ -79,23 +62,6 @@ class PkgColorizer { std::string color_lt; std::string color_eq; std::string color_gt; - - std::map color_to_escape = { - {"bold", "\033[1m"}, - {"dim", "\033[2m"}, - {"underline", "\033[4m"}, - {"blink", "\033[5m"}, - {"reverse", "\033[7m"}, - {"black", "\033[30m"}, - {"red", "\033[31m"}, - {"green", "\033[32m"}, - {"brown", "\033[33m"}, - {"blue", "\033[34m"}, - {"magenta", "\033[35m"}, - {"cyan", "\033[36m"}, - {"gray", "\033[37m"}, - {"white", "\033[1;37m"}, - }; }; diff --git a/include/libdnf5-cli/output/provides.hpp b/include/libdnf5-cli/output/provides.hpp index c900f0f63..ee82108a2 100644 --- a/include/libdnf5-cli/output/provides.hpp +++ b/include/libdnf5-cli/output/provides.hpp @@ -20,96 +20,14 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_PROVIDES_HPP #define LIBDNF5_CLI_OUTPUT_PROVIDES_HPP -#include "libdnf5-cli/output/key_value_table.hpp" - -#include -#include - -#include +#include "interfaces/package.hpp" namespace libdnf5::cli::output { enum ProvidesMatchedBy : int { NO_MATCH = 0, PROVIDES = 1, FILENAME = 2, BINARY = 3 }; -static void add_line_into_provides_table(struct libscols_table * table, const char * key, const char * value) { - struct libscols_line * ln = scols_table_new_line(table, nullptr); - scols_line_set_data(ln, 0, key); - scols_line_set_data(ln, 1, value); -} - -template -static struct libscols_table * create_provides_heading_table(Package & package) { - struct libscols_table * table = scols_new_table(); - scols_table_enable_noheadings(table, 1); - scols_table_set_column_separator(table, " : "); - scols_table_new_column(table, "key", 5, 0); - struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); - scols_column_set_safechars(cl, "\n"); - scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); - - add_line_into_provides_table(table, package.get_nevra().c_str(), package.get_summary().c_str()); - return table; -} - -template -static struct libscols_table * create_provides_table(Package & package, const char * spec, int match) { - struct libscols_table * table = scols_new_table(); - // don't print provides if don't match for at least one - if (!match) { - return table; - } - scols_table_enable_noheadings(table, 1); - scols_table_set_column_separator(table, " : "); - scols_table_new_column(table, "key", 5, 0); - struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); - scols_column_set_safechars(cl, "\n"); - scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); - - add_line_into_provides_table(table, "Repo", package.get_repo_id().c_str()); - add_line_into_provides_table(table, "Matched From", ""); - switch (match) { - case ProvidesMatchedBy::PROVIDES: { - std::string spec_and_version = - package.get_name() + " = " + package.get_version() + "-" + package.get_release(); - add_line_into_provides_table(table, "Provide", spec_and_version.c_str()); - break; - } - case ProvidesMatchedBy::FILENAME: { - std::string pattern(spec); - for (const auto & file : package.get_files()) { - if (sack::match_string(file, sack::QueryCmp::GLOB, pattern)) { - add_line_into_provides_table(table, "Filename", file.c_str()); - } - } - break; - } - case ProvidesMatchedBy::BINARY: { - std::string pattern(spec); - std::vector prefix{"/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/"}; - for (const auto & file : package.get_files()) { - for (const auto & p : prefix) { - if (sack::match_string(file, sack::QueryCmp::GLOB, p + pattern)) { - add_line_into_provides_table(table, "Filename", file.c_str()); - } - } - } - break; - } - } - - return table; -} +void print_provides_table(IPackage & package, const char * spec, int match); -template -static void print_provides_table(Package & package, const char * spec, int match) { - auto table_head = create_provides_heading_table(package); - auto table = create_provides_table(package, spec, match); - scols_print_table(table_head); - scols_print_table(table); - scols_unref_table(table_head); - scols_unref_table(table); - std::cout << std::endl; -} } // namespace libdnf5::cli::output #endif // LIBDNF5_CLI_OUTPUT_PROVIDES_HPP diff --git a/include/libdnf5-cli/output/repo_info.hpp b/include/libdnf5-cli/output/repo_info.hpp index 9654c5872..5797406ae 100644 --- a/include/libdnf5-cli/output/repo_info.hpp +++ b/include/libdnf5-cli/output/repo_info.hpp @@ -21,177 +21,25 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_REPO_INFO_HPP #define LIBDNF5_CLI_OUTPUT_REPO_INFO_HPP +#include "interfaces/repo.hpp" -#include "key_value_table.hpp" -#include "utils/string.hpp" - -#include "libdnf5-cli/utils/units.hpp" - +#include namespace libdnf5::cli::output { - -class RepoInfo : public KeyValueTable { +class RepoInfo { public: - template - void add_repo(Repo & repo); -}; - - -template -void RepoInfo::add_repo(Repo & repo) { - auto enabled = repo.is_enabled(); - - add_line("Repo ID", repo.get_id(), "bold"); - add_line("Name", repo.get_name()); - - add_line("Status", enabled ? "enabled" : "disabled", enabled ? "green" : "red"); - - add_line("Priority", repo.get_priority()); - add_line("Cost", repo.get_cost()); - add_line("Type", repo.get_type()); - - auto exclude_packages = repo.get_excludepkgs(); - if (!exclude_packages.empty()) { - add_line("Exclude packages", exclude_packages); - } - - auto include_packages = repo.get_includepkgs(); - if (!include_packages.empty()) { - add_line("Include packages", include_packages); - } - - auto cache_updated = repo.get_timestamp(); - std::string last_update = "unknown"; - if (cache_updated > 0) { - last_update = libdnf5::utils::string::format_epoch(cache_updated); - } - - auto metadata_expire = repo.get_metadata_expire(); - std::string expire_value; - if (metadata_expire <= -1) { - expire_value = "Never"; - } else if (metadata_expire == 0) { - expire_value = "Instant"; - } else { - expire_value = fmt::format("{} seconds", metadata_expire); - } - add_line("Metadata expire", fmt::format("{} (last: {})", expire_value, last_update)); - add_line("Skip if unavailable", fmt::format("{}", repo.get_skip_if_unavailable())); - - auto repo_file_path = repo.get_repo_file_path(); - if (!repo_file_path.empty()) { - add_line("Config file", repo_file_path); - } - - // URLs - auto group_urls = add_line("URLs", "", nullptr); - - auto base_url = repo.get_baseurl(); - if (!base_url.empty()) { - add_line("Base URL", base_url, nullptr, group_urls); - } else { - auto mirrors = repo.get_mirrors(); - if (!mirrors.empty()) { - add_line("Base URL", fmt::format("{} ({} more)", mirrors.front(), mirrors.size()), nullptr, group_urls); - } - } + RepoInfo(); + ~RepoInfo(); - auto metalink = repo.get_metalink(); - auto mirrorlist = repo.get_mirrorlist(); - if (!metalink.empty()) { - add_line("Metalink", metalink, nullptr, group_urls); - } else if (!mirrorlist.empty()) { - add_line("Mirrorlist", mirrorlist, nullptr, group_urls); - } - - // PGP - auto group_gpg = add_line("PGP", "", nullptr); - - auto gpg_keys = repo.get_gpgkey(); - if (!gpg_keys.empty()) { - add_line("Keys", gpg_keys, nullptr, group_gpg); - } - - add_line("Verify repodata", fmt::format("{}", repo.get_repo_gpgcheck()), nullptr, group_gpg); - add_line("Verify packages", fmt::format("{}", repo.get_gpgcheck()), nullptr, group_gpg); - - // TODO(jkolarik): Verbose is not implemented and not used yet - // if (verbose) { - // // Connection settings - // auto group_conn = add_line("Connection settings", ""); - // add_line("Authentication method", "", nullptr, group_conn); - // add_line("Username", "", nullptr, group_conn); - // add_line("Password", "", nullptr, group_conn); - // add_line("SSL CA certificate", "", nullptr, group_conn); - // add_line("SSL client certificate", "", nullptr, group_conn); - // add_line("SSL client key", "", nullptr, group_conn); - // add_line("Verify SSL certificate", "", nullptr, group_conn); - - // // Proxy settings - // auto group_proxy = add_line("Proxy settings", ""); - // add_line("URL", "", nullptr, group_proxy); - // add_line("Authentication method", "", nullptr, group_proxy); - // add_line("Username", "", nullptr, group_proxy); - // add_line("Password", "", nullptr, group_proxy); - // add_line("SSL CA certificate", "", nullptr, group_proxy); - // add_line("SSL client certificate", "", nullptr, group_proxy); - // add_line("SSL client key", "", nullptr, group_proxy); - // add_line("Verify SSL certificate", "", nullptr, group_proxy); - - // auto group_misc = add_line("Miscellaneous", ""); - // add_line("Load comps groups", "", nullptr, group_misc); - // add_line("Report \"countme\" statistics", "", nullptr, group_misc); - // add_line("Enable DeltaRPM", "", nullptr, group_misc); - // add_line("DeltaRPM percentage", "", nullptr, group_misc); - // add_line("Use the fastest mirror", "", nullptr, group_misc); - // add_line("Repository provides module hotfixes", "", nullptr, group_misc); - // add_line("Use zchunk repodata", "", nullptr, group_misc); - // } - - // Repodata - if (enabled) { - auto group_repodata = add_line("Repodata info", "", nullptr); - add_line("Available packages", repo.get_available_pkgs(), nullptr, group_repodata); - add_line("Total packages", repo.get_pkgs(), nullptr, group_repodata); - - std::string size = libdnf5::cli::utils::units::format_size_aligned(static_cast(repo.get_size())); - add_line("Size", size, nullptr, group_repodata); - - auto content_tags = repo.get_content_tags(); - if (!content_tags.empty()) { - add_line("Content tags", content_tags, nullptr, group_repodata); - } - - auto distro_tags_flat = repo.get_distro_tags(); - if (!distro_tags_flat.empty()) { - std::vector distro_tags; - for (auto & key_value : distro_tags_flat) { - distro_tags.push_back(key_value.second + " (" + key_value.first + ")"); - } - add_line("Distro tags", distro_tags, nullptr, group_repodata); - } - - add_line("Revision", repo.get_revision(), nullptr, group_repodata); - - add_line("Updated", libdnf5::utils::string::format_epoch(repo.get_max_timestamp()), nullptr, group_repodata); - } - - /* -general connection settings? - bandwidth - ip_resolve - max_parallel_downloads - minrate - retries - throttle - timeout - user_agent -*/ -} + void add_repo(IRepoInfo & repo); + void print(); +private: + class Impl; + std::unique_ptr p_impl; +}; } // namespace libdnf5::cli::output - #endif // LIBDNF5_CLI_OUTPUT_REPO_INFO_HPP diff --git a/include/libdnf5-cli/output/repolist.hpp b/include/libdnf5-cli/output/repolist.hpp index 099355cc9..f0401d1ff 100644 --- a/include/libdnf5-cli/output/repolist.hpp +++ b/include/libdnf5-cli/output/repolist.hpp @@ -21,60 +21,16 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_REPOLIST_HPP #define LIBDNF5_CLI_OUTPUT_REPOLIST_HPP -#include "libdnf5-cli/tty.hpp" +#include "interfaces/repo.hpp" -#include +#include namespace libdnf5::cli::output { // repository list table columns enum { COL_REPO_ID, COL_REPO_NAME, COL_REPO_STATUS }; -static struct libscols_table * create_repolist_table(bool with_status) { - struct libscols_table * table = scols_new_table(); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, 1); - scols_table_enable_maxout(table, 1); - } - struct libscols_column * cl = scols_table_new_column(table, "repo id", 0.4, 0); - scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); - scols_table_new_column(table, "repo name", 0.5, SCOLS_FL_TRUNC); - if (with_status) { - scols_table_new_column(table, "status", 0.1, SCOLS_FL_RIGHT); - } - return table; -} - -static void add_line_into_repolist_table( - struct libscols_table * table, bool with_status, const char * id, const char * descr, bool enabled) { - struct libscols_line * ln = scols_table_new_line(table, NULL); - scols_line_set_data(ln, COL_REPO_ID, id); - scols_line_set_data(ln, COL_REPO_NAME, descr); - if (with_status) { - scols_line_set_data(ln, COL_REPO_STATUS, enabled ? "enabled" : "disabled"); - struct libscols_cell * cl = scols_line_get_cell(ln, COL_REPO_STATUS); - scols_cell_set_color(cl, enabled ? "green" : "red"); - } -} - -template -static void print_repolist_table(Query query, bool with_status, size_t sort_column) { - auto table = create_repolist_table(with_status); - for (auto & repo : query.get_data()) { - add_line_into_repolist_table( - table, - with_status, - repo->get_id().c_str(), - repo->get_name().c_str(), //repo->get_config().get_name_option().get_value().c_str(), - repo->is_enabled()); - } - auto cl = scols_table_get_column(table, sort_column); - scols_sort_table(table, cl); - - scols_print_table(table); - scols_unref_table(table); -} - +void print_repolist_table(const std::vector> & repos, bool with_status, size_t sort_column); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/repoquery.hpp b/include/libdnf5-cli/output/repoquery.hpp index e0ab5e590..bb616daaa 100644 --- a/include/libdnf5-cli/output/repoquery.hpp +++ b/include/libdnf5-cli/output/repoquery.hpp @@ -22,7 +22,6 @@ along with libdnf. If not, see . #define LIBDNF5_CLI_OUTPUT_REPOQUERY_HPP #include -#include namespace libdnf5::cli::output { diff --git a/include/libdnf5-cli/output/transaction_table.hpp b/include/libdnf5-cli/output/transaction_table.hpp index 1b08fda08..d4334868c 100644 --- a/include/libdnf5-cli/output/transaction_table.hpp +++ b/include/libdnf5-cli/output/transaction_table.hpp @@ -21,577 +21,41 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_TRANSACTION_TABLE_HPP #define LIBDNF5_CLI_OUTPUT_TRANSACTION_TABLE_HPP -#include "smartcols_table_wrapper.hpp" +#include "interfaces/transaction.hpp" -#include "libdnf5-cli/tty.hpp" -#include "libdnf5-cli/utils/units.hpp" - -#include -#include -#include -#include -#include -#include +#include +#include #include -#include - namespace libdnf5::cli::output { - -enum { COL_NAME, COL_ARCH, COL_EVR, COL_REPO, COL_SIZE }; - - -static const char * action_color(libdnf5::transaction::TransactionItemAction action) { - switch (action) { - case libdnf5::transaction::TransactionItemAction::INSTALL: - case libdnf5::transaction::TransactionItemAction::UPGRADE: - case libdnf5::transaction::TransactionItemAction::REINSTALL: - case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: - case libdnf5::transaction::TransactionItemAction::ENABLE: - return "green"; - case libdnf5::transaction::TransactionItemAction::DOWNGRADE: - case libdnf5::transaction::TransactionItemAction::RESET: - return "magenta"; - case libdnf5::transaction::TransactionItemAction::REMOVE: - case libdnf5::transaction::TransactionItemAction::DISABLE: - return "red"; - case libdnf5::transaction::TransactionItemAction::REPLACED: - return "halfbright"; - } - - libdnf_throw_assertion("Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); -} - -class ActionHeaderPrinter { -public: - ActionHeaderPrinter(SmartcolsTableWrapper & table) : table(table) {} - - // TODO(lukash) to bring more sanity into the templated functions here, it - // would be better if the Transaction template type of - // print_transaction_table() was required to have a TransactionItem type - // defined inside, so that the ActionHeaderPrinter class could be templated - // instead of this method, and we could do (in print_transaction_table(), - // where this class is instantiated): - // ActionHeaderPrinter action_header_printer(...); - template - struct libscols_line * print(const T & tspkg) { - if (!current_action || *current_action != tspkg.get_action() || - ((*current_action == libdnf5::transaction::TransactionItemAction::INSTALL || - *current_action == libdnf5::transaction::TransactionItemAction::REMOVE) && - *current_reason != tspkg.get_reason())) { - auto reason = tspkg.get_reason(); - auto action = tspkg.get_action(); - current_header_line = scols_table_new_line(*table, NULL); - std::string text; - - switch (action) { - case libdnf5::transaction::TransactionItemAction::INSTALL: - text = "Installing"; - if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { - text += " dependencies"; - } else if (reason == libdnf5::transaction::TransactionItemReason::WEAK_DEPENDENCY) { - text += " weak dependencies"; - } else if (reason == libdnf5::transaction::TransactionItemReason::GROUP) { - text += " group/module packages"; - } - break; - case libdnf5::transaction::TransactionItemAction::UPGRADE: - text = "Upgrading"; - break; - case libdnf5::transaction::TransactionItemAction::DOWNGRADE: - text = "Downgrading"; - break; - case libdnf5::transaction::TransactionItemAction::REINSTALL: - text = "Reinstalling"; - break; - case libdnf5::transaction::TransactionItemAction::REMOVE: - text = "Removing"; - if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { - text += " dependent packages"; - } else if (reason == libdnf5::transaction::TransactionItemReason::CLEAN) { - text += " unused dependencies"; - } - break; - case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: - text = "Changing reason"; - break; - case libdnf5::transaction::TransactionItemAction::REPLACED: - case libdnf5::transaction::TransactionItemAction::ENABLE: - case libdnf5::transaction::TransactionItemAction::DISABLE: - case libdnf5::transaction::TransactionItemAction::RESET: - libdnf_throw_assertion( - "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); - } - - text += ":"; - - scols_line_set_data(current_header_line, COL_NAME, text.c_str()); - - current_action = action; - current_reason = reason; - } - - return current_header_line; - } - -private: - SmartcolsTableWrapper & table; - struct libscols_line * current_header_line = nullptr; - std::optional current_action; - std::optional current_reason; -}; - - -class ActionHeaderPrinterEnvironment { +class TransactionTable { public: - ActionHeaderPrinterEnvironment(SmartcolsTableWrapper & table) : table(table) {} - - template - struct libscols_line * print(const T & tsgrp) { - if (!current_action || *current_action != tsgrp.get_action() || !current_reason || - *current_reason != tsgrp.get_reason()) { - auto reason = tsgrp.get_reason(); - auto action = tsgrp.get_action(); - current_header_line = scols_table_new_line(*table, NULL); - std::string text; - - switch (action) { - case libdnf5::transaction::TransactionItemAction::INSTALL: - text = "Installing environmental groups"; - break; - case libdnf5::transaction::TransactionItemAction::REMOVE: - text = "Removing environmental groups"; - break; - case libdnf5::transaction::TransactionItemAction::UPGRADE: - text = "Upgrading environmental groups"; - break; - default: - libdnf_throw_assertion( - "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); - } + explicit TransactionTable(ITransaction & transaction); + ~TransactionTable(); - text += ":"; + TransactionTable(const TransactionTable &) = delete; + TransactionTable & operator=(const TransactionTable &) = delete; - scols_line_set_data(current_header_line, COL_NAME, text.c_str()); + TransactionTable(TransactionTable && src); + TransactionTable & operator=(TransactionTable && src); - current_action = action; - current_reason = reason; - } + void print_table(); + void print_summary() const; - return current_header_line; - } + void set_colors_enabled(bool enable); + void set_term_width(std::size_t width); + void set_output_stream(std::FILE * fd); private: - SmartcolsTableWrapper & table; - struct libscols_line * current_header_line = nullptr; - std::optional current_action; - std::optional current_reason; + class Impl; + ImplPtr p_impl; }; +void print_resolve_logs(const ITransaction & transaction, std::ostream & stream = std::cerr); -class ActionHeaderPrinterGroup { -public: - ActionHeaderPrinterGroup(SmartcolsTableWrapper & table) : table(table) {} - - // TODO(lukash) to bring more sanity into the templated functions here, it - // would be better if the Transaction template type of - // print_transaction_table() was required to have a TransactionItem type - // defined inside, so that the ActionHeaderPrinter class could be templated - // instead of this method, and we could do (in print_transaction_table(), - // where this class is instantiated): - // ActionHeaderPrinter action_header_printer(...); - template - struct libscols_line * print(const T & tsgrp) { - if (!current_action || *current_action != tsgrp.get_action() || !current_reason || - *current_reason != tsgrp.get_reason()) { - auto reason = tsgrp.get_reason(); - auto action = tsgrp.get_action(); - current_header_line = scols_table_new_line(*table, NULL); - std::string text; - - switch (action) { - case libdnf5::transaction::TransactionItemAction::INSTALL: - text = "Installing groups"; - if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { - text += " dependencies"; - } - break; - case libdnf5::transaction::TransactionItemAction::REMOVE: - text = "Removing groups"; - break; - case libdnf5::transaction::TransactionItemAction::UPGRADE: - text = "Upgrading groups"; - break; - default: - libdnf_throw_assertion( - "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); - } - - text += ":"; - - scols_line_set_data(current_header_line, COL_NAME, text.c_str()); - - current_action = action; - current_reason = reason; - } - - return current_header_line; - } - -private: - SmartcolsTableWrapper & table; - struct libscols_line * current_header_line = nullptr; - std::optional current_action; - std::optional current_reason; -}; - - -class ActionHeaderPrinterModule { -public: - ActionHeaderPrinterModule(SmartcolsTableWrapper & table) : table(table) {} - - template - struct libscols_line * print(const T & tsmodule) { - if (!current_action || *current_action != tsmodule.get_action() || !current_reason || - *current_reason != tsmodule.get_reason()) { - auto reason = tsmodule.get_reason(); - auto action = tsmodule.get_action(); - current_header_line = scols_table_new_line(*table, NULL); - std::string text; - - switch (action) { - case libdnf5::transaction::TransactionItemAction::ENABLE: - text = "Enabling module streams"; - break; - case libdnf5::transaction::TransactionItemAction::DISABLE: - text = "Disabling modules"; - break; - case libdnf5::transaction::TransactionItemAction::RESET: - text = "Resetting modules"; - break; - default: - libdnf_throw_assertion( - "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); - } - - text += ":"; - - scols_line_set_data(current_header_line, COL_NAME, text.c_str()); - - current_action = action; - current_reason = reason; - } - - return current_header_line; - } - -private: - SmartcolsTableWrapper & table; - struct libscols_line * current_header_line = nullptr; - std::optional current_action; - std::optional current_reason; -}; - - -class TransactionSummary { -public: - void add(const libdnf5::transaction::TransactionItemAction & action) { - switch (action) { - case libdnf5::transaction::TransactionItemAction::INSTALL: - installs++; - break; - case libdnf5::transaction::TransactionItemAction::UPGRADE: - upgrades++; - break; - case libdnf5::transaction::TransactionItemAction::DOWNGRADE: - downgrades++; - break; - case libdnf5::transaction::TransactionItemAction::REINSTALL: - reinstalls++; - break; - case libdnf5::transaction::TransactionItemAction::REMOVE: - removes++; - break; - case libdnf5::transaction::TransactionItemAction::REPLACED: - replaced++; - break; - case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: - reason_changes++; - break; - case libdnf5::transaction::TransactionItemAction::ENABLE: - case libdnf5::transaction::TransactionItemAction::DISABLE: - case libdnf5::transaction::TransactionItemAction::RESET: - // Only package change counts are reported. - break; - } - } - - void print(std::ostream & stream = std::cout) { - stream << "\nTransaction Summary:\n"; - if (installs != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Installing:", installs); - } - if (reinstalls != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Reinstalling:", reinstalls); - } - if (upgrades != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Upgrading:", upgrades); - } - if (replaced != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Replacing:", replaced); - } - if (removes != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Removing:", removes); - } - if (downgrades != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Downgrading:", downgrades); - } - if (reason_changes != 0) { - stream << fmt::format(" {:15} {:4} packages\n", "Changing reason:", reason_changes); - } - stream << std::endl; - } - -private: - int installs = 0; - int reinstalls = 0; - int upgrades = 0; - int downgrades = 0; - int removes = 0; - int replaced = 0; - int reason_changes = 0; -}; - - -template -static bool transaction_package_cmp(const TransactionPackage & tspkg1, const TransactionPackage & tspkg2) { - if (tspkg1.get_action() != tspkg2.get_action()) { - return tspkg1.get_action() > tspkg2.get_action(); - } - - // INSTALL and REMOVE actions are divided (printed) into groups according to the reason. - auto current_action = tspkg1.get_action(); - if ((current_action == libdnf5::transaction::TransactionItemAction::INSTALL || - current_action == libdnf5::transaction::TransactionItemAction::REMOVE) && - tspkg1.get_reason() != tspkg2.get_reason()) { - return tspkg1.get_reason() > tspkg2.get_reason(); - } - - return libdnf5::rpm::cmp_naevr(tspkg1.get_package(), tspkg2.get_package()); -} - - -template -static bool transaction_group_cmp(const TransactionGroup & tsgrp1, const TransactionGroup & tsgrp2) { - if (tsgrp1.get_action() != tsgrp2.get_action()) { - return tsgrp1.get_action() > tsgrp2.get_action(); - } - - if (tsgrp1.get_reason() != tsgrp2.get_reason()) { - return tsgrp1.get_reason() > tsgrp2.get_reason(); - } - - return tsgrp1.get_group().get_groupid() > tsgrp2.get_group().get_groupid(); -} - - -/// Prints all transaction problems -template -void print_resolve_logs(Transaction transaction, std::ostream & stream = std::cerr) { - const std::vector logs = transaction.get_resolve_logs_as_strings(); - for (const auto & log : logs) { - stream << log << std::endl; - } - if (logs.size() > 0) { - stream << std::endl; - } -} - -template -SmartcolsTableWrapper create_transaction_table(Transaction & transaction, TransactionSummary & ts_summary) { - SmartcolsTableWrapper tb; - - auto column = scols_table_new_column(*tb, "Package", 0.3, SCOLS_FL_TREE); - auto header = scols_column_get_header(column); - scols_cell_set_color(header, "bold"); - - column = scols_table_new_column(*tb, "Arch", 6, 0); - header = scols_column_get_header(column); - scols_cell_set_color(header, "bold"); - - column = scols_table_new_column(*tb, "Version", 0.3, SCOLS_FL_TRUNC); - header = scols_column_get_header(column); - scols_cell_set_color(header, "bold"); - - column = scols_table_new_column(*tb, "Repository", 0.1, SCOLS_FL_TRUNC); - header = scols_column_get_header(column); - scols_cell_set_color(header, "bold"); - - column = scols_table_new_column(*tb, "Size", 9, SCOLS_FL_RIGHT); - header = scols_column_get_header(column); - scols_cell_set_color(header, "bold"); - - scols_table_enable_maxout(*tb, 1); - scols_table_enable_colors(*tb, libdnf5::cli::tty::is_interactive()); - - // TODO(dmach): use colors from config - // TODO(dmach): highlight version changes (rebases) - // TODO(dmach): consider reordering so the major changes (installs, obsoletes, removals) are at the bottom next to the confirmation question - // TODO(jrohel): Print relations with obsoleted packages - - auto tspkgs = transaction.get_transaction_packages(); - std::sort(tspkgs.begin(), tspkgs.end(), transaction_package_cmp); - auto tsgrps = transaction.get_transaction_groups(); - std::sort(tsgrps.begin(), tsgrps.end(), transaction_group_cmp); - - struct libscols_line * header_ln = nullptr; - ActionHeaderPrinter action_header_printer(tb); - - for (auto & tspkg : tspkgs) { - // TODO(lukash) handle OBSOLETED correctly through the transaction table output - if (tspkg.get_action() == libdnf5::transaction::TransactionItemAction::REPLACED) { - ts_summary.add(tspkg.get_action()); - continue; - } - - auto pkg = tspkg.get_package(); - - header_ln = action_header_printer.print(tspkg); - - struct libscols_line * ln = scols_table_new_line(*tb, header_ln); - scols_line_set_data(ln, COL_NAME, pkg.get_name().c_str()); - scols_line_set_data(ln, COL_ARCH, pkg.get_arch().c_str()); - scols_line_set_data(ln, COL_EVR, pkg.get_evr().c_str()); - if (tspkg.get_action() == libdnf5::transaction::TransactionItemAction::REMOVE) { - scols_line_set_data(ln, COL_REPO, pkg.get_from_repo_id().c_str()); - } else { - scols_line_set_data(ln, COL_REPO, pkg.get_repo_id().c_str()); - } - auto tspkg_size = static_cast(pkg.get_install_size()); - scols_line_set_data(ln, COL_SIZE, libdnf5::cli::utils::units::format_size_aligned(tspkg_size).c_str()); - auto ce = scols_line_get_cell(ln, COL_NAME); - scols_cell_set_color(ce, action_color(tspkg.get_action())); - - ts_summary.add(tspkg.get_action()); - if (tspkg.get_action() == libdnf5::transaction::TransactionItemAction::REASON_CHANGE) { - auto replaced_color = action_color(libdnf5::transaction::TransactionItemAction::REPLACED); - struct libscols_line * ln_reason = scols_table_new_line(*tb, ln); - std::string reason = fmt::format( - "{} -> {}", - libdnf5::transaction::transaction_item_reason_to_string(pkg.get_reason()), - libdnf5::transaction::transaction_item_reason_to_string(tspkg.get_reason())); - scols_line_set_data(ln_reason, COL_NAME, reason.c_str()); - scols_cell_set_color(scols_line_get_cell(ln_reason, COL_NAME), replaced_color); - } - for (auto & replaced : tspkg.get_replaces()) { - // highlight incoming packages with epoch/version change - if (tspkg.get_package().get_epoch() != replaced.get_epoch() || - tspkg.get_package().get_version() != replaced.get_version()) { - auto cl_evr = scols_line_get_cell(ln, COL_EVR); - scols_cell_set_color(cl_evr, "bold"); - } - - struct libscols_line * ln_replaced = scols_table_new_line(*tb, ln); - // TODO(jmracek) Translate it - std::string name("replacing "); - name.append(replaced.get_name()); - scols_line_set_data(ln_replaced, COL_NAME, name.c_str()); - scols_line_set_data(ln_replaced, COL_ARCH, replaced.get_arch().c_str()); - scols_line_set_data(ln_replaced, COL_EVR, replaced.get_evr().c_str()); - scols_line_set_data(ln_replaced, COL_REPO, replaced.get_from_repo_id().c_str()); - - auto replaced_size = static_cast(replaced.get_install_size()); - scols_line_set_data( - ln_replaced, COL_SIZE, libdnf5::cli::utils::units::format_size_aligned(replaced_size).c_str()); - auto replaced_color = action_color(libdnf5::transaction::TransactionItemAction::REPLACED); - auto obsoleted_color = "brown"; - - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_EVR), replaced_color); - if (pkg.get_arch() == replaced.get_arch()) { - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_ARCH), replaced_color); - } else { - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_ARCH), obsoleted_color); - } - if (pkg.get_name() == replaced.get_name()) { - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_NAME), replaced_color); - } else { - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_NAME), obsoleted_color); - } - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_REPO), replaced_color); - scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_SIZE), replaced_color); - } - } - - ActionHeaderPrinterGroup action_header_printer_group(tb); - for (auto & tsgrp : tsgrps) { - auto grp = tsgrp.get_group(); - - header_ln = action_header_printer_group.print(tsgrp); - - struct libscols_line * ln = scols_table_new_line(*tb, header_ln); - auto const grp_name = grp.get_name(); - if (grp_name.empty()) { - scols_line_set_data(ln, COL_NAME, ""); - } else { - scols_line_set_data(ln, COL_NAME, grp_name.c_str()); - } - auto ce = scols_line_get_cell(ln, COL_NAME); - scols_cell_set_color(ce, action_color(tsgrp.get_action())); - } - - ActionHeaderPrinterEnvironment action_header_printer_environment(tb); - for (auto & tsenv : transaction.get_transaction_environments()) { - auto env = tsenv.get_environment(); - - header_ln = action_header_printer_environment.print(tsenv); - - struct libscols_line * ln = scols_table_new_line(*tb, header_ln); - auto const env_name = env.get_name(); - if (env_name.empty()) { - scols_line_set_data(ln, COL_NAME, ""); - } else { - scols_line_set_data(ln, COL_NAME, env_name.c_str()); - } - auto ce = scols_line_get_cell(ln, COL_NAME); - scols_cell_set_color(ce, action_color(tsenv.get_action())); - } - - ActionHeaderPrinterModule action_header_printer_module(tb); - for (auto & tsmodule : transaction.get_transaction_modules()) { - header_ln = action_header_printer_module.print(tsmodule); - - struct libscols_line * ln = scols_table_new_line(*tb, header_ln); - scols_line_set_data(ln, COL_NAME, tsmodule.get_module_name().c_str()); - scols_line_set_data(ln, COL_EVR, tsmodule.get_module_stream().c_str()); - scols_cell_set_color(scols_line_get_cell(ln, COL_NAME), action_color(tsmodule.get_action())); - } - - return tb; -} - -template -bool print_transaction_table(Transaction & transaction) { - // even correctly resolved transaction can contain some warnings / hints / infos - // in resolve logs (e.g. the package user wanted to install is already installed). - // Present them to the user. - print_resolve_logs(transaction); - - if (transaction.empty()) { - std::cout << "Nothing to do." << std::endl; - return false; - } - - TransactionSummary ts_summary; - auto tb = create_transaction_table(transaction, ts_summary); - - scols_print_table(*tb); - - ts_summary.print(); - - return true; -} +bool print_transaction_table(ITransaction & transaction); } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/transactioninfo.hpp b/include/libdnf5-cli/output/transactioninfo.hpp index 5d8e17f6b..fef5679b5 100644 --- a/include/libdnf5-cli/output/transactioninfo.hpp +++ b/include/libdnf5-cli/output/transactioninfo.hpp @@ -21,42 +21,12 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_TRANSACTIONINFO_HPP #define LIBDNF5_CLI_OUTPUT_TRANSACTIONINFO_HPP -#include "libdnf5-cli/output/key_value_table.hpp" -#include "libdnf5-cli/tty.hpp" - #include - namespace libdnf5::cli::output { void print_transaction_info(libdnf5::transaction::Transaction & transaction); -template -void print_transaction_item_table(std::vector items, const char * title) { - std::unique_ptr item_list(scols_new_table(), &scols_unref_table); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(item_list.get(), 1); - } - scols_cell_set_data(scols_table_get_title(item_list.get()), title); - - // The two spaces indent the table the same way as child lines in KeyValueTable - scols_table_new_column(item_list.get(), " Action", 0, 0); - scols_table_new_column(item_list.get(), "Package", 0, 0); - scols_table_new_column(item_list.get(), "Reason", 0, 0); - scols_table_new_column(item_list.get(), "Repository", 0, 0); - - for (auto & pkg : items) { - struct libscols_line * ln = scols_table_new_line(item_list.get(), NULL); - scols_line_set_data( - ln, 0, (" " + libdnf5::transaction::transaction_item_action_to_string(pkg.get_action())).c_str()); - scols_line_set_data(ln, 1, pkg.to_string().c_str()); - scols_line_set_data(ln, 2, libdnf5::transaction::transaction_item_reason_to_string(pkg.get_reason()).c_str()); - scols_line_set_data(ln, 3, pkg.get_repoid().c_str()); - } - - scols_print_table(item_list.get()); -} - } // namespace libdnf5::cli::output #endif // LIBDNF5_CLI_OUTPUT_TRANSACTIONINFO_HPP diff --git a/include/libdnf5-cli/output/transactionlist.hpp b/include/libdnf5-cli/output/transactionlist.hpp index 9ac253b3c..333d4274c 100644 --- a/include/libdnf5-cli/output/transactionlist.hpp +++ b/include/libdnf5-cli/output/transactionlist.hpp @@ -21,12 +21,8 @@ along with libdnf. If not, see . #ifndef LIBDNF5_CLI_OUTPUT_TRANSACTIONLIST_HPP #define LIBDNF5_CLI_OUTPUT_TRANSACTIONLIST_HPP -#include "libdnf5-cli/tty.hpp" - -#include #include - namespace libdnf5::cli::output { void print_transaction_list(std::vector & ts_list); diff --git a/libdnf5-cli/CMakeLists.txt b/libdnf5-cli/CMakeLists.txt index a321e4900..8e59731aa 100644 --- a/libdnf5-cli/CMakeLists.txt +++ b/libdnf5-cli/CMakeLists.txt @@ -39,7 +39,7 @@ target_link_libraries(libdnf5-cli PUBLIC ${LIBFMT_LIBRARIES}) pkg_check_modules(SMARTCOLS REQUIRED smartcols) list(APPEND LIBDNF5_CLI_PC_REQUIRES_PRIVATE "${SMARTCOLS_MODULE_NAME}") -target_link_libraries(libdnf5-cli PUBLIC ${SMARTCOLS_LIBRARIES}) +target_link_libraries(libdnf5-cli PRIVATE ${SMARTCOLS_LIBRARIES}) # sort the pkg-config requires and concatenate them into a string diff --git a/libdnf5-cli/output/advisoryinfo.cpp b/libdnf5-cli/output/advisoryinfo.cpp new file mode 100644 index 000000000..b80ddf5c7 --- /dev/null +++ b/libdnf5-cli/output/advisoryinfo.cpp @@ -0,0 +1,97 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/advisoryinfo.hpp" + +#include "key_value_table.hpp" +#include "utils/string.hpp" + +namespace libdnf5::cli::output { + +class AdvisoryInfo::Impl : public KeyValueTable { +public: + void add_advisory(IAdvisory & advisory); +}; + + +void AdvisoryInfo::Impl::add_advisory(IAdvisory & advisory) { + add_line("Name", advisory.get_name(), "bold"); + add_line("Title", advisory.get_title()); + add_line("Severity", advisory.get_severity()); + add_line("Type", advisory.get_type()); + add_line("Status", advisory.get_status()); + add_line("Vendor", advisory.get_vendor()); + add_line("Issued", libdnf5::utils::string::format_epoch(advisory.get_buildtime())); + add_lines("Description", libdnf5::utils::string::split(advisory.get_description(), "\n")); + add_line("Message", advisory.get_message()); + add_line("Rights", advisory.get_rights()); + + // References + for (auto & reference : advisory.get_references()) { + auto group_reference = add_line("Reference", ""); + add_line("Title", reference->get_title(), "bold", group_reference); + add_line("Id", reference->get_id(), nullptr, group_reference); + add_line("Type", reference->get_type_cstring(), nullptr, group_reference); + add_line("Url", reference->get_url(), nullptr, group_reference); + } + + // Collections + for (auto & collection : advisory.get_collections()) { + auto group_collection = add_line("Collection", ""); + + // Modules + auto modules = collection->get_modules(); + if (!modules.empty()) { + auto module_iter = modules.begin(); + add_line("Modules", (*module_iter)->get_nsvca(), nullptr, group_collection); + module_iter++; + while (module_iter != modules.end()) { + add_line("", (*module_iter)->get_nsvca(), nullptr, group_collection); + module_iter++; + } + } + + // Packages + auto packages = collection->get_packages(); + if (!packages.empty()) { + auto package_iter = packages.begin(); + add_line("Packages", (*package_iter)->get_nevra(), nullptr, group_collection); + package_iter++; + while (package_iter != packages.end()) { + add_line("", (*package_iter)->get_nevra(), nullptr, group_collection); + package_iter++; + } + } + } +} + + +AdvisoryInfo::AdvisoryInfo() : p_impl{new AdvisoryInfo::Impl} {} + +AdvisoryInfo::~AdvisoryInfo() = default; + +void AdvisoryInfo::add_advisory(IAdvisory & advisory) { + p_impl->add_advisory(advisory); +} + +void AdvisoryInfo::print() { + p_impl->print(); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/advisorylist.cpp b/libdnf5-cli/output/advisorylist.cpp index 27884c7b9..2501ba143 100644 --- a/libdnf5-cli/output/advisorylist.cpp +++ b/libdnf5-cli/output/advisorylist.cpp @@ -26,6 +26,8 @@ along with libdnf. If not, see . namespace libdnf5::cli::output { +namespace { + // advisory list table columns enum { COL_ADVISORY_ID, COL_ADVISORY_TYPE, COL_ADVISORY_SEVERITY, COL_ADVISORY_PACKAGE, COL_ADVISORY_BUILDTIME }; @@ -68,6 +70,40 @@ void add_line_into_advisorylist_table( } } +} // namespace + + +void print_advisorylist_table( + std::vector> & advisory_package_list_not_installed, + std::vector> & advisory_package_list_installed) { + struct libscols_table * table = create_advisorylist_table("Name"); + for (auto & adv_pkg : advisory_package_list_not_installed) { + auto advisory = adv_pkg->get_advisory(); + add_line_into_advisorylist_table( + table, + advisory->get_name().c_str(), + advisory->get_type().c_str(), + advisory->get_severity().c_str(), + adv_pkg->get_nevra().c_str(), + advisory->get_buildtime(), + false); + } + for (auto & adv_pkg : advisory_package_list_installed) { + auto advisory = adv_pkg->get_advisory(); + add_line_into_advisorylist_table( + table, + advisory->get_name().c_str(), + advisory->get_type().c_str(), + advisory->get_severity().c_str(), + adv_pkg->get_nevra().c_str(), + advisory->get_buildtime(), + true); + } + sort_advisorylist_table(table); + scols_print_table(table); + scols_unref_table(table); +} + void print_advisorylist_references_table( std::vector & advisory_package_list_not_installed, @@ -113,5 +149,4 @@ void print_advisorylist_references_table( scols_unref_table(table); } - } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/advisorysummary.cpp b/libdnf5-cli/output/advisorysummary.cpp index c0047c28b..9d946ba6f 100644 --- a/libdnf5-cli/output/advisorysummary.cpp +++ b/libdnf5-cli/output/advisorysummary.cpp @@ -20,13 +20,12 @@ along with libdnf. If not, see . #include "libdnf5-cli/output/advisorysummary.hpp" -#include "libdnf5-cli/output/key_value_table.hpp" +#include "key_value_table.hpp" #include namespace libdnf5::cli::output { - void print_advisorysummary_table(const libdnf5::advisory::AdvisoryQuery & advisories, const std::string & mode) { KeyValueTable output_table; std::cout << mode << " advisory information summary:" << std::endl; @@ -74,5 +73,4 @@ void print_advisorysummary_table(const libdnf5::advisory::AdvisoryQuery & adviso output_table.print(); } - } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/environmentinfo.cpp b/libdnf5-cli/output/environmentinfo.cpp new file mode 100644 index 000000000..7c28185f1 --- /dev/null +++ b/libdnf5-cli/output/environmentinfo.cpp @@ -0,0 +1,115 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/environmentinfo.hpp" + +#include "libdnf5-cli/tty.hpp" + +// TODO(lukash) include from common in a public libdnf-cli header +#include "utils/string.hpp" + +#include + + +namespace libdnf5::cli::output { + +namespace { + +void add_line_into_environmentinfo_table( + struct libscols_table * table, const char * key, const char * value, const char * color) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, 0, key); + scols_line_set_data(ln, 1, value); + + if (color && strcmp(color, "") != 0) { + auto cell_value = scols_line_get_cell(ln, 1); + scols_cell_set_color(cell_value, color); + } +} + + +void add_line_into_environmentinfo_table(struct libscols_table * table, const char * key, const char * value) { + add_line_into_environmentinfo_table(table, key, value, ""); +} + + +void add_groups(struct libscols_table * table, std::vector groups, const char * group_type_desc) { + if (groups.empty()) { + // don't even print the group type description + return; + } + + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, 0, group_type_desc); + + auto it = groups.begin(); + // put the first group at the same line as description + scols_line_set_data(ln, 1, it->c_str()); + it++; + + // put the remaining group on separate lines + for (; it != groups.end(); it++) { + struct libscols_line * group_ln = scols_table_new_line(table, ln); + scols_line_set_data(group_ln, 1, it->c_str()); + } +} + + +struct libscols_table * create_environmentinfo_table(IEnvironment & environment) { + struct libscols_table * table = scols_new_table(); + scols_table_enable_noheadings(table, 1); + scols_table_set_column_separator(table, " : "); + scols_table_new_column(table, "key", 20, SCOLS_FL_TREE); + struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); + scols_column_set_safechars(cl, "\n"); + scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, true); + } + auto sy = scols_new_symbols(); + scols_symbols_set_branch(sy, " "); + scols_symbols_set_right(sy, " "); + scols_symbols_set_vertical(sy, ""); + scols_table_set_symbols(table, sy); + scols_unref_symbols(sy); + + add_line_into_environmentinfo_table(table, "Id", environment.get_environmentid().c_str(), "bold"); + add_line_into_environmentinfo_table(table, "Name", environment.get_name().c_str()); + add_line_into_environmentinfo_table(table, "Description", environment.get_description().c_str()); + add_line_into_environmentinfo_table(table, "Order", environment.get_order().c_str()); + add_line_into_environmentinfo_table(table, "Installed", environment.get_installed() ? "True" : "False"); + add_line_into_environmentinfo_table( + table, "Repositories", libdnf5::utils::string::join(environment.get_repos(), ", ").c_str()); + + add_groups(table, environment.get_groups(), "Required groups"); + add_groups(table, environment.get_optional_groups(), "Optional groups"); + + return table; +} + +} // namespace + + +void print_environmentinfo_table(IEnvironment & environment) { + struct libscols_table * table = create_environmentinfo_table(environment); + scols_print_table(table); + scols_unref_table(table); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/environmentlist.cpp b/libdnf5-cli/output/environmentlist.cpp new file mode 100644 index 000000000..0167989eb --- /dev/null +++ b/libdnf5-cli/output/environmentlist.cpp @@ -0,0 +1,77 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/environmentlist.hpp" + +#include "libdnf5-cli/tty.hpp" + +#include + +namespace libdnf5::cli::output { + +namespace { + +// environment list table columns +enum { COL_ENVIRONMENT_ID, COL_ENVIRONMENT_NAME, COL_INSTALLED }; + + +struct libscols_table * create_environmentlist_table() { + struct libscols_table * table = scols_new_table(); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, 1); + } + struct libscols_column * cl = scols_table_new_column(table, "ID", 20, 0); + scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); + scols_table_new_column(table, "Name", 0.5, SCOLS_FL_TRUNC); + scols_table_new_column(table, "Installed", 0.1, SCOLS_FL_RIGHT); + return table; +} + + +void add_line_into_environmentlist_table( + struct libscols_table * table, const char * id, const char * name, bool installed) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, COL_ENVIRONMENT_ID, id); + scols_line_set_data(ln, COL_ENVIRONMENT_NAME, name); + scols_line_set_data(ln, COL_INSTALLED, installed ? "yes" : "no"); + if (installed) { + struct libscols_cell * cl = scols_line_get_cell(ln, COL_INSTALLED); + scols_cell_set_color(cl, "green"); + } +} + +} // namespace + + +void print_environmentlist_table(std::vector> & environment_list) { + struct libscols_table * table = create_environmentlist_table(); + for (auto & environment : environment_list) { + add_line_into_environmentlist_table( + table, + environment->get_environmentid().c_str(), + environment->get_name().c_str(), + environment->get_installed()); + } + auto cl = scols_table_get_column(table, COL_ENVIRONMENT_ID); + scols_sort_table(table, cl); + scols_print_table(table); + scols_unref_table(table); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/groupinfo.cpp b/libdnf5-cli/output/groupinfo.cpp new file mode 100644 index 000000000..3b1210142 --- /dev/null +++ b/libdnf5-cli/output/groupinfo.cpp @@ -0,0 +1,129 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/groupinfo.hpp" + +#include "libdnf5-cli/tty.hpp" + +// TODO(lukash) include from common in a public libdnf-cli header +#include "utils/string.hpp" + +#include + + +namespace libdnf5::cli::output { + +namespace { + +void add_line_into_groupinfo_table( + struct libscols_table * table, const char * key, const char * value, const char * color) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, 0, key); + scols_line_set_data(ln, 1, value); + + if (color && strcmp(color, "") != 0) { + auto cell_value = scols_line_get_cell(ln, 1); + scols_cell_set_color(cell_value, color); + } +} + + +void add_line_into_groupinfo_table(struct libscols_table * table, const char * key, const char * value) { + add_line_into_groupinfo_table(table, key, value, ""); +} + + +void add_packages( + struct libscols_table * table, IGroup & group, libdnf5::comps::PackageType pkg_type, const char * pkg_type_desc) { + std::set packages; + + // we don't mind iterating through all packages in every add_packages() call, + // because performance is not an issue here + for (auto & package : group.get_packages()) { + if (package->get_type() == pkg_type) { + packages.emplace(package->get_name()); + } + } + + if (packages.empty()) { + // don't even print the package type description + return; + } + + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, 0, pkg_type_desc); + + auto it = packages.begin(); + // put the first package at the same line as description + scols_line_set_data(ln, 1, it->c_str()); + it++; + + // put the remaining packages on separate lines + for (; it != packages.end(); it++) { + struct libscols_line * pkg_ln = scols_table_new_line(table, ln); + scols_line_set_data(pkg_ln, 1, it->c_str()); + } +} + + +struct libscols_table * create_groupinfo_table(IGroup & group) { + struct libscols_table * table = scols_new_table(); + scols_table_enable_noheadings(table, 1); + scols_table_set_column_separator(table, " : "); + scols_table_new_column(table, "key", 20, SCOLS_FL_TREE); + struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); + scols_column_set_safechars(cl, "\n"); + scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, true); + } + auto sy = scols_new_symbols(); + scols_symbols_set_branch(sy, " "); + scols_symbols_set_right(sy, " "); + scols_symbols_set_vertical(sy, ""); + scols_table_set_symbols(table, sy); + scols_unref_symbols(sy); + + add_line_into_groupinfo_table(table, "Id", group.get_groupid().c_str(), "bold"); + add_line_into_groupinfo_table(table, "Name", group.get_name().c_str()); + add_line_into_groupinfo_table(table, "Description", group.get_description().c_str()); + add_line_into_groupinfo_table(table, "Installed", group.get_installed() ? "yes" : "no"); + add_line_into_groupinfo_table(table, "Order", group.get_order().c_str()); + add_line_into_groupinfo_table(table, "Langonly", group.get_langonly().c_str()); + add_line_into_groupinfo_table(table, "Uservisible", group.get_uservisible() ? "yes" : "no"); + add_line_into_groupinfo_table(table, "Repositories", libdnf5::utils::string::join(group.get_repos(), ", ").c_str()); + + add_packages(table, group, libdnf5::comps::PackageType::MANDATORY, "Mandatory packages"); + add_packages(table, group, libdnf5::comps::PackageType::DEFAULT, "Default packages"); + add_packages(table, group, libdnf5::comps::PackageType::OPTIONAL, "Optional packages"); + add_packages(table, group, libdnf5::comps::PackageType::CONDITIONAL, "Conditional packages"); + + return table; +} + +} // namespace + + +void print_groupinfo_table(IGroup & group) { + struct libscols_table * table = create_groupinfo_table(group); + scols_print_table(table); + scols_unref_table(table); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/grouplist.cpp b/libdnf5-cli/output/grouplist.cpp new file mode 100644 index 000000000..57ab518ad --- /dev/null +++ b/libdnf5-cli/output/grouplist.cpp @@ -0,0 +1,74 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/grouplist.hpp" + +#include "libdnf5-cli/tty.hpp" + +#include + +namespace libdnf5::cli::output { + +namespace { + +// group list table columns +enum { COL_GROUP_ID, COL_GROUP_NAME, COL_INSTALLED }; + + +struct libscols_table * create_grouplist_table() { + struct libscols_table * table = scols_new_table(); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, 1); + } + struct libscols_column * cl = scols_table_new_column(table, "ID", 20, 0); + scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); + scols_table_new_column(table, "Name", 0.5, SCOLS_FL_TRUNC); + scols_table_new_column(table, "Installed", 0.1, SCOLS_FL_RIGHT); + return table; +} + + +void add_line_into_grouplist_table(struct libscols_table * table, const char * id, const char * name, bool installed) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, COL_GROUP_ID, id); + scols_line_set_data(ln, COL_GROUP_NAME, name); + scols_line_set_data(ln, COL_INSTALLED, installed ? "yes" : "no"); + if (installed) { + struct libscols_cell * cl = scols_line_get_cell(ln, COL_INSTALLED); + scols_cell_set_color(cl, "green"); + } +} + +} // namespace + + +//template +void print_grouplist_table(std::vector> & group_list) { + struct libscols_table * table = create_grouplist_table(); + for (auto & group : group_list) { + add_line_into_grouplist_table( + table, group->get_groupid().c_str(), group->get_name().c_str(), group->get_installed()); + } + auto cl = scols_table_get_column(table, COL_GROUP_ID); + scols_sort_table(table, cl); + scols_print_table(table); + scols_unref_table(table); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/key_value_table.cpp b/libdnf5-cli/output/key_value_table.cpp index ad62a295d..35e914f8e 100644 --- a/libdnf5-cli/output/key_value_table.cpp +++ b/libdnf5-cli/output/key_value_table.cpp @@ -18,7 +18,7 @@ along with libdnf. If not, see . */ -#include "libdnf5-cli/output/key_value_table.hpp" +#include "key_value_table.hpp" #include "utils/string.hpp" @@ -31,7 +31,6 @@ along with libdnf. If not, see . namespace libdnf5::cli::output { - KeyValueTable::KeyValueTable() { tb = scols_new_table(); if (libdnf5::cli::tty::is_interactive()) { @@ -88,6 +87,7 @@ struct libscols_line * KeyValueTable::add_line( return add_line(key, libdnf5::utils::string::join(value, " "), color, parent); } + struct libscols_line * KeyValueTable::add_lines( const char * key, const std::vector & values, const char * color, struct libscols_line * parent) { struct libscols_line * added_key_line = NULL; @@ -102,11 +102,11 @@ struct libscols_line * KeyValueTable::add_lines( return added_key_line; } + void KeyValueTable::drop_line_if_no_children(struct libscols_line * line) { if (scols_line_has_children(line) == 0) { scols_table_remove_line(tb, line); } } - } // namespace libdnf5::cli::output diff --git a/include/libdnf5-cli/output/key_value_table.hpp b/libdnf5-cli/output/key_value_table.hpp similarity index 99% rename from include/libdnf5-cli/output/key_value_table.hpp rename to libdnf5-cli/output/key_value_table.hpp index 38136a3a9..bd08d83a4 100644 --- a/include/libdnf5-cli/output/key_value_table.hpp +++ b/libdnf5-cli/output/key_value_table.hpp @@ -26,10 +26,8 @@ along with libdnf. If not, see . #include #include - namespace libdnf5::cli::output { - class KeyValueTable { public: explicit KeyValueTable(); @@ -69,8 +67,6 @@ class KeyValueTable { struct libscols_table * tb = nullptr; }; - } // namespace libdnf5::cli::output - #endif // LIBDNF5_CLI_OUTPUT_KEY_VALUE_TABLE_HPP diff --git a/libdnf5-cli/output/moduleinfo.cpp b/libdnf5-cli/output/moduleinfo.cpp index 88cc290a2..6dc9a20ff 100644 --- a/libdnf5-cli/output/moduleinfo.cpp +++ b/libdnf5-cli/output/moduleinfo.cpp @@ -20,8 +20,28 @@ along with libdnf. If not, see . #include "libdnf5-cli/output/moduleinfo.hpp" +#include "key_value_table.hpp" +#include "utils/string.hpp" + +#include +#include + +#include +#include + namespace libdnf5::cli::output { +namespace { + +const std::string MODULEINFO_TABLE_HINT = _("Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled, [a]ctive"); + +class ModuleInfo : public KeyValueTable { +public: + void add_module_item(IModuleItem & 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()) { @@ -39,5 +59,74 @@ void ModuleInfo::add_multiline_value(const char * key, const std::vector profile_names; + for (const auto & profile : module_item.get_profiles()) { + profile_names.push_back(profile->get_name() + (profile->is_default() ? " [d]" : "")); + } + + std::vector dependency_strings; + for (const auto & 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 auto 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]"); + } + if (module_item.is_active()) { + stream_string.append("[a]"); + } + stream_string = stream_string.empty() ? module_item.get_stream() : module_item.get_stream() + " " + stream_string; + + // Trim summary and description + auto summary = module_item.get_summary(); + libdnf5::utils::string::trim(summary); + auto description = module_item.get_description(); + libdnf5::utils::string::trim(description); + + 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_multiline_value("Summary", libdnf5::utils::string::split(summary, "\n")); + add_multiline_value("Description", libdnf5::utils::string::split(description, "\n")); + add_multiline_value("Requires", dependency_strings); + add_multiline_value("Artifacts", module_item.get_artifacts()); +} + +} // namespace + + +void print_module_item(IModuleItem & module_item) { + libdnf5::cli::output::ModuleInfo module_info; + module_info.add_module_item(module_item); + module_info.print(); + std::cout << std::endl; +} + + +void print_moduleinfo_table(std::vector> & module_list) { + std::map module_map; + for (const auto & module_item : module_list) { + module_map.emplace(module_item->get_full_identifier() + ":" + module_item->get_repo_id(), module_item.get()); + } + for (auto & module_item : module_map) { + print_module_item(*module_item.second); + } +} + + +void print_moduleinfo_table_hint() { + std::cout << MODULEINFO_TABLE_HINT << std::endl; +} } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/modulelist.cpp b/libdnf5-cli/output/modulelist.cpp new file mode 100644 index 000000000..0860b3938 --- /dev/null +++ b/libdnf5-cli/output/modulelist.cpp @@ -0,0 +1,118 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/modulelist.hpp" + +#include "utils/string.hpp" + +#include "libdnf5-cli/tty.hpp" + +#include +#include + +#include +#include + +namespace libdnf5::cli::output { + +namespace { + +// module list table columns +enum { COL_MODULE_NAME, COL_MODULE_STREAM, COL_MODULE_PROFILES, COL_MODULE_SUMMARY }; + +const std::string MODULELIST_TABLE_HINT = _("Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled"); + + +struct libscols_table * create_modulelist_table() { + struct libscols_table * table = scols_new_table(); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, 1); + } + struct libscols_column * cl = scols_table_new_column(table, "Name", 0.15, 0); + scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); + scols_table_new_column(table, "Stream", 0.15, SCOLS_FL_TRUNC); + scols_table_new_column(table, "Profiles", 0.3, SCOLS_FL_WRAP); + scols_table_new_column(table, "Summary", 0.4, SCOLS_FL_WRAP); + scols_table_enable_maxout(table, 1); + return table; +} + + +void add_line_into_modulelist_table( + struct libscols_table * table, + const char * name, + const char * stream, + const char * profiles, + const char * summary) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, COL_MODULE_NAME, name); + scols_line_set_data(ln, COL_MODULE_STREAM, stream); + scols_line_set_data(ln, COL_MODULE_PROFILES, profiles); + scols_line_set_data(ln, COL_MODULE_SUMMARY, summary); +} + +} // namespace + +void print_modulelist_table(const std::vector> & module_list) { + struct libscols_table * table = create_modulelist_table(); + std::set> name_stream_pairs; + for (auto & module_item : module_list) { + const std::string & name = module_item->get_name(); + const std::string & stream = module_item->get_stream(); + if (!name_stream_pairs.contains(make_pair(name, stream))) { + // 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() ? stream : stream + " " + stream_string; + + // Get profile strings (append [d] or [i] if needed) + std::vector profile_strings; + for (const auto & profile : module_item->get_profiles()) { + // TODO(pkratoch): Also show "[i]" for installed profiles + profile_strings.push_back(profile->is_default() ? profile->get_name() + " [d]" : profile->get_name()); + } + + add_line_into_modulelist_table( + table, + name.c_str(), + stream_string.c_str(), + utils::string::join(profile_strings, ", ").c_str(), + module_item->get_summary().c_str()); + name_stream_pairs.emplace(make_pair(name, stream)); + } + } + 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); +} + + +void print_modulelist_table_hint() { + std::cout << MODULELIST_TABLE_HINT << std::endl; +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/package_info_sections.cpp b/libdnf5-cli/output/package_info_sections.cpp index 66ef55ee5..cdfffc569 100644 --- a/libdnf5-cli/output/package_info_sections.cpp +++ b/libdnf5-cli/output/package_info_sections.cpp @@ -17,37 +17,104 @@ You should have received a copy of the GNU Lesser General Public License along with libdnf. If not, see . */ + #include "libdnf5-cli/output/package_info_sections.hpp" -#include "libdnf5-cli/tty.hpp" +#include "package_list_sections_impl.hpp" +#include "utils/string.hpp" + +#include "libdnf5-cli/output/adapters/package.hpp" +#include "libdnf5-cli/utils/units.hpp" #include -#include -#include #include -#include -#include namespace libdnf5::cli::output { +namespace { enum { COL_KEY, COL_VALUE }; -void PackageInfoSections::setup_cols() { - scols_table_new_column(table, "key", 1, 0); - scols_table_new_column(table, "value", 1, SCOLS_FL_WRAP); - scols_table_set_column_separator(table, " : "); -} - - -struct libscols_line * PackageInfoSections::add_line(const std::string & key, const ::std::string & value) { +struct libscols_line * add_line(struct libscols_table * table, const std::string & key, const ::std::string & value) { struct libscols_line * ln = scols_table_new_line(table, NULL); scols_line_set_data(ln, COL_KEY, key.c_str()); scols_line_set_data(ln, COL_VALUE, value.c_str()); return ln; } +} // namespace + + +PackageInfoSections::PackageInfoSections() = default; + +PackageInfoSections::~PackageInfoSections() = default; + + +bool PackageInfoSections::add_package( + IPackage & pkg, + const std::string & heading, + const std::unique_ptr & colorizer, + const std::vector & obsoletes) { + auto * table = p_impl->table; + struct libscols_line * first_line = add_line(table, "Name", pkg.get_name()); + if (colorizer) { + scols_line_set_color(first_line, colorizer->get_pkg_color(pkg).c_str()); + } + + add_line(table, "Epoch", pkg.get_epoch()); + add_line(table, "Version", pkg.get_version()); + add_line(table, "Release", pkg.get_release()); + add_line(table, "Architecture", pkg.get_arch()); + + if (!obsoletes.empty()) { + auto iterator = obsoletes.begin(); + add_line(table, "Obsoletes", iterator->get_full_nevra()); + ++iterator; + for (; iterator != obsoletes.end(); ++iterator) { + add_line(table, "", iterator->get_full_nevra()); + } + } + + if (!pkg.is_installed()) { + add_line( + table, "Download size", utils::units::format_size_aligned(static_cast(pkg.get_download_size()))); + } + add_line(table, "Installed size", utils::units::format_size_aligned(static_cast(pkg.get_install_size()))); + if (pkg.get_arch() != "src") { + add_line(table, "Source", pkg.get_sourcerpm()); + } + if (pkg.is_installed()) { + add_line(table, "From repository", pkg.get_from_repo_id()); + } else { + add_line(table, "Repository", pkg.get_repo_id()); + } + add_line(table, "Summary", pkg.get_summary()); + add_line(table, "URL", pkg.get_url()); + add_line(table, "License", pkg.get_license()); + + auto lines = libdnf5::utils::string::split(pkg.get_description(), "\n"); + auto iterator = lines.begin(); + add_line(table, "Description", *iterator); + ++iterator; + for (; iterator != lines.end(); ++iterator) { + add_line(table, "", *iterator); + } + struct libscols_line * last_line = add_line(table, "Vendor", pkg.get_vendor()); + + // for info output keep each package as a separate section, which means + // an empty line is printed between packages resulting in better readability + p_impl->sections.emplace_back(heading, first_line, last_line); + return true; +} + + +void PackageInfoSections::setup_cols() { + scols_table_new_column(p_impl->table, "key", 1, 0); + scols_table_new_column(p_impl->table, "value", 1, SCOLS_FL_WRAP); + scols_table_set_column_separator(p_impl->table, " : "); +} + bool PackageInfoSections::add_section( const std::string & heading, @@ -65,10 +132,11 @@ bool PackageInfoSections::add_section( auto tmp_heading = heading; for (const auto & pkg : packages) { auto obsoletes_it = obsoletes.find(pkg.get_id()); + PackageAdapter cli_pkg(pkg); if (obsoletes_it != obsoletes.end()) { - add_package(pkg, tmp_heading, colorizer, obsoletes_it->second); + add_package(cli_pkg, tmp_heading, colorizer, obsoletes_it->second); } else { - add_package(pkg, tmp_heading, colorizer, {}); + add_package(cli_pkg, tmp_heading, colorizer, {}); } tmp_heading = ""; } @@ -78,5 +146,4 @@ bool PackageInfoSections::add_section( } } - } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/package_list_sections.cpp b/libdnf5-cli/output/package_list_sections.cpp index 203e8c838..d8000e6be 100644 --- a/libdnf5-cli/output/package_list_sections.cpp +++ b/libdnf5-cli/output/package_list_sections.cpp @@ -19,33 +19,17 @@ along with libdnf. If not, see . #include "libdnf5-cli/output/package_list_sections.hpp" -#include "libdnf5-cli/tty.hpp" +#include "package_list_sections_impl.hpp" + +#include "libdnf5-cli/output/adapters/package.hpp" #include -#include -#include #include -#include -#include namespace libdnf5::cli::output { -PackageListSections::PackageListSections() { - table = scols_new_table(); - scols_table_enable_noheadings(table, 1); - if (libdnf5::cli::tty::is_interactive()) { - scols_table_enable_colors(table, 1); - } -} - - -PackageListSections::~PackageListSections() { - scols_unref_table(table); -} - - -void PackageListSections::print() { +void PackageListSections::Impl::print() { // smartcols does not support spanning the text among multiple cells to create // heading lines. To create sections, print the headings separately and than print // appropriate range of the table. @@ -66,10 +50,21 @@ void PackageListSections::print() { } +PackageListSections::PackageListSections() : p_impl{new Impl} {} + + +PackageListSections::~PackageListSections() = default; + + +void PackageListSections::print() { + p_impl->print(); +} + + void PackageListSections::setup_cols() { - scols_table_new_column(table, "Name", 1, 0); - scols_table_new_column(table, "Version", 1, 0); - scols_table_new_column(table, "Repository", 1, SCOLS_FL_TRUNC); + scols_table_new_column(p_impl->table, "Name", 1, 0); + scols_table_new_column(p_impl->table, "Version", 1, 0); + scols_table_new_column(p_impl->table, "Repository", 1, SCOLS_FL_TRUNC); } @@ -90,13 +85,13 @@ bool PackageListSections::add_section( struct libscols_line * first_line = nullptr; struct libscols_line * last_line = nullptr; for (const auto & pkg : packages) { - struct libscols_line * ln = scols_table_new_line(table, NULL); + struct libscols_line * ln = scols_table_new_line(p_impl->table, NULL); if (first_line == nullptr) { first_line = ln; } last_line = ln; if (colorizer) { - scols_line_set_color(ln, colorizer->get_pkg_color(pkg).c_str()); + scols_line_set_color(ln, colorizer->get_pkg_color(PackageAdapter(pkg)).c_str()); } scols_line_set_data(ln, COL_NA, pkg.get_na().c_str()); scols_line_set_data(ln, COL_EVR, pkg.get_evr().c_str()); @@ -109,7 +104,7 @@ bool PackageListSections::add_section( auto obsoletes_it = obsoletes.find(pkg.get_id()); if (obsoletes_it != obsoletes.end() && !obsoletes_it->second.empty()) { for (const auto & pkg_ob : obsoletes_it->second) { - struct libscols_line * ln = scols_table_new_line(table, NULL); + struct libscols_line * ln = scols_table_new_line(p_impl->table, NULL); last_line = ln; scols_line_set_data(ln, COL_NA, (" " + pkg_ob.get_na()).c_str()); scols_line_set_data(ln, COL_EVR, pkg_ob.get_evr().c_str()); @@ -117,12 +112,11 @@ bool PackageListSections::add_section( } } } - sections.emplace_back(heading, first_line, last_line); + p_impl->sections.emplace_back(heading, first_line, last_line); return true; } else { return false; } } - } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/package_list_sections_impl.hpp b/libdnf5-cli/output/package_list_sections_impl.hpp new file mode 100644 index 000000000..bcf354a65 --- /dev/null +++ b/libdnf5-cli/output/package_list_sections_impl.hpp @@ -0,0 +1,54 @@ +/* +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 LIBDNF5_CLI_OUTPUT_PACKAGE_LIST_SECTIONS_IMPL_HPP +#define LIBDNF5_CLI_OUTPUT_PACKAGE_LIST_SECTIONS_IMPL_HPP + +#include "libdnf5-cli/output/package_list_sections.hpp" +#include "libdnf5-cli/tty.hpp" + +#include + +#include + +namespace libdnf5::cli::output { + +class PackageListSections::Impl { +public: + Impl() { + table = scols_new_table(); + scols_table_enable_noheadings(table, 1); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, 1); + } + } + + ~Impl() { scols_unref_table(table); } + + void print(); + + struct libscols_table * table = nullptr; + // keeps track of the first and the last line of sections + std::vector> sections; +}; + +} // namespace libdnf5::cli::output + +#endif // LIBDNF5_CLI_OUTPUT_PACKAGE_LIST_SECTIONS_IMPL_HPP diff --git a/libdnf5-cli/output/pkg_colorizer.cpp b/libdnf5-cli/output/pkg_colorizer.cpp index 7b068387a..00b7a33ce 100644 --- a/libdnf5-cli/output/pkg_colorizer.cpp +++ b/libdnf5-cli/output/pkg_colorizer.cpp @@ -24,28 +24,69 @@ along with libdnf. If not, see . #include #include -#include +#include +#include namespace libdnf5::cli::output { +namespace { + +const std::map color_to_escape = { + {"bold", "\033[1m"}, + {"dim", "\033[2m"}, + {"underline", "\033[4m"}, + {"blink", "\033[5m"}, + {"reverse", "\033[7m"}, + {"black", "\033[30m"}, + {"red", "\033[31m"}, + {"green", "\033[32m"}, + {"brown", "\033[33m"}, + {"blue", "\033[34m"}, + {"magenta", "\033[35m"}, + {"cyan", "\033[36m"}, + {"gray", "\033[37m"}, + {"white", "\033[1;37m"}, +}; + +} + + PkgColorizer::PkgColorizer( const libdnf5::rpm::PackageSet & base_versions, const std::string & color_not_found, const std::string & color_lt, const std::string & color_eq, - const std::string & color_gt) { - this->color_not_found = to_escape(color_not_found); - this->color_lt = to_escape(color_lt); - this->color_eq = to_escape(color_eq); - this->color_gt = to_escape(color_gt); - + const std::string & color_gt) + : color_not_found{to_escape(color_not_found)}, + color_lt{to_escape(color_lt)}, + color_eq{to_escape(color_eq)}, + color_gt{to_escape(color_gt)} { for (const auto & pkg : base_versions) { base_na_version.emplace(pkg.get_na(), std::move(pkg)); } } -inline std::string PkgColorizer::to_escape(const std::string & color) { +std::string PkgColorizer::get_pkg_color(const IPackage & package) { + auto base_pkg = base_na_version.find(package.get_na()); + std::string color = ""; + if (base_pkg == base_na_version.end()) { + color = color_not_found; + } else { + auto vercmp = libdnf5::rpm::evrcmp(package, base_pkg->second); + if (vercmp < 0) { + color = color_lt; + } else if (vercmp == 0) { + color = color_eq; + } else { + color = color_gt; + } + } + return color; +} + + +std::string PkgColorizer::to_escape(const std::string & color) { if (color.empty()) { return ""; } @@ -65,5 +106,4 @@ inline std::string PkgColorizer::to_escape(const std::string & color) { return output; } - } // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/provides.cpp b/libdnf5-cli/output/provides.cpp new file mode 100644 index 000000000..f43395cc8 --- /dev/null +++ b/libdnf5-cli/output/provides.cpp @@ -0,0 +1,115 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/provides.hpp" + +#include "key_value_table.hpp" + +#include +#include + +#include + +namespace libdnf5::cli::output { + +namespace { + +void add_line_into_provides_table(struct libscols_table * table, const char * key, const char * value) { + struct libscols_line * ln = scols_table_new_line(table, nullptr); + scols_line_set_data(ln, 0, key); + scols_line_set_data(ln, 1, value); +} + + +struct libscols_table * create_provides_heading_table(IPackage & package) { + struct libscols_table * table = scols_new_table(); + scols_table_enable_noheadings(table, 1); + scols_table_set_column_separator(table, " : "); + scols_table_new_column(table, "key", 5, 0); + struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); + scols_column_set_safechars(cl, "\n"); + scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); + + add_line_into_provides_table(table, package.get_nevra().c_str(), package.get_summary().c_str()); + return table; +} + + +struct libscols_table * create_provides_table(IPackage & package, const char * spec, int match) { + struct libscols_table * table = scols_new_table(); + // don't print provides if don't match for at least one + if (!match) { + return table; + } + scols_table_enable_noheadings(table, 1); + scols_table_set_column_separator(table, " : "); + scols_table_new_column(table, "key", 5, 0); + struct libscols_column * cl = scols_table_new_column(table, "value", 10, SCOLS_FL_WRAP); + scols_column_set_safechars(cl, "\n"); + scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize, scols_wrapnl_nextchunk, nullptr); + + add_line_into_provides_table(table, "Repo", package.get_repo_id().c_str()); + add_line_into_provides_table(table, "Matched From", ""); + switch (match) { + case ProvidesMatchedBy::PROVIDES: { + std::string spec_and_version = + package.get_name() + " = " + package.get_version() + "-" + package.get_release(); + add_line_into_provides_table(table, "Provide", spec_and_version.c_str()); + break; + } + case ProvidesMatchedBy::FILENAME: { + std::string pattern(spec); + for (const auto & file : package.get_files()) { + if (sack::match_string(file, sack::QueryCmp::GLOB, pattern)) { + add_line_into_provides_table(table, "Filename", file.c_str()); + } + } + break; + } + case ProvidesMatchedBy::BINARY: { + std::string pattern(spec); + std::vector prefix{"/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/"}; + for (const auto & file : package.get_files()) { + for (const auto & p : prefix) { + if (sack::match_string(file, sack::QueryCmp::GLOB, p + pattern)) { + add_line_into_provides_table(table, "Filename", file.c_str()); + } + } + } + break; + } + } + + return table; +} + +} // namespace + + +void print_provides_table(IPackage & package, const char * spec, int match) { + auto table_head = create_provides_heading_table(package); + auto table = create_provides_table(package, spec, match); + scols_print_table(table_head); + scols_print_table(table); + scols_unref_table(table_head); + scols_unref_table(table); + std::cout << std::endl; +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/repo_info.cpp b/libdnf5-cli/output/repo_info.cpp new file mode 100644 index 000000000..d023f656c --- /dev/null +++ b/libdnf5-cli/output/repo_info.cpp @@ -0,0 +1,198 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/repo_info.hpp" + +#include "key_value_table.hpp" +#include "utils/string.hpp" + +#include "libdnf5-cli/utils/units.hpp" + +namespace libdnf5::cli::output { + +class RepoInfo::Impl : public KeyValueTable { +public: + void add_repo(IRepoInfo & repo); +}; + +void RepoInfo::Impl::add_repo(IRepoInfo & repo) { + auto enabled = repo.is_enabled(); + + add_line("Repo ID", repo.get_id(), "bold"); + add_line("Name", repo.get_name()); + + add_line("Status", enabled ? "enabled" : "disabled", enabled ? "green" : "red"); + + add_line("Priority", repo.get_priority()); + add_line("Cost", repo.get_cost()); + add_line("Type", repo.get_type()); + + auto exclude_packages = repo.get_excludepkgs(); + if (!exclude_packages.empty()) { + add_line("Exclude packages", exclude_packages); + } + + auto include_packages = repo.get_includepkgs(); + if (!include_packages.empty()) { + add_line("Include packages", include_packages); + } + + auto cache_updated = repo.get_timestamp(); + std::string last_update = "unknown"; + if (cache_updated > 0) { + last_update = libdnf5::utils::string::format_epoch(cache_updated); + } + + auto metadata_expire = repo.get_metadata_expire(); + std::string expire_value; + if (metadata_expire <= -1) { + expire_value = "Never"; + } else if (metadata_expire == 0) { + expire_value = "Instant"; + } else { + expire_value = fmt::format("{} seconds", metadata_expire); + } + add_line("Metadata expire", fmt::format("{} (last: {})", expire_value, last_update)); + add_line("Skip if unavailable", fmt::format("{}", repo.get_skip_if_unavailable())); + + auto repo_file_path = repo.get_repo_file_path(); + if (!repo_file_path.empty()) { + add_line("Config file", repo_file_path); + } + + // URLs + auto group_urls = add_line("URLs", "", nullptr); + + auto base_url = repo.get_baseurl(); + if (!base_url.empty()) { + add_line("Base URL", base_url, nullptr, group_urls); + } else { + auto mirrors = repo.get_mirrors(); + if (!mirrors.empty()) { + add_line("Base URL", fmt::format("{} ({} more)", mirrors.front(), mirrors.size()), nullptr, group_urls); + } + } + + auto metalink = repo.get_metalink(); + auto mirrorlist = repo.get_mirrorlist(); + if (!metalink.empty()) { + add_line("Metalink", metalink, nullptr, group_urls); + } else if (!mirrorlist.empty()) { + add_line("Mirrorlist", mirrorlist, nullptr, group_urls); + } + + // PGP + auto group_gpg = add_line("PGP", "", nullptr); + + auto gpg_keys = repo.get_gpgkey(); + if (!gpg_keys.empty()) { + add_line("Keys", gpg_keys, nullptr, group_gpg); + } + + add_line("Verify repodata", fmt::format("{}", repo.get_repo_gpgcheck()), nullptr, group_gpg); + add_line("Verify packages", fmt::format("{}", repo.get_gpgcheck()), nullptr, group_gpg); + + // TODO(jkolarik): Verbose is not implemented and not used yet + // if (verbose) { + // // Connection settings + // auto group_conn = add_line("Connection settings", ""); + // add_line("Authentication method", "", nullptr, group_conn); + // add_line("Username", "", nullptr, group_conn); + // add_line("Password", "", nullptr, group_conn); + // add_line("SSL CA certificate", "", nullptr, group_conn); + // add_line("SSL client certificate", "", nullptr, group_conn); + // add_line("SSL client key", "", nullptr, group_conn); + // add_line("Verify SSL certificate", "", nullptr, group_conn); + + // // Proxy settings + // auto group_proxy = add_line("Proxy settings", ""); + // add_line("URL", "", nullptr, group_proxy); + // add_line("Authentication method", "", nullptr, group_proxy); + // add_line("Username", "", nullptr, group_proxy); + // add_line("Password", "", nullptr, group_proxy); + // add_line("SSL CA certificate", "", nullptr, group_proxy); + // add_line("SSL client certificate", "", nullptr, group_proxy); + // add_line("SSL client key", "", nullptr, group_proxy); + // add_line("Verify SSL certificate", "", nullptr, group_proxy); + + // auto group_misc = add_line("Miscelaneous", ""); + // add_line("Load comps groups", "", nullptr, group_misc); + // add_line("Report \"countme\" statistics", "", nullptr, group_misc); + // add_line("Enable DeltaRPM", "", nullptr, group_misc); + // add_line("DeltaRPM percentage", "", nullptr, group_misc); + // add_line("Use the fastest mirror", "", nullptr, group_misc); + // add_line("Repository provides module hotfixes", "", nullptr, group_misc); + // add_line("Use zchunk repodata", "", nullptr, group_misc); + // } + + // Repodata + if (enabled) { + auto group_repodata = add_line("Repodata info", "", nullptr); + add_line("Available packages", repo.get_available_pkgs(), nullptr, group_repodata); + add_line("Total packages", repo.get_pkgs(), nullptr, group_repodata); + + std::string size = libdnf5::cli::utils::units::format_size_aligned(static_cast(repo.get_size())); + add_line("Size", size, nullptr, group_repodata); + + auto content_tags = repo.get_content_tags(); + if (!content_tags.empty()) { + add_line("Content tags", content_tags, nullptr, group_repodata); + } + + auto distro_tags_flat = repo.get_distro_tags(); + if (!distro_tags_flat.empty()) { + std::vector distro_tags; + for (auto & key_value : distro_tags_flat) { + distro_tags.push_back(key_value.second + " (" + key_value.first + ")"); + } + add_line("Distro tags", distro_tags, nullptr, group_repodata); + } + + add_line("Revision", repo.get_revision(), nullptr, group_repodata); + + add_line("Updated", libdnf5::utils::string::format_epoch(repo.get_max_timestamp()), nullptr, group_repodata); + } + + /* +general connection settings? + bandwidth + ip_resolve + max_parallel_downloads + minrate + retries + throttle + timeout + user_agent +*/ +} + + +RepoInfo::RepoInfo() : p_impl{new Impl} {} + +RepoInfo::~RepoInfo() = default; + +void RepoInfo::add_repo(IRepoInfo & repo) { + p_impl->add_repo(repo); +} + +void RepoInfo::print() { + p_impl->print(); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/repolist.cpp b/libdnf5-cli/output/repolist.cpp new file mode 100644 index 000000000..d03b97803 --- /dev/null +++ b/libdnf5-cli/output/repolist.cpp @@ -0,0 +1,78 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/repolist.hpp" + +#include "libdnf5-cli/tty.hpp" + +#include + +namespace libdnf5::cli::output { + +namespace { + +struct libscols_table * create_repolist_table(bool with_status) { + struct libscols_table * table = scols_new_table(); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(table, 1); + scols_table_enable_maxout(table, 1); + } + struct libscols_column * cl = scols_table_new_column(table, "repo id", 0.4, 0); + scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL); + scols_table_new_column(table, "repo name", 0.5, SCOLS_FL_TRUNC); + if (with_status) { + scols_table_new_column(table, "status", 0.1, SCOLS_FL_RIGHT); + } + return table; +} + + +void add_line_into_repolist_table( + struct libscols_table * table, bool with_status, const char * id, const char * descr, bool enabled) { + struct libscols_line * ln = scols_table_new_line(table, NULL); + scols_line_set_data(ln, COL_REPO_ID, id); + scols_line_set_data(ln, COL_REPO_NAME, descr); + if (with_status) { + scols_line_set_data(ln, COL_REPO_STATUS, enabled ? "enabled" : "disabled"); + struct libscols_cell * cl = scols_line_get_cell(ln, COL_REPO_STATUS); + scols_cell_set_color(cl, enabled ? "green" : "red"); + } +} + +} // namespace + + +void print_repolist_table(const std::vector> & repos, bool with_status, size_t sort_column) { + auto table = create_repolist_table(with_status); + for (const auto & repo : repos) { + add_line_into_repolist_table( + table, + with_status, + repo->get_id().c_str(), + repo->get_name().c_str(), //repo->get_config().get_name_option().get_value().c_str(), + repo->is_enabled()); + } + auto cl = scols_table_get_column(table, sort_column); + scols_sort_table(table, cl); + + scols_print_table(table); + scols_unref_table(table); +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/repoqueryformat.cpp b/libdnf5-cli/output/repoqueryformat.cpp index 6b2e4923c..fe8efd09e 100644 --- a/libdnf5-cli/output/repoqueryformat.cpp +++ b/libdnf5-cli/output/repoqueryformat.cpp @@ -31,6 +31,8 @@ along with libdnf. If not, see . namespace libdnf5::cli::output { +namespace { + using StrGetter = std::string (libdnf5::rpm::Package::*)() const; using VecStrGetter = std::vector (libdnf5::rpm::Package::*)() const; using UnsignedLongLongGetter = unsigned long long (libdnf5::rpm::Package::*)() const; @@ -46,7 +48,7 @@ using Getter = std::variant< TransactionItemReasonGetter, StrGetterLambda>; -static const std::unordered_map NAME_TO_GETTER = { +const std::unordered_map NAME_TO_GETTER = { {"name", &libdnf5::rpm::Package::get_name}, {"epoch", &libdnf5::rpm::Package::get_epoch}, {"version", &libdnf5::rpm::Package::get_version}, @@ -96,20 +98,11 @@ static const std::unordered_map NAME_TO_GETTER = { }}, }; -void print_available_pkg_attrs(std::FILE * target) { - std::set output; - for (const auto & pair : NAME_TO_GETTER) { - output.insert(pair.first); - } - for (const auto & line : output) { - fmt::print(target, "{}\n", line); - } -} // Argument format contains partially copied and converted queryformat, for example: "name: %-30{{name". // We know the tag "name" is valid so this function check align spec "-30" and if it is valid // it overwrites the format with fmt formatting: "name: {:<30}". -static bool replace_tag_in_format( +bool replace_tag_in_format( std::string & format, std::string::size_type & format_size, std::string::size_type tag_start, @@ -163,6 +156,7 @@ static bool replace_tag_in_format( return true; } + std::pair, std::string> parse_queryformat(const std::string & queryformat) { std::vector getters; std::string format; @@ -225,6 +219,8 @@ std::pair, std::string> parse_queryformat(const std::string return {getters, format}; } +} // namespace + bool requires_filelists(const std::string & queryformat) { auto [getters, _] = parse_queryformat(queryformat); for (auto getter : getters) { @@ -237,6 +233,7 @@ bool requires_filelists(const std::string & queryformat) { return false; } + void print_pkg_set_with_format( std::FILE * target, const libdnf5::rpm::PackageSet & pkgs, const std::string & queryformat) { auto [getters, format] = parse_queryformat(queryformat); @@ -284,6 +281,7 @@ void print_pkg_set_with_format( } } + void print_pkg_attr_uniq_sorted( std::FILE * target, const libdnf5::rpm::PackageSet & pkgs, const std::string & getter_name) { auto getter = NAME_TO_GETTER.find(getter_name); @@ -321,6 +319,18 @@ void print_pkg_attr_uniq_sorted( } } + +void print_available_pkg_attrs(std::FILE * target) { + std::set output; + for (const auto & pair : NAME_TO_GETTER) { + output.insert(pair.first); + } + for (const auto & line : output) { + fmt::print(target, "{}\n", line); + } +} + + libdnf5::rpm::ReldepList get_reldeplist_for_attr( const libdnf5::rpm::PackageSet & pkgs, const std::string & getter_name) { auto getter = NAME_TO_GETTER.find(getter_name); diff --git a/libdnf5-cli/output/search.cpp b/libdnf5-cli/output/search.cpp index f727228f0..59bec76ea 100644 --- a/libdnf5-cli/output/search.cpp +++ b/libdnf5-cli/output/search.cpp @@ -31,10 +31,12 @@ along with libdnf. If not, see . namespace libdnf5::cli::output { +namespace { + using namespace libdnf5::cli::tty; /// Get a string representation of a key that was matched together with a type of the match. -static std::string key_to_string(const matched_key_pair & key_pair) { +std::string key_to_string(const matched_key_pair & key_pair) { auto & [key, key_match] = key_pair; if (key_match == SearchKeyMatch::PARTIAL) { return key; @@ -43,37 +45,45 @@ static std::string key_to_string(const matched_key_pair & key_pair) { } } + /// Auxiliary method for aggregating a list of matched keys into a string. -static std::string concat_keys(const std::string & acc, const matched_key_pair & pair) { +std::string concat_keys(const std::string & acc, const matched_key_pair & pair) { return acc.empty() ? key_to_string(pair) : acc + ", " + key_to_string(pair); } + /// Construct a string representation of a list of matched keys. -static std::string construct_keys_string(const std::vector & key_pairs) { +std::string construct_keys_string(const std::vector & key_pairs) { return std::accumulate(key_pairs.begin(), key_pairs.end(), std::string{}, concat_keys); } + /// Auxiliary method for aggregating a pattern matching expression into a string. -static std::string concat_patterns(const std::string & acc, const std::string & pattern) { +std::string concat_patterns(const std::string & acc, const std::string & pattern) { return acc.empty() ? pattern : acc + "|" + pattern; } + /// Construct a regular expression for matching all occurrences of any given pattern in the text. -static std::regex construct_patterns_regex(std::vector patterns) { +std::regex construct_patterns_regex(std::vector patterns) { // Glob patterns are skipped from highlighting for now. std::erase_if(patterns, [](const auto & pattern) { return libdnf5::utils::is_glob_pattern(pattern.c_str()); }); auto inner_patterns_regex_str = std::accumulate(patterns.begin(), patterns.end(), std::string{}, concat_patterns); return std::regex("(" + inner_patterns_regex_str + ")", std::regex_constants::icase); } + /// Construct a highlighting regex replacement format string. /// In the end this is just wrapping all matched patterns with green highlighting markup. -static std::string construct_highlight_regex_format() { +std::string construct_highlight_regex_format() { std::stringstream replace_format; replace_format << green << "$1" << reset; return replace_format.str(); } +} // namespace + + void print_search_results(const SearchResults & results) { if (results.group_results.empty()) { std::cout << "No matches found." << std::endl; diff --git a/libdnf5-cli/output/smartcols_table_wrapper.cpp b/libdnf5-cli/output/smartcols_table_wrapper.cpp index 3c7f77158..68e1a94c7 100644 --- a/libdnf5-cli/output/smartcols_table_wrapper.cpp +++ b/libdnf5-cli/output/smartcols_table_wrapper.cpp @@ -18,7 +18,7 @@ along with libdnf. If not, see . */ -#include "libdnf5-cli/output/smartcols_table_wrapper.hpp" +#include "smartcols_table_wrapper.hpp" #include diff --git a/include/libdnf5-cli/output/smartcols_table_wrapper.hpp b/libdnf5-cli/output/smartcols_table_wrapper.hpp similarity index 96% rename from include/libdnf5-cli/output/smartcols_table_wrapper.hpp rename to libdnf5-cli/output/smartcols_table_wrapper.hpp index c08b30705..c22c33426 100644 --- a/include/libdnf5-cli/output/smartcols_table_wrapper.hpp +++ b/libdnf5-cli/output/smartcols_table_wrapper.hpp @@ -37,13 +37,15 @@ class SmartcolsTableWrapper { } ~SmartcolsTableWrapper(); - libscols_table * operator*() { return tb; } - -private: // Disallow copy operations SmartcolsTableWrapper(const SmartcolsTableWrapper &) = delete; SmartcolsTableWrapper & operator=(const SmartcolsTableWrapper &) = delete; + libscols_table * operator*() { return tb; } + + const libscols_table * operator*() const { return tb; } + +private: libscols_table * tb; libscols_symbols * sb; }; diff --git a/libdnf5-cli/output/transaction_table.cpp b/libdnf5-cli/output/transaction_table.cpp new file mode 100644 index 000000000..2ba031c5e --- /dev/null +++ b/libdnf5-cli/output/transaction_table.cpp @@ -0,0 +1,619 @@ +/* +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 . +*/ + +#include "libdnf5-cli/output/transaction_table.hpp" + +#include "smartcols_table_wrapper.hpp" + +#include "libdnf5-cli/output/interfaces/comps.hpp" +#include "libdnf5-cli/output/interfaces/package.hpp" +#include "libdnf5-cli/tty.hpp" +#include "libdnf5-cli/utils/units.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +namespace libdnf5::cli::output { + +namespace { + +enum { COL_NAME, COL_ARCH, COL_EVR, COL_REPO, COL_SIZE }; + +const char * action_color(libdnf5::transaction::TransactionItemAction action) { + switch (action) { + case libdnf5::transaction::TransactionItemAction::INSTALL: + case libdnf5::transaction::TransactionItemAction::UPGRADE: + case libdnf5::transaction::TransactionItemAction::REINSTALL: + case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: + case libdnf5::transaction::TransactionItemAction::ENABLE: + return "green"; + case libdnf5::transaction::TransactionItemAction::DOWNGRADE: + case libdnf5::transaction::TransactionItemAction::RESET: + return "magenta"; + case libdnf5::transaction::TransactionItemAction::REMOVE: + case libdnf5::transaction::TransactionItemAction::DISABLE: + return "red"; + case libdnf5::transaction::TransactionItemAction::REPLACED: + return "halfbright"; + } + + libdnf_throw_assertion("Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); +} + + +class ActionHeaderPrinter { +public: + ActionHeaderPrinter(SmartcolsTableWrapper & table) : table(table) {} + + struct libscols_line * print(const ITransactionPackage & tspkg) { + if (!current_action || *current_action != tspkg.get_action() || + ((*current_action == libdnf5::transaction::TransactionItemAction::INSTALL || + *current_action == libdnf5::transaction::TransactionItemAction::REMOVE) && + *current_reason != tspkg.get_reason())) { + auto reason = tspkg.get_reason(); + auto action = tspkg.get_action(); + current_header_line = scols_table_new_line(*table, NULL); + std::string text; + + switch (action) { + case libdnf5::transaction::TransactionItemAction::INSTALL: + text = "Installing"; + if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { + text += " dependencies"; + } else if (reason == libdnf5::transaction::TransactionItemReason::WEAK_DEPENDENCY) { + text += " weak dependencies"; + } else if (reason == libdnf5::transaction::TransactionItemReason::GROUP) { + text += " group/module packages"; + } + break; + case libdnf5::transaction::TransactionItemAction::UPGRADE: + text = "Upgrading"; + break; + case libdnf5::transaction::TransactionItemAction::DOWNGRADE: + text = "Downgrading"; + break; + case libdnf5::transaction::TransactionItemAction::REINSTALL: + text = "Reinstalling"; + break; + case libdnf5::transaction::TransactionItemAction::REMOVE: + text = "Removing"; + if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { + text += " dependent packages"; + } else if (reason == libdnf5::transaction::TransactionItemReason::CLEAN) { + text += " unused dependencies"; + } + break; + case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: + text = "Changing reason"; + break; + case libdnf5::transaction::TransactionItemAction::REPLACED: + case libdnf5::transaction::TransactionItemAction::ENABLE: + case libdnf5::transaction::TransactionItemAction::DISABLE: + case libdnf5::transaction::TransactionItemAction::RESET: + libdnf_throw_assertion( + "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); + } + + text += ":"; + + scols_line_set_data(current_header_line, COL_NAME, text.c_str()); + + current_action = action; + current_reason = reason; + } + + return current_header_line; + } + +private: + SmartcolsTableWrapper & table; + struct libscols_line * current_header_line = nullptr; + std::optional current_action; + std::optional current_reason; +}; + + +class ActionHeaderPrinterEnvironment { +public: + ActionHeaderPrinterEnvironment(SmartcolsTableWrapper & table) : table(table) {} + + struct libscols_line * print(const ITransactionEnvironment & tsgrp) { + if (!current_action || *current_action != tsgrp.get_action() || !current_reason || + *current_reason != tsgrp.get_reason()) { + auto reason = tsgrp.get_reason(); + auto action = tsgrp.get_action(); + current_header_line = scols_table_new_line(*table, NULL); + std::string text; + + switch (action) { + case libdnf5::transaction::TransactionItemAction::INSTALL: + text = "Installing environmental groups"; + break; + case libdnf5::transaction::TransactionItemAction::REMOVE: + text = "Removing environmental groups"; + break; + case libdnf5::transaction::TransactionItemAction::UPGRADE: + text = "Upgrading environmental groups"; + break; + default: + libdnf_throw_assertion( + "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); + } + + text += ":"; + + scols_line_set_data(current_header_line, COL_NAME, text.c_str()); + + current_action = action; + current_reason = reason; + } + + return current_header_line; + } + +private: + SmartcolsTableWrapper & table; + struct libscols_line * current_header_line = nullptr; + std::optional current_action; + std::optional current_reason; +}; + + +class ActionHeaderPrinterGroup { +public: + ActionHeaderPrinterGroup(SmartcolsTableWrapper & table) : table(table) {} + + struct libscols_line * print(const ITransactionGroup & tsgrp) { + if (!current_action || *current_action != tsgrp.get_action() || !current_reason || + *current_reason != tsgrp.get_reason()) { + auto reason = tsgrp.get_reason(); + auto action = tsgrp.get_action(); + current_header_line = scols_table_new_line(*table, NULL); + std::string text; + + switch (action) { + case libdnf5::transaction::TransactionItemAction::INSTALL: + text = "Installing groups"; + if (reason == libdnf5::transaction::TransactionItemReason::DEPENDENCY) { + text += " dependencies"; + } + break; + case libdnf5::transaction::TransactionItemAction::REMOVE: + text = "Removing groups"; + break; + case libdnf5::transaction::TransactionItemAction::UPGRADE: + text = "Upgrading groups"; + break; + default: + libdnf_throw_assertion( + "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); + } + + text += ":"; + + scols_line_set_data(current_header_line, COL_NAME, text.c_str()); + + current_action = action; + current_reason = reason; + } + + return current_header_line; + } + +private: + SmartcolsTableWrapper & table; + struct libscols_line * current_header_line = nullptr; + std::optional current_action; + std::optional current_reason; +}; + + +class ActionHeaderPrinterModule { +public: + ActionHeaderPrinterModule(SmartcolsTableWrapper & table) : table(table) {} + + struct libscols_line * print(const ITransactionModule & tsmodule) { + if (!current_action || *current_action != tsmodule.get_action() || !current_reason || + *current_reason != tsmodule.get_reason()) { + auto reason = tsmodule.get_reason(); + auto action = tsmodule.get_action(); + current_header_line = scols_table_new_line(*table, NULL); + std::string text; + + switch (action) { + case libdnf5::transaction::TransactionItemAction::ENABLE: + text = "Enabling module streams"; + break; + case libdnf5::transaction::TransactionItemAction::DISABLE: + text = "Disabling modules"; + break; + case libdnf5::transaction::TransactionItemAction::RESET: + text = "Resetting modules"; + break; + default: + libdnf_throw_assertion( + "Unexpected action in print_transaction_table: {}", libdnf5::utils::to_underlying(action)); + } + + text += ":"; + + scols_line_set_data(current_header_line, COL_NAME, text.c_str()); + + current_action = action; + current_reason = reason; + } + + return current_header_line; + } + +private: + SmartcolsTableWrapper & table; + struct libscols_line * current_header_line = nullptr; + std::optional current_action; + std::optional current_reason; +}; + + +class TransactionSummary { +public: + void add(const libdnf5::transaction::TransactionItemAction & action) { + switch (action) { + case libdnf5::transaction::TransactionItemAction::INSTALL: + installs++; + break; + case libdnf5::transaction::TransactionItemAction::UPGRADE: + upgrades++; + break; + case libdnf5::transaction::TransactionItemAction::DOWNGRADE: + downgrades++; + break; + case libdnf5::transaction::TransactionItemAction::REINSTALL: + reinstalls++; + break; + case libdnf5::transaction::TransactionItemAction::REMOVE: + removes++; + break; + case libdnf5::transaction::TransactionItemAction::REPLACED: + replaced++; + break; + case libdnf5::transaction::TransactionItemAction::REASON_CHANGE: + reason_changes++; + break; + case libdnf5::transaction::TransactionItemAction::ENABLE: + case libdnf5::transaction::TransactionItemAction::DISABLE: + case libdnf5::transaction::TransactionItemAction::RESET: + // Only package change counts are reported. + break; + } + } + + void print(std::FILE * fd = stdout) const { + std::fputs("\nTransaction Summary:\n", fd); + if (installs != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Installing:", installs).c_str(), fd); + } + if (reinstalls != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Reinstalling:", reinstalls).c_str(), fd); + } + if (upgrades != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Upgrading:", upgrades).c_str(), fd); + } + if (replaced != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Replacing:", replaced).c_str(), fd); + } + if (removes != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Removing:", removes).c_str(), fd); + } + if (downgrades != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Downgrading:", downgrades).c_str(), fd); + } + if (reason_changes != 0) { + std::fputs(fmt::format(" {:15} {:4} packages\n", "Changing reason:", reason_changes).c_str(), fd); + } + std::fputc('\n', fd); + } + +private: + int installs = 0; + int reinstalls = 0; + int upgrades = 0; + int downgrades = 0; + int removes = 0; + int replaced = 0; + int reason_changes = 0; +}; + + +bool transaction_package_cmp( + const std::unique_ptr & tspkg1, const std::unique_ptr & tspkg2) { + if (tspkg1->get_action() != tspkg2->get_action()) { + return tspkg1->get_action() > tspkg2->get_action(); + } + + // INSTALL and REMOVE actions are divided (printed) into groups according to the reason. + auto current_action = tspkg1->get_action(); + if ((current_action == libdnf5::transaction::TransactionItemAction::INSTALL || + current_action == libdnf5::transaction::TransactionItemAction::REMOVE) && + tspkg1->get_reason() != tspkg2->get_reason()) { + return tspkg1->get_reason() > tspkg2->get_reason(); + } + + return libdnf5::rpm::cmp_naevr(*tspkg1->get_package(), *tspkg2->get_package()); +} + + +bool transaction_group_cmp( + const std::unique_ptr & tsgrp1, const std::unique_ptr & tsgrp2) { + if (tsgrp1->get_action() != tsgrp2->get_action()) { + return tsgrp1->get_action() > tsgrp2->get_action(); + } + + if (tsgrp1->get_reason() != tsgrp2->get_reason()) { + return tsgrp1->get_reason() > tsgrp2->get_reason(); + } + + return tsgrp1->get_group()->get_groupid() > tsgrp2->get_group()->get_groupid(); +} + +} // namespace + + +class TransactionTable::Impl { +public: + Impl(ITransaction & transaction); + +private: + friend class TransactionTable; + TransactionSummary ts_summary; + SmartcolsTableWrapper tb; +}; + + +TransactionTable::Impl::Impl(ITransaction & transaction) { + //SmartcolsTableWrapper create_transaction_table(ITransaction & transaction, TransactionSummary & ts_summary) { + // SmartcolsTableWrapper tb; + + auto column = scols_table_new_column(*tb, "Package", 0.3, SCOLS_FL_TREE); + auto header = scols_column_get_header(column); + scols_cell_set_color(header, "bold"); + + column = scols_table_new_column(*tb, "Arch", 6, 0); + header = scols_column_get_header(column); + scols_cell_set_color(header, "bold"); + + column = scols_table_new_column(*tb, "Version", 0.3, SCOLS_FL_TRUNC); + header = scols_column_get_header(column); + scols_cell_set_color(header, "bold"); + + column = scols_table_new_column(*tb, "Repository", 0.1, SCOLS_FL_TRUNC); + header = scols_column_get_header(column); + scols_cell_set_color(header, "bold"); + + column = scols_table_new_column(*tb, "Size", 9, SCOLS_FL_RIGHT); + header = scols_column_get_header(column); + scols_cell_set_color(header, "bold"); + + scols_table_enable_maxout(*tb, 1); + scols_table_enable_colors(*tb, libdnf5::cli::tty::is_interactive()); + + // TODO(dmach): use colors from config + // TODO(dmach): highlight version changes (rebases) + // TODO(dmach): consider reordering so the major changes (installs, obsoletes, removals) are at the bottom next to the confirmation question + // TODO(jrohel): Print relations with obsoleted packages + + auto tspkgs = transaction.get_transaction_packages(); + std::sort(tspkgs.begin(), tspkgs.end(), transaction_package_cmp); + auto tsgrps = transaction.get_transaction_groups(); + std::sort(tsgrps.begin(), tsgrps.end(), transaction_group_cmp); + + struct libscols_line * header_ln = nullptr; + ActionHeaderPrinter action_header_printer(tb); + + for (auto & tspkg : tspkgs) { + // TODO(lukash) handle OBSOLETED correctly through the transaction table output + if (tspkg->get_action() == libdnf5::transaction::TransactionItemAction::REPLACED) { + ts_summary.add(tspkg->get_action()); + continue; + } + + auto pkg = tspkg->get_package(); + + header_ln = action_header_printer.print(*tspkg); + + struct libscols_line * ln = scols_table_new_line(*tb, header_ln); + scols_line_set_data(ln, COL_NAME, pkg->get_name().c_str()); + scols_line_set_data(ln, COL_ARCH, pkg->get_arch().c_str()); + scols_line_set_data(ln, COL_EVR, pkg->get_evr().c_str()); + if (tspkg->get_action() == libdnf5::transaction::TransactionItemAction::REMOVE) { + scols_line_set_data(ln, COL_REPO, pkg->get_from_repo_id().c_str()); + } else { + scols_line_set_data(ln, COL_REPO, pkg->get_repo_id().c_str()); + } + auto tspkg_size = static_cast(pkg->get_install_size()); + scols_line_set_data(ln, COL_SIZE, libdnf5::cli::utils::units::format_size_aligned(tspkg_size).c_str()); + auto ce = scols_line_get_cell(ln, COL_NAME); + scols_cell_set_color(ce, action_color(tspkg->get_action())); + + ts_summary.add(tspkg->get_action()); + if (tspkg->get_action() == libdnf5::transaction::TransactionItemAction::REASON_CHANGE) { + auto replaced_color = action_color(libdnf5::transaction::TransactionItemAction::REPLACED); + struct libscols_line * ln_reason = scols_table_new_line(*tb, ln); + std::string reason = fmt::format( + "{} -> {}", + libdnf5::transaction::transaction_item_reason_to_string(pkg->get_reason()), + libdnf5::transaction::transaction_item_reason_to_string(tspkg->get_reason())); + scols_line_set_data(ln_reason, COL_NAME, reason.c_str()); + scols_cell_set_color(scols_line_get_cell(ln_reason, COL_NAME), replaced_color); + } + for (auto & replaced : tspkg->get_replaces()) { + // highlight incoming packages with epoch/version change + if (tspkg->get_package()->get_epoch() != replaced->get_epoch() || + tspkg->get_package()->get_version() != replaced->get_version()) { + auto cl_evr = scols_line_get_cell(ln, COL_EVR); + scols_cell_set_color(cl_evr, "bold"); + } + + struct libscols_line * ln_replaced = scols_table_new_line(*tb, ln); + // TODO(jmracek) Translate it + std::string name("replacing "); + name.append(replaced->get_name()); + scols_line_set_data(ln_replaced, COL_NAME, name.c_str()); + scols_line_set_data(ln_replaced, COL_ARCH, replaced->get_arch().c_str()); + scols_line_set_data(ln_replaced, COL_EVR, replaced->get_evr().c_str()); + scols_line_set_data(ln_replaced, COL_REPO, replaced->get_from_repo_id().c_str()); + + auto replaced_size = static_cast(replaced->get_install_size()); + scols_line_set_data( + ln_replaced, COL_SIZE, libdnf5::cli::utils::units::format_size_aligned(replaced_size).c_str()); + auto replaced_color = action_color(libdnf5::transaction::TransactionItemAction::REPLACED); + auto obsoleted_color = "brown"; + + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_EVR), replaced_color); + if (pkg->get_arch() == replaced->get_arch()) { + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_ARCH), replaced_color); + } else { + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_ARCH), obsoleted_color); + } + if (pkg->get_name() == replaced->get_name()) { + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_NAME), replaced_color); + } else { + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_NAME), obsoleted_color); + } + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_REPO), replaced_color); + scols_cell_set_color(scols_line_get_cell(ln_replaced, COL_SIZE), replaced_color); + } + } + + ActionHeaderPrinterGroup action_header_printer_group(tb); + for (auto & tsgrp : tsgrps) { + auto grp = tsgrp->get_group(); + + header_ln = action_header_printer_group.print(*tsgrp); + + struct libscols_line * ln = scols_table_new_line(*tb, header_ln); + auto const grp_name = grp->get_name(); + if (grp_name.empty()) { + scols_line_set_data(ln, COL_NAME, ""); + } else { + scols_line_set_data(ln, COL_NAME, grp_name.c_str()); + } + auto ce = scols_line_get_cell(ln, COL_NAME); + scols_cell_set_color(ce, action_color(tsgrp->get_action())); + } + + ActionHeaderPrinterEnvironment action_header_printer_environment(tb); + for (auto & tsenv : transaction.get_transaction_environments()) { + auto env = tsenv->get_environment(); + + header_ln = action_header_printer_environment.print(*tsenv); + + struct libscols_line * ln = scols_table_new_line(*tb, header_ln); + auto const env_name = env->get_name(); + if (env_name.empty()) { + scols_line_set_data(ln, COL_NAME, ""); + } else { + scols_line_set_data(ln, COL_NAME, env_name.c_str()); + } + auto ce = scols_line_get_cell(ln, COL_NAME); + scols_cell_set_color(ce, action_color(tsenv->get_action())); + } + + ActionHeaderPrinterModule action_header_printer_module(tb); + for (auto & tsmodule : transaction.get_transaction_modules()) { + header_ln = action_header_printer_module.print(*tsmodule); + + struct libscols_line * ln = scols_table_new_line(*tb, header_ln); + scols_line_set_data(ln, COL_NAME, tsmodule->get_module_name().c_str()); + scols_line_set_data(ln, COL_EVR, tsmodule->get_module_stream().c_str()); + scols_cell_set_color(scols_line_get_cell(ln, COL_NAME), action_color(tsmodule->get_action())); + } +} + + +TransactionTable::TransactionTable(ITransaction & transaction) : p_impl(new Impl(transaction)) {} + +TransactionTable::~TransactionTable() = default; + +TransactionTable::TransactionTable(TransactionTable && src) = default; + +TransactionTable & TransactionTable::operator=(TransactionTable && src) = default; + + +void TransactionTable::print_table() { + scols_print_table(*p_impl->tb); +} + + +void TransactionTable::print_summary() const { + p_impl->ts_summary.print(scols_table_get_stream(*p_impl->tb)); +} + + +void TransactionTable::set_colors_enabled(bool enable) { + scols_table_enable_colors(*p_impl->tb, enable); +} + + +void TransactionTable::set_term_width(std::size_t width) { + scols_table_set_termwidth(*p_impl->tb, width); +} + + +void TransactionTable::set_output_stream(FILE * fd) { + scols_table_set_stream(*p_impl->tb, fd); +} + + +/// Prints all transaction problems +void print_resolve_logs(const ITransaction & transaction, std::ostream & stream) { + const std::vector logs = transaction.get_resolve_logs_as_strings(); + for (const auto & log : logs) { + stream << log << std::endl; + } + if (logs.size() > 0) { + stream << std::endl; + } +} + + +bool print_transaction_table(ITransaction & transaction) { + // even correctly resolved transaction can contain some warnings / hints / infos + // in resolve logs (e.g. the package user wanted to install is already installed). + // Present them to the user. + print_resolve_logs(transaction); + + if (transaction.empty()) { + std::cout << "Nothing to do." << std::endl; + return false; + } + + TransactionTable table(transaction); + table.print_table(); + table.print_summary(); + + return true; +} + +} // namespace libdnf5::cli::output diff --git a/libdnf5-cli/output/transactioninfo.cpp b/libdnf5-cli/output/transactioninfo.cpp index fe099ea9e..a59e6f573 100644 --- a/libdnf5-cli/output/transactioninfo.cpp +++ b/libdnf5-cli/output/transactioninfo.cpp @@ -20,8 +20,11 @@ along with libdnf. If not, see . #include "libdnf5-cli/output/transactioninfo.hpp" +#include "key_value_table.hpp" #include "utils/string.hpp" +#include "libdnf5-cli/tty.hpp" + #include #include @@ -30,6 +33,7 @@ along with libdnf. If not, see . namespace libdnf5::cli::output { namespace { + std::string generate_user_info_str(uint32_t user_id) { auto results = std::to_string(user_id); auto user_info = getpwuid(user_id); @@ -53,8 +57,37 @@ std::string generate_user_info_str(uint32_t user_id) { return results; } + + +template +void print_transaction_item_table(std::vector items, const char * title) { + std::unique_ptr item_list(scols_new_table(), &scols_unref_table); + if (libdnf5::cli::tty::is_interactive()) { + scols_table_enable_colors(item_list.get(), 1); + } + scols_cell_set_data(scols_table_get_title(item_list.get()), title); + + // The two spaces indent the table the same way as child lines in KeyValueTable + scols_table_new_column(item_list.get(), " Action", 0, 0); + scols_table_new_column(item_list.get(), "Package", 0, 0); + scols_table_new_column(item_list.get(), "Reason", 0, 0); + scols_table_new_column(item_list.get(), "Repository", 0, 0); + + for (auto & pkg : items) { + struct libscols_line * ln = scols_table_new_line(item_list.get(), NULL); + scols_line_set_data( + ln, 0, (" " + libdnf5::transaction::transaction_item_action_to_string(pkg.get_action())).c_str()); + scols_line_set_data(ln, 1, pkg.to_string().c_str()); + scols_line_set_data(ln, 2, libdnf5::transaction::transaction_item_reason_to_string(pkg.get_reason()).c_str()); + scols_line_set_data(ln, 3, pkg.get_repoid().c_str()); + } + + scols_print_table(item_list.get()); +} + } // namespace + void print_transaction_info(libdnf5::transaction::Transaction & transaction) { KeyValueTable info; info.add_line("Transaction ID", transaction.get_id(), "bold");