diff --git a/libtransmission/serializer.h b/libtransmission/serializer.h index df10408b0..019aa0731 100644 --- a/libtransmission/serializer.h +++ b/libtransmission/serializer.h @@ -137,7 +137,7 @@ public: return detail::from_array(src); } - fmt::print(stderr, "ERROR: No serializer registered for type '{}'\\n", typeid(T).name()); + fmt::print(stderr, "ERROR: No serializer registered for type '{}'\n", typeid(T).name()); return {}; } @@ -166,6 +166,7 @@ public: return detail::to_array(src, ptgt); } + fmt::print(stderr, "ERROR: No deserializer registered for type '{}'\n", typeid(T).name()); return false; } diff --git a/qt/Session.cc b/qt/Session.cc index e33a123da..cba0b5887 100644 --- a/qt/Session.cc +++ b/qt/Session.cc @@ -22,6 +22,8 @@ #include #include +#include + #include #include @@ -44,6 +46,7 @@ using namespace std::literals; using ::trqt::variant_helpers::dictAdd; using ::trqt::variant_helpers::dictFind; using ::trqt::variant_helpers::getValue; +using ::trqt::variant_helpers::to_variant; /*** **** @@ -385,6 +388,18 @@ void Session::start() // --- +void Session::addOptionalIds(tr_variant::Map& params, torrent_ids_t const& torrent_ids) const +{ + if (&torrent_ids == &RecentlyActiveIDs) + { + params.try_emplace(TR_KEY_ids, tr_variant::unmanaged_string(TR_KEY_recently_active)); + } + else if (!std::empty(torrent_ids)) + { + params.try_emplace(TR_KEY_ids, to_variant(torrent_ids)); + } +} + void Session::addOptionalIds(tr_variant* args_dict, torrent_ids_t const& torrent_ids) const { if (&torrent_ids == &RecentlyActiveIDs) @@ -511,149 +526,177 @@ void Session::torrentRenamePath(torrent_ids_t const& torrent_ids, QString const& q->run(); } -std::set const& Session::getKeyNames(TorrentProperties props) +namespace { - std::set& names = names_[props]; +using TorrentProperties = Session::TorrentProperties; - if (names.empty()) +[[nodiscard]] small::max_size_vector getKeys(TorrentProperties const props) +{ + // IMPORTANT when changing these key sets: + // 1. `TR_KEY_id` must be in every set + // 2. When changing `MainInfo` or `MainStats`, update their union in `MainAll` + + // clang-format off: one line per key + switch (props) { - // unchanging fields needed by the main window - static auto constexpr MainInfoKeys = std::array{ - TR_KEY_added_date, // - TR_KEY_download_dir, // - TR_KEY_file_count, // - TR_KEY_hash_string, // - TR_KEY_labels, // - TR_KEY_name, // - TR_KEY_primary_mime_type, // - TR_KEY_total_size, // - TR_KEY_trackers, // - }; - - // changing fields needed by the main window - static auto constexpr MainStatKeys = std::array{ - TR_KEY_downloaded_ever, - TR_KEY_edit_date, - TR_KEY_error, - TR_KEY_error_string, - TR_KEY_eta, - TR_KEY_have_unchecked, - TR_KEY_have_valid, - TR_KEY_is_finished, - TR_KEY_left_until_done, - TR_KEY_manual_announce_time, - TR_KEY_metadata_percent_complete, - TR_KEY_peers_connected, - TR_KEY_peers_getting_from_us, - TR_KEY_peers_sending_to_us, - TR_KEY_percent_done, - TR_KEY_queue_position, - TR_KEY_rate_download, - TR_KEY_rate_upload, - TR_KEY_recheck_progress, - TR_KEY_seed_ratio_limit, - TR_KEY_seed_ratio_mode, - TR_KEY_size_when_done, - TR_KEY_status, - TR_KEY_uploaded_ever, - TR_KEY_webseeds_sending_to_us, - }; - // unchanging fields needed by the details dialog - static auto constexpr DetailInfoKeys = std::array{ - TR_KEY_comment, // - TR_KEY_creator, // - TR_KEY_date_created, // - TR_KEY_files, // - TR_KEY_is_private, // - TR_KEY_labels, // - TR_KEY_piece_count, // - TR_KEY_piece_size, // - TR_KEY_tracker_list, // - TR_KEY_trackers, // - }; + case TorrentProperties::DetailInfo: + return { + TR_KEY_comment, + TR_KEY_creator, + TR_KEY_date_created, + TR_KEY_files, + TR_KEY_id, + TR_KEY_is_private, + TR_KEY_labels, + TR_KEY_piece_count, + TR_KEY_piece_size, + TR_KEY_tracker_list, + TR_KEY_trackers, + }; // changing fields needed by the details dialog - static auto constexpr DetailStatKeys = std::array{ - TR_KEY_activity_date, // - TR_KEY_bandwidth_priority, // - TR_KEY_corrupt_ever, // - TR_KEY_desired_available, // - TR_KEY_downloaded_ever, // - TR_KEY_download_limit, // - TR_KEY_download_limited, // - TR_KEY_file_stats, // - TR_KEY_have_unchecked, // - TR_KEY_honors_session_limits, // - TR_KEY_peer_limit, // - TR_KEY_peers, // - TR_KEY_seed_idle_limit, // - TR_KEY_seed_idle_mode, // - TR_KEY_start_date, // - TR_KEY_tracker_stats, // - TR_KEY_upload_limit, // - TR_KEY_upload_limited, // - }; + case TorrentProperties::DetailStat: + return { + TR_KEY_activity_date, + TR_KEY_bandwidth_priority, + TR_KEY_corrupt_ever, + TR_KEY_desired_available, + TR_KEY_download_limit, + TR_KEY_download_limited, + TR_KEY_downloaded_ever, + TR_KEY_file_stats, + TR_KEY_have_unchecked, + TR_KEY_honors_session_limits, + TR_KEY_id, + TR_KEY_peer_limit, + TR_KEY_peers, + TR_KEY_seed_idle_limit, + TR_KEY_seed_idle_mode, + TR_KEY_start_date, + TR_KEY_tracker_stats, + TR_KEY_upload_limit, + TR_KEY_upload_limited, + }; + + // union of MainInfoKeys + MainStatKeys + case TorrentProperties::MainAll: + return { + TR_KEY_added_date, + TR_KEY_download_dir, + TR_KEY_downloaded_ever, + TR_KEY_edit_date, + TR_KEY_error, + TR_KEY_error_string, + TR_KEY_eta, + TR_KEY_file_count, + TR_KEY_hash_string, + TR_KEY_have_unchecked, + TR_KEY_have_valid, + TR_KEY_id, + TR_KEY_is_finished, + TR_KEY_labels, + TR_KEY_left_until_done, + TR_KEY_manual_announce_time, + TR_KEY_metadata_percent_complete, + TR_KEY_name, + TR_KEY_peers_connected, + TR_KEY_peers_getting_from_us, + TR_KEY_peers_sending_to_us, + TR_KEY_percent_done, + TR_KEY_primary_mime_type, + TR_KEY_queue_position, + TR_KEY_rate_download, + TR_KEY_rate_upload, + TR_KEY_recheck_progress, + TR_KEY_seed_ratio_limit, + TR_KEY_seed_ratio_mode, + TR_KEY_size_when_done, + TR_KEY_status, + TR_KEY_total_size, + TR_KEY_trackers, + TR_KEY_uploaded_ever, + TR_KEY_webseeds_sending_to_us, + }; + + // unchanging fields needed by the main window + case TorrentProperties::MainInfo: + return { + TR_KEY_added_date, + TR_KEY_download_dir, + TR_KEY_file_count, + TR_KEY_hash_string, + TR_KEY_id, + TR_KEY_labels, + TR_KEY_name, + TR_KEY_primary_mime_type, + TR_KEY_total_size, + TR_KEY_trackers, + }; + + // changing fields needed by the main window + case TorrentProperties::MainStats: + return { + TR_KEY_downloaded_ever, + TR_KEY_edit_date, + TR_KEY_error, + TR_KEY_error_string, + TR_KEY_eta, + TR_KEY_have_unchecked, + TR_KEY_have_valid, + TR_KEY_id, + TR_KEY_is_finished, + TR_KEY_left_until_done, + TR_KEY_manual_announce_time, + TR_KEY_metadata_percent_complete, + TR_KEY_peers_connected, + TR_KEY_peers_getting_from_us, + TR_KEY_peers_sending_to_us, + TR_KEY_percent_done, + TR_KEY_queue_position, + TR_KEY_rate_download, + TR_KEY_rate_upload, + TR_KEY_recheck_progress, + TR_KEY_seed_ratio_limit, + TR_KEY_seed_ratio_mode, + TR_KEY_size_when_done, + TR_KEY_status, + TR_KEY_uploaded_ever, + TR_KEY_webseeds_sending_to_us, + }; // keys needed after renaming a torrent - static auto constexpr RenameKeys = std::array{ - TR_KEY_file_stats, - TR_KEY_files, - TR_KEY_name, - }; - - auto const append = [&names](tr_quark key) - { - names.emplace(tr_quark_get_string_view(key)); - }; - - switch (props) - { - case TorrentProperties::DetailInfo: - std::for_each(DetailInfoKeys.begin(), DetailInfoKeys.end(), append); - break; - - case TorrentProperties::DetailStat: - std::for_each(DetailStatKeys.begin(), DetailStatKeys.end(), append); - break; - - case TorrentProperties::MainAll: - std::for_each(MainInfoKeys.begin(), MainInfoKeys.end(), append); - std::for_each(MainStatKeys.begin(), MainStatKeys.end(), append); - break; - - case TorrentProperties::MainInfo: - std::for_each(MainInfoKeys.begin(), MainInfoKeys.end(), append); - break; - - case TorrentProperties::MainStats: - std::for_each(MainStatKeys.begin(), MainStatKeys.end(), append); - break; - case TorrentProperties::Rename: - std::for_each(RenameKeys.begin(), RenameKeys.end(), append); - break; - } - - // must be in every torrent req - append(TR_KEY_id); + return { + TR_KEY_file_stats, + TR_KEY_files, + TR_KEY_id, + TR_KEY_name, + }; } - - return names; + // clang-format on } +} // namespace -void Session::refreshTorrents(torrent_ids_t const& torrent_ids, TorrentProperties props) +void Session::refreshTorrents(torrent_ids_t const& torrent_ids, TorrentProperties const props) { - auto constexpr Table = std::string_view{ "table" }; + auto fields = tr_variant::Vector{}; + auto const keys = getKeys(props); + fields.reserve(std::size(keys)); + std::transform( + std::begin(keys), + std::end(keys), + std::back_inserter(fields), + [](auto key) { return tr_variant::unmanaged_string(key); }); - tr_variant args; - tr_variantInitDict(&args, 3); - dictAdd(&args, TR_KEY_format, Table); - dictAdd(&args, TR_KEY_fields, getKeyNames(props)); - addOptionalIds(&args, torrent_ids); + auto map = tr_variant::Map{ 3U }; + map.try_emplace(TR_KEY_format, tr_variant::unmanaged_string("table"sv)); + map.try_emplace(TR_KEY_fields, std::move(fields)); + addOptionalIds(map, torrent_ids); auto* q = new RpcQueue{}; + auto args = tr_variant{ std::move(map) }; q->add([this, &args]() { return exec(TR_KEY_torrent_get, &args); }); bool const all_torrents = std::empty(torrent_ids); diff --git a/qt/Session.h b/qt/Session.h index 7753741bf..daf171f20 100644 --- a/qt/Session.h +++ b/qt/Session.h @@ -9,7 +9,6 @@ #include // int64_t #include #include -#include #include #include @@ -135,26 +134,6 @@ public: Rename }; - void addKeyName(TorrentProperties props, tr_quark const key) - { - // populate names cache with default values - if (names_[props].empty()) - { - getKeyNames(props); - } - - names_[props].emplace(tr_quark_get_string_view(key)); - } - - void removeKeyName(TorrentProperties props, tr_quark const key) - { - // do not remove id because it must be in every torrent req - if (key != TR_KEY_id) - { - names_[props].erase(tr_quark_get_string_view(key)); - } - } - public slots: void addTorrent(AddData add_me); void launchWebInterface() const; @@ -195,17 +174,15 @@ private: void pumpRequests(); void sendTorrentRequest(tr_quark method, torrent_ids_t const& torrent_ids); void refreshTorrents(torrent_ids_t const& ids, TorrentProperties props); - std::set const& getKeyNames(TorrentProperties props); static void updateStats(tr_variant* args_dict, tr_session_stats* stats); + void addOptionalIds(tr_variant::Map& params, torrent_ids_t const& torrent_ids) const; void addOptionalIds(tr_variant* args_dict, torrent_ids_t const& torrent_ids) const; QString const config_dir_; Prefs& prefs_; - std::map> names_; - int64_t blocklist_size_ = -1; std::array port_test_pending_ = {}; tr_session* session_ = {}; diff --git a/qt/VariantHelpers.cc b/qt/VariantHelpers.cc index 73afc95cd..f738a37e3 100644 --- a/qt/VariantHelpers.cc +++ b/qt/VariantHelpers.cc @@ -8,10 +8,13 @@ #include #include #include +#include #include #include +#include + #include "Application.h" // qApp #include "Speed.h" #include "Torrent.h" @@ -228,4 +231,40 @@ void variantInit(tr_variant* init_me, std::string_view value) *init_me = value; } +namespace +{ +bool to_int(tr_variant const& src, int* tgt) +{ + if (auto const val = src.value_if()) + { + if (*val < std::numeric_limits::min() || *val > std::numeric_limits::max()) + { + return false; + } + + *tgt = static_cast(*val); + return true; + } + + return false; +} + +tr_variant from_int(int const& val) +{ + return static_cast(val); +} +} // namespace + +void register_qt_converters() +{ + static auto once = std::once_flag{}; + std::call_once( + once, + [] + { + using namespace libtransmission::serializer; + Converters::add(to_int, from_int); + }); +} + } // namespace trqt::variant_helpers diff --git a/qt/VariantHelpers.h b/qt/VariantHelpers.h index ea54ae9b9..932916499 100644 --- a/qt/VariantHelpers.h +++ b/qt/VariantHelpers.h @@ -14,6 +14,7 @@ #include +#include #include class QByteArray; @@ -26,6 +27,13 @@ struct TrackerStat; namespace trqt::variant_helpers { +void register_qt_converters(); + +template +[[nodiscard]] tr_variant to_variant(T const& val) +{ + return libtransmission::serializer::Converters::serialize(val); +} template>* = nullptr> auto getValue(tr_variant const* variant) diff --git a/qt/main.cc b/qt/main.cc index c98210634..413193485 100644 --- a/qt/main.cc +++ b/qt/main.cc @@ -18,6 +18,7 @@ #include "Application.h" #include "InteropHelper.h" #include "Prefs.h" +#include "VariantHelpers.h" using namespace std::string_view_literals; @@ -99,8 +100,8 @@ bool tryDelegate(QStringList const& filenames) int tr_main(int argc, char** argv) { tr_lib_init(); - tr_locale_set_global(""); + trqt::variant_helpers::register_qt_converters(); // parse the command-line arguments bool minimized = false;