From f8970a9183a5913bc1e6ab3ef33eae621185e739 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 16 Dec 2025 15:20:51 -0600 Subject: [PATCH] refactor: use the new jsonrpc API for RPC calls in tr-gtk (#7938) * feat: copy TR_RPC_VERBOSE env var feature from tr-qt --- gtk/Application.cc | 80 ++++++++++--------- gtk/DetailsDialog.cc | 116 +++++++--------------------- gtk/Session.cc | 156 +++++++++++++++++++------------------- gtk/Session.h | 27 ++++++- libtransmission/utils.cc | 2 +- libtransmission/utils.h | 2 +- libtransmission/variant.h | 4 +- 7 files changed, 180 insertions(+), 207 deletions(-) diff --git a/gtk/Application.cc b/gtk/Application.cc index c8bc91822..dd83a0f69 100644 --- a/gtk/Application.cc +++ b/gtk/Application.cc @@ -25,6 +25,7 @@ #include "Utils.h" #include +#include #include #include #include @@ -198,7 +199,7 @@ private: void start_all_torrents(); void pause_all_torrents(); void copy_magnet_link_to_clipboard(Glib::RefPtr const& torrent) const; - bool call_rpc_for_selected_torrents(std::string const& method); + bool call_rpc_for_selected_torrents(tr_quark method); void remove_selected(bool delete_files); static tr_rpc_callback_status on_rpc_changed( @@ -1416,25 +1417,18 @@ void Application::Impl::show_about_dialog() d->show(); } -bool Application::Impl::call_rpc_for_selected_torrents(std::string const& method) +bool Application::Impl::call_rpc_for_selected_torrents(tr_quark const method) { - tr_variant top; - bool invoked = false; - auto* session = core_->get_session(); - - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, method); - auto* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 1); - auto* const ids = tr_variantDictAddList(args, TR_KEY_ids, 0); - wind_->for_each_selected_torrent([ids](auto const& torrent) { tr_variantListAddInt(ids, torrent->get_id()); }); - - if (tr_variantListSize(ids) != 0) + auto const ids = get_selected_torrent_ids(); + if (std::empty(ids)) { - tr_rpc_request_exec(session, top, {}); - invoked = true; + return false; } - return invoked; + auto params = tr_variant::Map{ 1U }; + params.try_emplace(TR_KEY_ids, Session::to_variant(ids)); + core_->exec(method, std::move(params)); + return true; } void Application::Impl::remove_selected(bool delete_files) @@ -1447,22 +1441,12 @@ void Application::Impl::remove_selected(bool delete_files) void Application::Impl::start_all_torrents() { - auto* session = core_->get_session(); - tr_variant request; - - tr_variantInitDict(&request, 1); - tr_variantDictAddStrView(&request, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_start_kebab)); - tr_rpc_request_exec(session, request, {}); + core_->exec(TR_KEY_torrent_start, {}); } void Application::Impl::pause_all_torrents() { - auto* session = core_->get_session(); - tr_variant request; - - tr_variantInitDict(&request, 1); - tr_variantDictAddStrView(&request, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_stop_kebab)); - tr_rpc_request_exec(session, request, {}); + core_->exec(TR_KEY_torrent_stop, {}); } void Application::Impl::copy_magnet_link_to_clipboard(Glib::RefPtr const& torrent) const @@ -1483,6 +1467,38 @@ void gtr_actions_handler(Glib::ustring const& action_name, gpointer user_data) static_cast(user_data)->actions_handler(action_name); } +namespace +{ + +[[nodiscard]] std::optional get_rpc_method(std::string_view const str) +{ + if (auto quark = tr_quark_lookup(str)) // method-name, methodName, method_name + { + quark = tr_quark_convert(*quark); // method_name + switch (*quark) + { + // method_name + case TR_KEY_queue_move_bottom: + case TR_KEY_queue_move_down: + case TR_KEY_queue_move_top: + case TR_KEY_queue_move_up: + case TR_KEY_torrent_reannounce: + case TR_KEY_torrent_start: + case TR_KEY_torrent_start_now: + case TR_KEY_torrent_stop: + case TR_KEY_torrent_verify: + return quark; + + default: + break; + } + } + + return {}; +} + +} // namespace + void Application::Impl::actions_handler(Glib::ustring const& action_name) { bool changed = false; @@ -1533,13 +1549,9 @@ void Application::Impl::actions_handler(Glib::ustring const& action_name) w->show(); } } - else if ( - // TODO: migrate from _kebab - action_name == "torrent-start" || action_name == "torrent-start-now" || action_name == "torrent-stop" || - action_name == "torrent-reannounce" || action_name == "torrent-verify" || action_name == "queue-move-top" || - action_name == "queue-move-up" || action_name == "queue-move-down" || action_name == "queue-move-bottom") + else if (auto const method = get_rpc_method(action_name.raw())) { - changed = call_rpc_for_selected_torrents(action_name); + changed = call_rpc_for_selected_torrents(*method); } else if (action_name == "open-torrent-folder") { diff --git a/gtk/DetailsDialog.cc b/gtk/DetailsDialog.cc index 4b207c3dc..a1f384b44 100644 --- a/gtk/DetailsDialog.cc +++ b/gtk/DetailsDialog.cc @@ -109,9 +109,14 @@ private: void onScrapeToggled(); void onBackupToggled(); - void torrent_set_bool(tr_quark key, bool value); - void torrent_set_int(tr_quark key, int value); - void torrent_set_real(tr_quark key, double value); + template + void torrent_set_field(tr_quark const key, T value) + { + auto params = tr_variant::Map{ 2U }; + params.try_emplace(key, std::forward(value)); + params.try_emplace(TR_KEY_ids, Session::to_variant(ids_)); + core_->exec(TR_KEY_torrent_set, std::move(params)); + } void refreshInfo(std::vector const& torrents); void refreshPeers(std::vector const& torrents); @@ -436,88 +441,34 @@ void DetailsDialog::Impl::refreshOptions(std::vector const& torrent } } -void DetailsDialog::Impl::torrent_set_bool(tr_quark key, bool value) -{ - tr_variant top; - - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_set_kebab)); - tr_variant* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddBool(args, key, value); - tr_variant* const ids = tr_variantDictAddList(args, TR_KEY_ids, ids_.size()); - - for (auto const id : ids_) - { - tr_variantListAddInt(ids, id); - } - - core_->exec(top); -} - -void DetailsDialog::Impl::torrent_set_int(tr_quark key, int value) -{ - tr_variant top; - - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_set_kebab)); - tr_variant* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddInt(args, key, value); - tr_variant* const ids = tr_variantDictAddList(args, TR_KEY_ids, ids_.size()); - - for (auto const id : ids_) - { - tr_variantListAddInt(ids, id); - } - - core_->exec(top); -} - -void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value) -{ - tr_variant top; - - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_set_kebab)); - tr_variant* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddReal(args, key, value); - tr_variant* const ids = tr_variantDictAddList(args, TR_KEY_ids, ids_.size()); - - for (auto const id : ids_) - { - tr_variantListAddInt(ids, id); - } - - core_->exec(top); -} - void DetailsDialog::Impl::options_page_init(Glib::RefPtr const& /*builder*/) { auto const speed_units_kbyps_str = Speed::units().display_name(Speed::Units::KByps); honor_limits_check_tag_ = honor_limits_check_->signal_toggled().connect( - [this]() { torrent_set_bool(TR_KEY_honors_session_limits_camel, honor_limits_check_->get_active()); }); + [this]() { torrent_set_field(TR_KEY_honors_session_limits, honor_limits_check_->get_active()); }); down_limited_check_->set_label( fmt::format(fmt::runtime(down_limited_check_->get_label().raw()), fmt::arg("speed_units", speed_units_kbyps_str))); down_limited_check_tag_ = down_limited_check_->signal_toggled().connect( - [this]() { torrent_set_bool(TR_KEY_download_limited_camel, down_limited_check_->get_active()); }); + [this]() { torrent_set_field(TR_KEY_download_limited, down_limited_check_->get_active()); }); down_limit_spin_->set_adjustment(Gtk::Adjustment::create(0, 0, std::numeric_limits::max(), 5)); down_limit_spin_tag_ = down_limit_spin_->signal_value_changed().connect( - [this]() { torrent_set_int(TR_KEY_download_limit_camel, down_limit_spin_->get_value_as_int()); }); + [this]() { torrent_set_field(TR_KEY_download_limit, down_limit_spin_->get_value_as_int()); }); up_limited_check_->set_label( fmt::format(fmt::runtime(up_limited_check_->get_label().raw()), fmt::arg("speed_units", speed_units_kbyps_str))); up_limited_check_tag_ = up_limited_check_->signal_toggled().connect( - [this]() { torrent_set_bool(TR_KEY_upload_limited_camel, up_limited_check_->get_active()); }); + [this]() { torrent_set_field(TR_KEY_upload_limited, up_limited_check_->get_active()); }); up_limit_sping_->set_adjustment(Gtk::Adjustment::create(0, 0, std::numeric_limits::max(), 5)); up_limit_spin_tag_ = up_limit_sping_->signal_value_changed().connect( - [this]() { torrent_set_int(TR_KEY_upload_limit_camel, up_limit_sping_->get_value_as_int()); }); + [this]() { torrent_set_field(TR_KEY_upload_limit, up_limit_sping_->get_value_as_int()); }); gtr_priority_combo_init(*bandwidth_combo_); bandwidth_combo_tag_ = bandwidth_combo_->signal_changed().connect( - [this]() { torrent_set_int(TR_KEY_bandwidth_priority_camel, gtr_combo_box_get_active_enum(*bandwidth_combo_)); }); + [this]() { torrent_set_field(TR_KEY_bandwidth_priority, gtr_combo_box_get_active_enum(*bandwidth_combo_)); }); gtr_combo_box_set_enum( *ratio_combo_, @@ -529,13 +480,13 @@ void DetailsDialog::Impl::options_page_init(Glib::RefPtr const& /* ratio_combo_tag_ = ratio_combo_->signal_changed().connect( [this]() { - torrent_set_int(TR_KEY_seed_ratio_mode_camel, gtr_combo_box_get_active_enum(*ratio_combo_)); + torrent_set_field(TR_KEY_seed_ratio_mode, gtr_combo_box_get_active_enum(*ratio_combo_)); refresh(); }); ratio_spin_->set_adjustment(Gtk::Adjustment::create(0, 0, 1000, .05)); ratio_spin_->set_width_chars(7); ratio_spin_tag_ = ratio_spin_->signal_value_changed().connect( - [this]() { torrent_set_real(TR_KEY_seed_ratio_limit_camel, ratio_spin_->get_value()); }); + [this]() { torrent_set_field(TR_KEY_seed_ratio_limit, ratio_spin_->get_value()); }); gtr_combo_box_set_enum( *idle_combo_, @@ -547,16 +498,16 @@ void DetailsDialog::Impl::options_page_init(Glib::RefPtr const& /* idle_combo_tag_ = idle_combo_->signal_changed().connect( [this]() { - torrent_set_int(TR_KEY_seed_idle_mode_camel, gtr_combo_box_get_active_enum(*idle_combo_)); + torrent_set_field(TR_KEY_seed_idle_mode, gtr_combo_box_get_active_enum(*idle_combo_)); refresh(); }); idle_spin_->set_adjustment(Gtk::Adjustment::create(1, 1, 40320, 5)); idle_spin_tag_ = idle_spin_->signal_value_changed().connect( - [this]() { torrent_set_int(TR_KEY_seed_idle_limit_camel, idle_spin_->get_value_as_int()); }); + [this]() { torrent_set_field(TR_KEY_seed_idle_limit, idle_spin_->get_value_as_int()); }); max_peers_spin_->set_adjustment(Gtk::Adjustment::create(1, 1, 3000, 5)); max_peers_spin_tag_ = max_peers_spin_->signal_value_changed().connect( - [this]() { torrent_set_int(TR_KEY_peer_limit_kebab, max_peers_spin_->get_value_as_int()); }); + [this]() { torrent_set_field(TR_KEY_peer_limit, max_peers_spin_->get_value_as_int()); }); } /**** @@ -2388,16 +2339,11 @@ void AddTrackerDialog::on_response(int response) { if (tr_urlIsValidTracker(url.c_str())) { - tr_variant top; - - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_set_kebab)); - auto* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddInt(args, TR_KEY_id, torrent_id_); - auto* const trackers = tr_variantDictAddList(args, TR_KEY_tracker_add_camel, 1); - tr_variantListAddStr(trackers, url.raw()); - - core_->exec(top); + // TODO(ckerr) migrate to `TR_KEY_tracker_list` + auto params = tr_variant::Map{ 2U }; + params.try_emplace(TR_KEY_ids, Session::to_variant({ torrent_id_ })); + params.try_emplace(TR_KEY_tracker_add, Session::to_variant({ url.raw() })); + core_->exec(TR_KEY_torrent_set, std::move(params)); parent_.refresh(); } else @@ -2435,16 +2381,12 @@ void DetailsDialog::Impl::on_tracker_list_remove_button_clicked() { auto const torrent_id = iter->get_value(tracker_cols.torrent_id); auto const tracker_id = iter->get_value(tracker_cols.tracker_id); - tr_variant top; - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, tr_quark_get_string_view(TR_KEY_torrent_set_kebab)); - auto* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddInt(args, TR_KEY_id, torrent_id); - auto* const trackers = tr_variantDictAddList(args, TR_KEY_tracker_remove_camel, 1); - tr_variantListAddInt(trackers, tracker_id); - - core_->exec(top); + // TODO(ckerr): migrate to `TR_KEY_tracker_list` + auto params = tr_variant::Map{ 2U }; + params.try_emplace(TR_KEY_ids, Session::to_variant({ torrent_id })); + params.try_emplace(TR_KEY_tracker_remove, Session::to_variant({ tracker_id })); + core_->exec(TR_KEY_torrent_set, std::move(params)); refresh(); } } diff --git a/gtk/Session.cc b/gtk/Session.cc index 93a07df47..e33e48c14 100644 --- a/gtk/Session.cc +++ b/gtk/Session.cc @@ -91,7 +91,7 @@ public: void remove_torrent(tr_torrent_id_t id, bool delete_files); - void send_rpc_request(tr_variant const& request, int64_t tag, std::function const& response_func); + void send_rpc_request(tr_quark method, tr_variant const& params, std::function on_response); void commit_prefs_change(tr_quark key); @@ -1008,16 +1008,11 @@ void Session::update() impl_->update(); } -void Session::start_now(tr_torrent_id_t id) +void Session::start_now(tr_torrent_id_t const id) { - tr_variant top; - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, "torrent-start-now"); - - auto* args = tr_variantDictAddDict(&top, TR_KEY_arguments, 1); - auto* ids = tr_variantDictAddList(args, TR_KEY_ids, 1); - tr_variantListAddInt(ids, id); - exec(top); + auto params = tr_variant::Map{ 1U }; + params.try_emplace(TR_KEY_ids, to_variant({ id })); + exec(TR_KEY_torrent_start_now, std::move(params)); } void Session::Impl::update() @@ -1208,31 +1203,34 @@ void Session::set_pref(tr_quark const key, double newval) **** ***/ -/* #define DEBUG_RPC */ - namespace { -int64_t nextTag = 1; +int64_t nextId = 1; + +bool const verbose_ = tr_env_key_exists("TR_RPC_VERBOSE"); std::map> pendingRequests; bool core_read_rpc_response_idle(tr_variant& response) { - if (int64_t tag = 0; tr_variantDictFindInt(&response, TR_KEY_tag, &tag)) + if (verbose_) { - if (auto const data_it = pendingRequests.find(tag); data_it != pendingRequests.end()) - { - if (auto const& response_func = data_it->second; response_func) - { - response_func(response); - } + fmt::print("{:s}:{:d} got response:\n{:s}\n", __FILE__, __LINE__, tr_variant_serde::json().to_string(response)); + } - pendingRequests.erase(data_it); - } - else + if (auto const* resmap = response.get_if()) + { + if (auto const id = resmap->value_if(TR_KEY_id)) { - gtr_warning(fmt::format(fmt::runtime(_("Couldn't find pending RPC request for tag {tag}")), fmt::arg("tag", tag))); + if (auto const nh = pendingRequests.extract(*id)) + { + nh.mapped()(response); + } + else + { + gtr_warning(fmt::format(fmt::runtime(_("Couldn't find pending RPC request for id {id}")), fmt::arg("id", *id))); + } } } @@ -1248,26 +1246,45 @@ void core_read_rpc_response(tr_session* /*session*/, tr_variant&& response) } // namespace void Session::Impl::send_rpc_request( - tr_variant const& request, - int64_t tag, - std::function const& response_func) + tr_quark const method, + tr_variant const& params, + std::function on_response) { if (session_ == nullptr) { gtr_error("GTK+ client doesn't support connections to remote servers yet."); + return; } - else + + // build the jsonrpc request + auto reqmap = tr_variant::Map{ 4U }; + reqmap.try_emplace(TR_KEY_jsonrpc, tr_variant::unmanaged_string(JsonRpc::Version)); + reqmap.try_emplace(TR_KEY_method, tr_variant::unmanaged_string(method)); + + // add params if there are any + if (params.has_value()) { - /* remember this request */ - pendingRequests.try_emplace(tag, response_func); - - /* make the request */ -#ifdef DEBUG_RPC - gtr_message(fmt::format("request: [{}]", tr_variantToStr(request, TR_VARIANT_FMT_JSON_LEAN))); -#endif - - tr_rpc_request_exec(session_, request, core_read_rpc_response); + reqmap.try_emplace(TR_KEY_params, params.clone()); } + + // add id if we want a response + auto callback = std::function{}; + if (on_response) + { + auto const id = nextId++; + pendingRequests.try_emplace(id, std::move(on_response)); + reqmap.try_emplace(TR_KEY_id, id); + callback = core_read_rpc_response; + } + + auto req = tr_variant{ std::move(reqmap) }; + + if (verbose_) + { + fmt::print("{:s}:{:d} sending req:\n{:s}\n", __FILE__, __LINE__, tr_variant_serde::json().to_string(req)); + } + + tr_rpc_request_exec(session_, req, std::move(callback)); } /*** @@ -1284,36 +1301,30 @@ void Session::port_test(PortTestIpProtocol const ip_protocol) } impl_->set_port_test_pending(true, ip_protocol); - auto const tag = nextTag++; - - auto arguments_map = tr_variant::Map{ 1U }; - arguments_map.try_emplace(TR_KEY_ip_protocol, tr_variant::unmanaged_string(IpStr[ip_protocol])); - - auto request_map = tr_variant::Map{ 3U }; - request_map.try_emplace(TR_KEY_method, tr_variant::unmanaged_string("port-test"sv)); - request_map.try_emplace(TR_KEY_tag, tag); - request_map.try_emplace(TR_KEY_arguments, std::move(arguments_map)); + auto params = tr_variant::Map{ 1U }; + params.try_emplace(TR_KEY_ip_protocol, tr_variant::unmanaged_string(IpStr[ip_protocol])); impl_->send_rpc_request( - tr_variant{ std::move(request_map) }, - tag, + TR_KEY_port_test, + std::move(params), [this, ip_protocol](tr_variant& response) { impl_->set_port_test_pending(false, ip_protocol); - auto status = std::optional{}; - if (tr_variant* args = nullptr; tr_variantDictFindDict(&response, TR_KEY_arguments, &args)) + auto is_open = std::optional(); + + if (auto const* resmap = response.get_if()) { - if (auto result = bool{}; tr_variantDictFindBool(args, TR_KEY_port_is_open_kebab, &result)) + if (auto const* result = resmap->find_if(TR_KEY_result)) { - status = result; + is_open = result->value_if(TR_KEY_port_is_open); } } // If for whatever reason the status optional is empty here, // then something must have gone wrong with the port test, // so the UI should show the "error" state - impl_->signal_port_tested().emit(status, ip_protocol); + impl_->signal_port_tested().emit(is_open, ip_protocol); }); } @@ -1341,46 +1352,35 @@ void Session::Impl::set_port_test_pending(bool pending, Session::PortTestIpProto void Session::blocklist_update() { - auto const tag = nextTag; - ++nextTag; - - tr_variant request; - tr_variantInitDict(&request, 2); - tr_variantDictAddStrView(&request, TR_KEY_method, "blocklist-update"); - tr_variantDictAddInt(&request, TR_KEY_tag, tag); impl_->send_rpc_request( - request, - tag, - [this](auto& response) + TR_KEY_blocklist_update, + tr_variant{}, // no params + [this](tr_variant& response) { - tr_variant* args = nullptr; - int64_t ruleCount = 0; + std::optional n_rules; - if (!tr_variantDictFindDict(&response, TR_KEY_arguments, &args) || - !tr_variantDictFindInt(args, TR_KEY_blocklist_size_kebab, &ruleCount)) + if (auto const* resmap = response.get_if()) { - ruleCount = -1; + if (auto const* result = resmap->find_if(TR_KEY_result)) + { + n_rules = result->value_if(TR_KEY_blocklist_size); + } } - if (ruleCount > 0) + if (n_rules.has_value()) { gtr_pref_int_set(TR_KEY_blocklist_date, tr_time()); } - impl_->signal_blocklist_updated().emit(ruleCount >= 0); + impl_->signal_blocklist_updated().emit(*n_rules >= 0); }); } -/*** -**** -***/ +// --- -void Session::exec(tr_variant const& request) +void Session::exec(tr_quark method, tr_variant const& params) { - auto const tag = nextTag; - ++nextTag; - - impl_->send_rpc_request(request, tag, {}); + impl_->send_rpc_request(method, params, {}); } /*** diff --git a/gtk/Session.h b/gtk/Session.h index 8a022345f..65613b02d 100644 --- a/gtk/Session.h +++ b/gtk/Session.h @@ -134,16 +134,35 @@ public: void set_pref(tr_quark key, int val); void set_pref(tr_quark key, double val); - /** - *** - **/ + // --- + + // Helper for building RPC payloads. + // TODO(C++20): fold these two into a single std::span method + template + [[nodiscard]] static auto to_variant(std::vector const& items) + { + auto vec = tr_variant::Vector{}; + vec.reserve(std::size(items)); + for (auto const& item : items) + { + vec.emplace_back(item); + } + return vec; + } + template + [[nodiscard]] static auto to_variant(std::initializer_list items) + { + return to_variant(std::vector{ std::move(items) }); + } + + // --- void port_test(PortTestIpProtocol ip_protocol); bool port_test_pending(PortTestIpProtocol ip_protocol) const noexcept; void blocklist_update(); - void exec(tr_variant const& request); + void exec(tr_quark method, tr_variant const& params); void open_folder(tr_torrent_id_t torrent_id) const; diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index 79877a569..4f2f5cd28 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -691,7 +691,7 @@ uint64_t tr_ntohll(uint64_t netlonglong) // --- ENVIRONMENT -bool tr_env_key_exists(char const* key) +bool tr_env_key_exists(char const* key) noexcept { TR_ASSERT(key != nullptr); diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 4190bca02..f9dbb2dbf 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -286,7 +286,7 @@ constexpr void tr_timeUpdate(time_t now) noexcept // --- /** @brief Check if environment variable exists. */ -[[nodiscard]] bool tr_env_key_exists(char const* key); +[[nodiscard]] bool tr_env_key_exists(char const* key) noexcept; /** @brief Get environment variable value as string. */ [[nodiscard]] std::string tr_env_get_string(std::string_view key, std::string_view default_value = {}); diff --git a/libtransmission/variant.h b/libtransmission/variant.h index a34656eab..af515cced 100644 --- a/libtransmission/variant.h +++ b/libtransmission/variant.h @@ -230,7 +230,7 @@ public: return it->second.value_if(); } - return {}; + return std::nullopt; } template @@ -241,7 +241,7 @@ public: return it->second.value_if(); } - return {}; + return std::nullopt; } private: