mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
feat: use api_compat for torrent .resume files (#7932)
* test: add benc2cpp.py, a benc beautifier for hardcoded cpp test cases * test: add .resume file unit test * refactor: use api_compat::convert_incoming_data() and convert_outgoing_data() on .resume files * chore: mark TR_KEY_peers2_6_kebab as APICOMPAT * chore: mark TR_KEY_speed_Bps_kebab as APICOMPAT * chore: mark TR_KEY_use_speed_limit_kebab as APICOMPAT * chore: mark as APICOMPAT: TR_KEY_use_global_speed_limit_kebab * chore: mark as APICOMPAT: TR_KEY_ratio_mode_kebab * chore: mark as APICOMPAT: TR_KEY_idle_limit_kebab * chore: mark as APICOMPAT: TR_KEY_idle_mode_kebab * chore: mark as APICOMPAT: TR_KEY_max_peers_kebab * chore: mark as APICOMPAT: TR_KEY_added_date_kebab * chore: mark as APICOMPAT: TR_KEY_seeding_time_seconds_kebab * chore: mark as APICOMPAT: TR_KEY_downloading_time_seconds_kebab * chore: mark as APICOMPAT: TR_KEY_bandwidth_priority * chore: mark as APICOMPAT: TR_KEY_done_date_kebab * chore: mark as APICOMPAT: TR_KEY_activity_date_kebab * chore: remove remaining _kebab cases from resume.cc * chore: clang-format
This commit is contained in:
@@ -246,8 +246,8 @@ auto constexpr RpcKeys = std::array<ApiKey, 212U>{ {
|
|||||||
} };
|
} };
|
||||||
|
|
||||||
auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
||||||
{ TR_KEY_activity_date, TR_KEY_activity_date_kebab }, // TODO(ckerr) legacy duplicate
|
{ TR_KEY_activity_date, TR_KEY_activity_date_kebab_APICOMPAT },
|
||||||
{ TR_KEY_added_date, TR_KEY_added_date_kebab }, // TODO(ckerr) legacy duplicate
|
{ TR_KEY_added_date, TR_KEY_added_date_kebab_APICOMPAT },
|
||||||
{ TR_KEY_alt_speed_down, TR_KEY_alt_speed_down_kebab },
|
{ TR_KEY_alt_speed_down, TR_KEY_alt_speed_down_kebab },
|
||||||
{ TR_KEY_alt_speed_enabled, TR_KEY_alt_speed_enabled_kebab },
|
{ TR_KEY_alt_speed_enabled, TR_KEY_alt_speed_enabled_kebab },
|
||||||
{ TR_KEY_alt_speed_time_begin, TR_KEY_alt_speed_time_begin_kebab },
|
{ TR_KEY_alt_speed_time_begin, TR_KEY_alt_speed_time_begin_kebab },
|
||||||
@@ -259,7 +259,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_announce_ip_enabled, TR_KEY_announce_ip_enabled_kebab },
|
{ TR_KEY_announce_ip_enabled, TR_KEY_announce_ip_enabled_kebab },
|
||||||
{ TR_KEY_anti_brute_force_enabled, TR_KEY_anti_brute_force_enabled_kebab },
|
{ TR_KEY_anti_brute_force_enabled, TR_KEY_anti_brute_force_enabled_kebab },
|
||||||
{ TR_KEY_anti_brute_force_threshold, TR_KEY_anti_brute_force_threshold_kebab },
|
{ TR_KEY_anti_brute_force_threshold, TR_KEY_anti_brute_force_threshold_kebab },
|
||||||
{ TR_KEY_bandwidth_priority, TR_KEY_bandwidth_priority_kebab }, // TODO(ckerr) legacy duplicate
|
{ TR_KEY_bandwidth_priority, TR_KEY_bandwidth_priority_kebab_APICOMPAT },
|
||||||
{ TR_KEY_bind_address_ipv4, TR_KEY_bind_address_ipv4_kebab },
|
{ TR_KEY_bind_address_ipv4, TR_KEY_bind_address_ipv4_kebab },
|
||||||
{ TR_KEY_bind_address_ipv6, TR_KEY_bind_address_ipv6_kebab },
|
{ TR_KEY_bind_address_ipv6, TR_KEY_bind_address_ipv6_kebab },
|
||||||
{ TR_KEY_blocklist_date, TR_KEY_blocklist_date_kebab },
|
{ TR_KEY_blocklist_date, TR_KEY_blocklist_date_kebab },
|
||||||
@@ -272,18 +272,18 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_details_window_height, TR_KEY_details_window_height_kebab },
|
{ TR_KEY_details_window_height, TR_KEY_details_window_height_kebab },
|
||||||
{ TR_KEY_details_window_width, TR_KEY_details_window_width_kebab },
|
{ TR_KEY_details_window_width, TR_KEY_details_window_width_kebab },
|
||||||
{ TR_KEY_dht_enabled, TR_KEY_dht_enabled_kebab },
|
{ TR_KEY_dht_enabled, TR_KEY_dht_enabled_kebab },
|
||||||
{ TR_KEY_done_date, TR_KEY_done_date_kebab }, // TODO(ckerr) legacy duplicate
|
{ TR_KEY_done_date, TR_KEY_done_date_kebab_APICOMPAT },
|
||||||
{ TR_KEY_download_dir, TR_KEY_download_dir_kebab }, // TODO(ckerr) legacy duplicate
|
{ TR_KEY_download_dir, TR_KEY_download_dir_kebab }, // TODO(ckerr) legacy duplicate
|
||||||
{ TR_KEY_download_queue_enabled, TR_KEY_download_queue_enabled_kebab },
|
{ TR_KEY_download_queue_enabled, TR_KEY_download_queue_enabled_kebab },
|
||||||
{ TR_KEY_download_queue_size, TR_KEY_download_queue_size_kebab },
|
{ TR_KEY_download_queue_size, TR_KEY_download_queue_size_kebab },
|
||||||
{ TR_KEY_downloaded_bytes, TR_KEY_downloaded_bytes_kebab_APICOMPAT },
|
{ TR_KEY_downloaded_bytes, TR_KEY_downloaded_bytes_kebab_APICOMPAT },
|
||||||
{ TR_KEY_downloading_time_seconds, TR_KEY_downloading_time_seconds_kebab },
|
{ TR_KEY_downloading_time_seconds, TR_KEY_downloading_time_seconds_kebab_APICOMPAT },
|
||||||
{ TR_KEY_files_added, TR_KEY_files_added_kebab_APICOMPAT },
|
{ TR_KEY_files_added, TR_KEY_files_added_kebab_APICOMPAT },
|
||||||
{ TR_KEY_filter_mode, TR_KEY_filter_mode_kebab },
|
{ TR_KEY_filter_mode, TR_KEY_filter_mode_kebab },
|
||||||
{ TR_KEY_filter_text, TR_KEY_filter_text_kebab },
|
{ TR_KEY_filter_text, TR_KEY_filter_text_kebab },
|
||||||
{ TR_KEY_filter_trackers, TR_KEY_filter_trackers_kebab },
|
{ TR_KEY_filter_trackers, TR_KEY_filter_trackers_kebab },
|
||||||
{ TR_KEY_idle_limit, TR_KEY_idle_limit_kebab },
|
{ TR_KEY_idle_limit, TR_KEY_idle_limit_kebab_APICOMPAT },
|
||||||
{ TR_KEY_idle_mode, TR_KEY_idle_mode_kebab },
|
{ TR_KEY_idle_mode, TR_KEY_idle_mode_kebab_APICOMPAT },
|
||||||
{ TR_KEY_idle_seeding_limit, TR_KEY_idle_seeding_limit_kebab },
|
{ TR_KEY_idle_seeding_limit, TR_KEY_idle_seeding_limit_kebab },
|
||||||
{ TR_KEY_idle_seeding_limit_enabled, TR_KEY_idle_seeding_limit_enabled_kebab },
|
{ TR_KEY_idle_seeding_limit_enabled, TR_KEY_idle_seeding_limit_enabled_kebab },
|
||||||
{ TR_KEY_incomplete_dir, TR_KEY_incomplete_dir_kebab },
|
{ TR_KEY_incomplete_dir, TR_KEY_incomplete_dir_kebab },
|
||||||
@@ -296,7 +296,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_main_window_width, TR_KEY_main_window_width_kebab },
|
{ TR_KEY_main_window_width, TR_KEY_main_window_width_kebab },
|
||||||
{ TR_KEY_main_window_x, TR_KEY_main_window_x_kebab },
|
{ TR_KEY_main_window_x, TR_KEY_main_window_x_kebab },
|
||||||
{ TR_KEY_main_window_y, TR_KEY_main_window_y_kebab },
|
{ TR_KEY_main_window_y, TR_KEY_main_window_y_kebab },
|
||||||
{ TR_KEY_max_peers, TR_KEY_max_peers_kebab },
|
{ TR_KEY_max_peers, TR_KEY_max_peers_kebab_APICOMPAT },
|
||||||
{ TR_KEY_message_level, TR_KEY_message_level_kebab },
|
{ TR_KEY_message_level, TR_KEY_message_level_kebab },
|
||||||
{ TR_KEY_open_dialog_dir, TR_KEY_open_dialog_dir_kebab },
|
{ TR_KEY_open_dialog_dir, TR_KEY_open_dialog_dir_kebab },
|
||||||
{ TR_KEY_peer_congestion_algorithm, TR_KEY_peer_congestion_algorithm_kebab },
|
{ TR_KEY_peer_congestion_algorithm, TR_KEY_peer_congestion_algorithm_kebab },
|
||||||
@@ -307,7 +307,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_peer_port_random_low, TR_KEY_peer_port_random_low_kebab },
|
{ TR_KEY_peer_port_random_low, TR_KEY_peer_port_random_low_kebab },
|
||||||
{ TR_KEY_peer_port_random_on_start, TR_KEY_peer_port_random_on_start_kebab },
|
{ TR_KEY_peer_port_random_on_start, TR_KEY_peer_port_random_on_start_kebab },
|
||||||
{ TR_KEY_peer_socket_tos, TR_KEY_peer_socket_tos_kebab },
|
{ TR_KEY_peer_socket_tos, TR_KEY_peer_socket_tos_kebab },
|
||||||
{ TR_KEY_peers2_6, TR_KEY_peers2_6_kebab },
|
{ TR_KEY_peers2_6, TR_KEY_peers2_6_kebab_APICOMPAT },
|
||||||
{ TR_KEY_pex_enabled, TR_KEY_pex_enabled_kebab },
|
{ TR_KEY_pex_enabled, TR_KEY_pex_enabled_kebab },
|
||||||
{ TR_KEY_port_forwarding_enabled, TR_KEY_port_forwarding_enabled_kebab },
|
{ TR_KEY_port_forwarding_enabled, TR_KEY_port_forwarding_enabled_kebab },
|
||||||
{ TR_KEY_prompt_before_exit, TR_KEY_prompt_before_exit_kebab },
|
{ TR_KEY_prompt_before_exit, TR_KEY_prompt_before_exit_kebab },
|
||||||
@@ -315,7 +315,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_queue_stalled_minutes, TR_KEY_queue_stalled_minutes_kebab },
|
{ TR_KEY_queue_stalled_minutes, TR_KEY_queue_stalled_minutes_kebab },
|
||||||
{ TR_KEY_ratio_limit, TR_KEY_ratio_limit_kebab },
|
{ TR_KEY_ratio_limit, TR_KEY_ratio_limit_kebab },
|
||||||
{ TR_KEY_ratio_limit_enabled, TR_KEY_ratio_limit_enabled_kebab },
|
{ TR_KEY_ratio_limit_enabled, TR_KEY_ratio_limit_enabled_kebab },
|
||||||
{ TR_KEY_ratio_mode, TR_KEY_ratio_mode_kebab },
|
{ TR_KEY_ratio_mode, TR_KEY_ratio_mode_kebab_APICOMPAT },
|
||||||
{ TR_KEY_read_clipboard, TR_KEY_read_clipboard_kebab },
|
{ TR_KEY_read_clipboard, TR_KEY_read_clipboard_kebab },
|
||||||
{ TR_KEY_remote_session_enabled, TR_KEY_remote_session_enabled_kebab },
|
{ TR_KEY_remote_session_enabled, TR_KEY_remote_session_enabled_kebab },
|
||||||
{ TR_KEY_remote_session_host, TR_KEY_remote_session_host_kebab },
|
{ TR_KEY_remote_session_host, TR_KEY_remote_session_host_kebab },
|
||||||
@@ -347,7 +347,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_seconds_active, TR_KEY_seconds_active_kebab_APICOMPAT },
|
{ TR_KEY_seconds_active, TR_KEY_seconds_active_kebab_APICOMPAT },
|
||||||
{ TR_KEY_seed_queue_enabled, TR_KEY_seed_queue_enabled_kebab },
|
{ TR_KEY_seed_queue_enabled, TR_KEY_seed_queue_enabled_kebab },
|
||||||
{ TR_KEY_seed_queue_size, TR_KEY_seed_queue_size_kebab },
|
{ TR_KEY_seed_queue_size, TR_KEY_seed_queue_size_kebab },
|
||||||
{ TR_KEY_seeding_time_seconds, TR_KEY_seeding_time_seconds_kebab },
|
{ TR_KEY_seeding_time_seconds, TR_KEY_seeding_time_seconds_kebab_APICOMPAT },
|
||||||
{ TR_KEY_session_count, TR_KEY_session_count_kebab_APICOMPAT },
|
{ TR_KEY_session_count, TR_KEY_session_count_kebab_APICOMPAT },
|
||||||
{ TR_KEY_show_backup_trackers, TR_KEY_show_backup_trackers_kebab },
|
{ TR_KEY_show_backup_trackers, TR_KEY_show_backup_trackers_kebab },
|
||||||
{ TR_KEY_show_extra_peer_details, TR_KEY_show_extra_peer_details_kebab },
|
{ TR_KEY_show_extra_peer_details, TR_KEY_show_extra_peer_details_kebab },
|
||||||
@@ -360,7 +360,7 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_sleep_per_seconds_during_verify, TR_KEY_sleep_per_seconds_during_verify_kebab },
|
{ TR_KEY_sleep_per_seconds_during_verify, TR_KEY_sleep_per_seconds_during_verify_kebab },
|
||||||
{ TR_KEY_sort_mode, TR_KEY_sort_mode_kebab },
|
{ TR_KEY_sort_mode, TR_KEY_sort_mode_kebab },
|
||||||
{ TR_KEY_sort_reversed, TR_KEY_sort_reversed_kebab },
|
{ TR_KEY_sort_reversed, TR_KEY_sort_reversed_kebab },
|
||||||
{ TR_KEY_speed_Bps, TR_KEY_speed_Bps_kebab },
|
{ TR_KEY_speed_Bps, TR_KEY_speed_Bps_kebab_APICOMPAT },
|
||||||
{ TR_KEY_speed_limit_down, TR_KEY_speed_limit_down_kebab },
|
{ TR_KEY_speed_limit_down, TR_KEY_speed_limit_down_kebab },
|
||||||
{ TR_KEY_speed_limit_down_enabled, TR_KEY_speed_limit_down_enabled_kebab },
|
{ TR_KEY_speed_limit_down_enabled, TR_KEY_speed_limit_down_enabled_kebab },
|
||||||
{ TR_KEY_speed_limit_up, TR_KEY_speed_limit_up_kebab },
|
{ TR_KEY_speed_limit_up, TR_KEY_speed_limit_up_kebab },
|
||||||
@@ -379,8 +379,8 @@ auto constexpr SessionKeys = std::array<ApiKey, 139U>{ {
|
|||||||
{ TR_KEY_trash_original_torrent_files, TR_KEY_trash_original_torrent_files_kebab },
|
{ TR_KEY_trash_original_torrent_files, TR_KEY_trash_original_torrent_files_kebab },
|
||||||
{ TR_KEY_upload_slots_per_torrent, TR_KEY_upload_slots_per_torrent_kebab },
|
{ TR_KEY_upload_slots_per_torrent, TR_KEY_upload_slots_per_torrent_kebab },
|
||||||
{ TR_KEY_uploaded_bytes, TR_KEY_uploaded_bytes_kebab_APICOMPAT },
|
{ TR_KEY_uploaded_bytes, TR_KEY_uploaded_bytes_kebab_APICOMPAT },
|
||||||
{ TR_KEY_use_global_speed_limit, TR_KEY_use_global_speed_limit_kebab },
|
{ TR_KEY_use_global_speed_limit, TR_KEY_use_global_speed_limit_kebab_APICOMPAT },
|
||||||
{ TR_KEY_use_speed_limit, TR_KEY_use_speed_limit_kebab },
|
{ TR_KEY_use_speed_limit, TR_KEY_use_speed_limit_kebab_APICOMPAT },
|
||||||
{ TR_KEY_utp_enabled, TR_KEY_utp_enabled_kebab },
|
{ TR_KEY_utp_enabled, TR_KEY_utp_enabled_kebab },
|
||||||
{ TR_KEY_watch_dir, TR_KEY_watch_dir_kebab },
|
{ TR_KEY_watch_dir, TR_KEY_watch_dir_kebab },
|
||||||
{ TR_KEY_watch_dir_enabled, TR_KEY_watch_dir_enabled_kebab },
|
{ TR_KEY_watch_dir_enabled, TR_KEY_watch_dir_enabled_kebab },
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_NONE, /* represented as an empty string */
|
TR_KEY_NONE, /* represented as an empty string */
|
||||||
TR_KEY_active_torrent_count_camel, /* rpc (deprecated) */
|
TR_KEY_active_torrent_count_camel, /* rpc (deprecated) */
|
||||||
TR_KEY_active_torrent_count, /* rpc */
|
TR_KEY_active_torrent_count, /* rpc */
|
||||||
TR_KEY_activity_date_kebab, /* resume file (legacy) */
|
TR_KEY_activity_date_kebab_APICOMPAT,
|
||||||
TR_KEY_activity_date_camel, /* rpc (deprecated) */
|
TR_KEY_activity_date_camel, /* rpc (deprecated) */
|
||||||
TR_KEY_activity_date, /* rpc, resume file */
|
TR_KEY_activity_date, /* rpc, resume file */
|
||||||
TR_KEY_added, /* pex */
|
TR_KEY_added, /* pex */
|
||||||
TR_KEY_added_date_kebab, /* resume file (legacy) */
|
TR_KEY_added_date_kebab_APICOMPAT, /* resume file (legacy) */
|
||||||
TR_KEY_added_f, /* pex */
|
TR_KEY_added_f, /* pex */
|
||||||
TR_KEY_added6, /* pex */
|
TR_KEY_added6, /* pex */
|
||||||
TR_KEY_added6_f, /* pex */
|
TR_KEY_added6_f, /* pex */
|
||||||
@@ -74,7 +74,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_anti_brute_force_threshold, /* rpc, settings */
|
TR_KEY_anti_brute_force_threshold, /* rpc, settings */
|
||||||
TR_KEY_arguments, /* rpc */
|
TR_KEY_arguments, /* rpc */
|
||||||
TR_KEY_availability, // rpc
|
TR_KEY_availability, // rpc
|
||||||
TR_KEY_bandwidth_priority_kebab,
|
TR_KEY_bandwidth_priority_kebab_APICOMPAT,
|
||||||
TR_KEY_bandwidth_priority_camel,
|
TR_KEY_bandwidth_priority_camel,
|
||||||
TR_KEY_bandwidth_priority,
|
TR_KEY_bandwidth_priority,
|
||||||
TR_KEY_begin_piece,
|
TR_KEY_begin_piece,
|
||||||
@@ -143,7 +143,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_dht_enabled_kebab,
|
TR_KEY_dht_enabled_kebab,
|
||||||
TR_KEY_dht_enabled,
|
TR_KEY_dht_enabled,
|
||||||
TR_KEY_dnd,
|
TR_KEY_dnd,
|
||||||
TR_KEY_done_date_kebab,
|
TR_KEY_done_date_kebab_APICOMPAT,
|
||||||
TR_KEY_done_date_camel,
|
TR_KEY_done_date_camel,
|
||||||
TR_KEY_done_date,
|
TR_KEY_done_date,
|
||||||
TR_KEY_download_dir_kebab,
|
TR_KEY_download_dir_kebab,
|
||||||
@@ -170,7 +170,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_downloaded_bytes,
|
TR_KEY_downloaded_bytes,
|
||||||
TR_KEY_downloaded_ever,
|
TR_KEY_downloaded_ever,
|
||||||
TR_KEY_downloader_count,
|
TR_KEY_downloader_count,
|
||||||
TR_KEY_downloading_time_seconds_kebab,
|
TR_KEY_downloading_time_seconds_kebab_APICOMPAT,
|
||||||
TR_KEY_downloading_time_seconds,
|
TR_KEY_downloading_time_seconds,
|
||||||
TR_KEY_dropped,
|
TR_KEY_dropped,
|
||||||
TR_KEY_dropped6,
|
TR_KEY_dropped6,
|
||||||
@@ -246,8 +246,8 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_host,
|
TR_KEY_host,
|
||||||
TR_KEY_id,
|
TR_KEY_id,
|
||||||
TR_KEY_id_timestamp,
|
TR_KEY_id_timestamp,
|
||||||
TR_KEY_idle_limit_kebab,
|
TR_KEY_idle_limit_kebab_APICOMPAT,
|
||||||
TR_KEY_idle_mode_kebab,
|
TR_KEY_idle_mode_kebab_APICOMPAT,
|
||||||
TR_KEY_idle_seeding_limit_kebab,
|
TR_KEY_idle_seeding_limit_kebab,
|
||||||
TR_KEY_idle_seeding_limit_enabled_kebab,
|
TR_KEY_idle_seeding_limit_enabled_kebab,
|
||||||
TR_KEY_idle_limit,
|
TR_KEY_idle_limit,
|
||||||
@@ -333,7 +333,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_main_window_y,
|
TR_KEY_main_window_y,
|
||||||
TR_KEY_manual_announce_time_camel,
|
TR_KEY_manual_announce_time_camel,
|
||||||
TR_KEY_manual_announce_time,
|
TR_KEY_manual_announce_time,
|
||||||
TR_KEY_max_peers_kebab,
|
TR_KEY_max_peers_kebab_APICOMPAT,
|
||||||
TR_KEY_max_connected_peers_camel,
|
TR_KEY_max_connected_peers_camel,
|
||||||
TR_KEY_max_connected_peers,
|
TR_KEY_max_connected_peers,
|
||||||
TR_KEY_max_peers,
|
TR_KEY_max_peers,
|
||||||
@@ -392,7 +392,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_peer_socket_tos,
|
TR_KEY_peer_socket_tos,
|
||||||
TR_KEY_peers,
|
TR_KEY_peers,
|
||||||
TR_KEY_peers2,
|
TR_KEY_peers2,
|
||||||
TR_KEY_peers2_6_kebab,
|
TR_KEY_peers2_6_kebab_APICOMPAT,
|
||||||
TR_KEY_peers2_6,
|
TR_KEY_peers2_6,
|
||||||
TR_KEY_peers_connected_camel,
|
TR_KEY_peers_connected_camel,
|
||||||
TR_KEY_peers_from_camel,
|
TR_KEY_peers_from_camel,
|
||||||
@@ -464,7 +464,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_rate_upload,
|
TR_KEY_rate_upload,
|
||||||
TR_KEY_ratio_limit_kebab,
|
TR_KEY_ratio_limit_kebab,
|
||||||
TR_KEY_ratio_limit_enabled_kebab,
|
TR_KEY_ratio_limit_enabled_kebab,
|
||||||
TR_KEY_ratio_mode_kebab,
|
TR_KEY_ratio_mode_kebab_APICOMPAT,
|
||||||
TR_KEY_ratio_limit,
|
TR_KEY_ratio_limit,
|
||||||
TR_KEY_ratio_limit_enabled,
|
TR_KEY_ratio_limit_enabled,
|
||||||
TR_KEY_ratio_mode,
|
TR_KEY_ratio_mode,
|
||||||
@@ -564,7 +564,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_seed_ratio_mode,
|
TR_KEY_seed_ratio_mode,
|
||||||
TR_KEY_seeder_count_camel,
|
TR_KEY_seeder_count_camel,
|
||||||
TR_KEY_seeder_count,
|
TR_KEY_seeder_count,
|
||||||
TR_KEY_seeding_time_seconds_kebab,
|
TR_KEY_seeding_time_seconds_kebab_APICOMPAT,
|
||||||
TR_KEY_seeding_time_seconds,
|
TR_KEY_seeding_time_seconds,
|
||||||
TR_KEY_sequential_download,
|
TR_KEY_sequential_download,
|
||||||
TR_KEY_sequential_download_from_piece,
|
TR_KEY_sequential_download_from_piece,
|
||||||
@@ -613,7 +613,7 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_sort_reversed,
|
TR_KEY_sort_reversed,
|
||||||
TR_KEY_source,
|
TR_KEY_source,
|
||||||
TR_KEY_speed,
|
TR_KEY_speed,
|
||||||
TR_KEY_speed_Bps_kebab,
|
TR_KEY_speed_Bps_kebab_APICOMPAT,
|
||||||
TR_KEY_speed_bytes_kebab,
|
TR_KEY_speed_bytes_kebab,
|
||||||
TR_KEY_speed_limit_down_kebab,
|
TR_KEY_speed_limit_down_kebab,
|
||||||
TR_KEY_speed_limit_down_enabled_kebab,
|
TR_KEY_speed_limit_down_enabled_kebab,
|
||||||
@@ -722,8 +722,8 @@ enum // NOLINT(performance-enum-size)
|
|||||||
TR_KEY_uploaded_bytes,
|
TR_KEY_uploaded_bytes,
|
||||||
TR_KEY_uploaded_ever,
|
TR_KEY_uploaded_ever,
|
||||||
TR_KEY_url_list,
|
TR_KEY_url_list,
|
||||||
TR_KEY_use_global_speed_limit_kebab,
|
TR_KEY_use_global_speed_limit_kebab_APICOMPAT,
|
||||||
TR_KEY_use_speed_limit_kebab,
|
TR_KEY_use_speed_limit_kebab_APICOMPAT,
|
||||||
TR_KEY_use_global_speed_limit,
|
TR_KEY_use_global_speed_limit,
|
||||||
TR_KEY_use_speed_limit,
|
TR_KEY_use_speed_limit,
|
||||||
TR_KEY_ut_holepunch,
|
TR_KEY_ut_holepunch,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "libtransmission/transmission.h"
|
#include "libtransmission/transmission.h"
|
||||||
|
|
||||||
|
#include "libtransmission/api-compat.h"
|
||||||
#include "libtransmission/bitfield.h"
|
#include "libtransmission/bitfield.h"
|
||||||
#include "libtransmission/error.h"
|
#include "libtransmission/error.h"
|
||||||
#include "libtransmission/file.h"
|
#include "libtransmission/file.h"
|
||||||
@@ -74,7 +75,7 @@ auto load_peers(tr_variant::Map const& map, tr_torrent* tor)
|
|||||||
ret = tr_resume::Peers;
|
ret = tr_resume::Peers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const* l = map.find_if<tr_variant::Vector>({ TR_KEY_peers2_6, TR_KEY_peers2_6_kebab }); l != nullptr)
|
if (auto const* l = map.find_if<tr_variant::Vector>(TR_KEY_peers2_6))
|
||||||
{
|
{
|
||||||
auto const num_added = add_peers(tor, *l);
|
auto const num_added = add_peers(tor, *l);
|
||||||
tr_logAddTraceTor(tor, fmt::format("Loaded {} IPv6 peers from resume file", num_added));
|
tr_logAddTraceTor(tor, fmt::format("Loaded {} IPv6 peers from resume file", num_added));
|
||||||
@@ -266,7 +267,7 @@ void save_idle_limits(tr_variant::Map& map, tr_torrent const* tor)
|
|||||||
|
|
||||||
void load_single_speed_limit(tr_variant::Map const& map, tr_direction dir, tr_torrent* tor)
|
void load_single_speed_limit(tr_variant::Map const& map, tr_direction dir, tr_torrent* tor)
|
||||||
{
|
{
|
||||||
if (auto const i = map.value_if<int64_t>({ TR_KEY_speed_Bps, TR_KEY_speed_Bps_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_speed_Bps))
|
||||||
{
|
{
|
||||||
tor->set_speed_limit(dir, Speed{ *i, Speed::Units::Byps });
|
tor->set_speed_limit(dir, Speed{ *i, Speed::Units::Byps });
|
||||||
}
|
}
|
||||||
@@ -275,12 +276,12 @@ void load_single_speed_limit(tr_variant::Map const& map, tr_direction dir, tr_to
|
|||||||
tor->set_speed_limit(dir, Speed{ *i2, Speed::Units::KByps });
|
tor->set_speed_limit(dir, Speed{ *i2, Speed::Units::KByps });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const b = map.value_if<bool>({ TR_KEY_use_speed_limit, TR_KEY_use_speed_limit_kebab }); b)
|
if (auto const b = map.value_if<bool>(TR_KEY_use_speed_limit))
|
||||||
{
|
{
|
||||||
tor->use_speed_limit(dir, *b);
|
tor->use_speed_limit(dir, *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const b = map.value_if<bool>({ TR_KEY_use_global_speed_limit, TR_KEY_use_global_speed_limit_kebab }); b)
|
if (auto const b = map.value_if<bool>(TR_KEY_use_global_speed_limit))
|
||||||
{
|
{
|
||||||
tr_torrentUseSessionLimits(tor, *b);
|
tr_torrentUseSessionLimits(tor, *b);
|
||||||
}
|
}
|
||||||
@@ -290,15 +291,13 @@ auto load_speed_limits(tr_variant::Map const& map, tr_torrent* tor)
|
|||||||
{
|
{
|
||||||
auto ret = tr_resume::fields_t{};
|
auto ret = tr_resume::fields_t{};
|
||||||
|
|
||||||
if (auto const* child = map.find_if<tr_variant::Map>({ TR_KEY_speed_limit_up, TR_KEY_speed_limit_up_kebab });
|
if (auto const* child = map.find_if<tr_variant::Map>(TR_KEY_speed_limit_up))
|
||||||
child != nullptr)
|
|
||||||
{
|
{
|
||||||
load_single_speed_limit(*child, TR_UP, tor);
|
load_single_speed_limit(*child, TR_UP, tor);
|
||||||
ret = tr_resume::Speedlimit;
|
ret = tr_resume::Speedlimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const* child = map.find_if<tr_variant::Map>({ TR_KEY_speed_limit_down, TR_KEY_speed_limit_down_kebab });
|
if (auto const* child = map.find_if<tr_variant::Map>(TR_KEY_speed_limit_down))
|
||||||
child != nullptr)
|
|
||||||
{
|
{
|
||||||
load_single_speed_limit(*child, TR_DOWN, tor);
|
load_single_speed_limit(*child, TR_DOWN, tor);
|
||||||
ret = tr_resume::Speedlimit;
|
ret = tr_resume::Speedlimit;
|
||||||
@@ -309,18 +308,18 @@ auto load_speed_limits(tr_variant::Map const& map, tr_torrent* tor)
|
|||||||
|
|
||||||
tr_resume::fields_t load_ratio_limits(tr_variant::Map const& map, tr_torrent* tor)
|
tr_resume::fields_t load_ratio_limits(tr_variant::Map const& map, tr_torrent* tor)
|
||||||
{
|
{
|
||||||
auto const* const d = map.find_if<tr_variant::Map>({ TR_KEY_ratio_limit, TR_KEY_ratio_limit_kebab });
|
auto const* const d = map.find_if<tr_variant::Map>(TR_KEY_ratio_limit);
|
||||||
if (d == nullptr)
|
if (d == nullptr)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const dratio = d->value_if<double>({ TR_KEY_ratio_limit, TR_KEY_ratio_limit_kebab }); dratio)
|
if (auto const dratio = d->value_if<double>(TR_KEY_ratio_limit))
|
||||||
{
|
{
|
||||||
tor->set_seed_ratio(*dratio);
|
tor->set_seed_ratio(*dratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const i = d->value_if<int64_t>({ TR_KEY_ratio_mode, TR_KEY_ratio_mode_kebab }); i)
|
if (auto const i = d->value_if<int64_t>(TR_KEY_ratio_mode))
|
||||||
{
|
{
|
||||||
tor->set_seed_ratio_mode(static_cast<tr_ratiolimit>(*i));
|
tor->set_seed_ratio_mode(static_cast<tr_ratiolimit>(*i));
|
||||||
}
|
}
|
||||||
@@ -330,18 +329,18 @@ tr_resume::fields_t load_ratio_limits(tr_variant::Map const& map, tr_torrent* to
|
|||||||
|
|
||||||
tr_resume::fields_t load_idle_limits(tr_variant::Map const& map, tr_torrent* tor)
|
tr_resume::fields_t load_idle_limits(tr_variant::Map const& map, tr_torrent* tor)
|
||||||
{
|
{
|
||||||
auto const* const d = map.find_if<tr_variant::Map>({ TR_KEY_idle_limit, TR_KEY_idle_limit_kebab });
|
auto const* const d = map.find_if<tr_variant::Map>(TR_KEY_idle_limit);
|
||||||
if (d == nullptr)
|
if (d == nullptr)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const imin = d->value_if<int64_t>({ TR_KEY_idle_limit, TR_KEY_idle_limit_kebab }); imin)
|
if (auto const imin = d->value_if<int64_t>(TR_KEY_idle_limit))
|
||||||
{
|
{
|
||||||
tor->set_idle_limit_minutes(*imin);
|
tor->set_idle_limit_minutes(*imin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const i = d->value_if<int64_t>({ TR_KEY_idle_mode, TR_KEY_idle_mode_kebab }); i)
|
if (auto const i = d->value_if<int64_t>(TR_KEY_idle_mode))
|
||||||
{
|
{
|
||||||
tor->set_idle_limit_mode(static_cast<tr_idlelimit>(*i));
|
tor->set_idle_limit_mode(static_cast<tr_idlelimit>(*i));
|
||||||
}
|
}
|
||||||
@@ -636,6 +635,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
otop = libtransmission::api_compat::convert_incoming_data(*otop);
|
||||||
auto const* const p_map = otop->get_if<tr_variant::Map>();
|
auto const* const p_map = otop->get_if<tr_variant::Map>();
|
||||||
if (p_map == nullptr)
|
if (p_map == nullptr)
|
||||||
{
|
{
|
||||||
@@ -667,8 +667,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & (tr_resume::Progress | tr_resume::IncompleteDir)) != 0)
|
if ((fields_to_load & (tr_resume::Progress | tr_resume::IncompleteDir)) != 0)
|
||||||
{
|
{
|
||||||
if (auto sv = map.value_if<std::string_view>({ TR_KEY_incomplete_dir, TR_KEY_incomplete_dir_kebab });
|
if (auto sv = map.value_if<std::string_view>(TR_KEY_incomplete_dir); sv && !std::empty(*sv))
|
||||||
sv && !std::empty(*sv))
|
|
||||||
{
|
{
|
||||||
helper.load_incomplete_dir(*sv);
|
helper.load_incomplete_dir(*sv);
|
||||||
fields_loaded |= tr_resume::IncompleteDir;
|
fields_loaded |= tr_resume::IncompleteDir;
|
||||||
@@ -695,7 +694,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::MaxPeers) != 0)
|
if ((fields_to_load & tr_resume::MaxPeers) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_max_peers, TR_KEY_max_peers_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_max_peers))
|
||||||
{
|
{
|
||||||
tor->set_peer_limit(static_cast<uint16_t>(*i));
|
tor->set_peer_limit(static_cast<uint16_t>(*i));
|
||||||
fields_loaded |= tr_resume::MaxPeers;
|
fields_loaded |= tr_resume::MaxPeers;
|
||||||
@@ -713,7 +712,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::AddedDate) != 0)
|
if ((fields_to_load & tr_resume::AddedDate) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_added_date, TR_KEY_added_date_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_added_date))
|
||||||
{
|
{
|
||||||
helper.load_date_added(static_cast<time_t>(*i));
|
helper.load_date_added(static_cast<time_t>(*i));
|
||||||
fields_loaded |= tr_resume::AddedDate;
|
fields_loaded |= tr_resume::AddedDate;
|
||||||
@@ -722,7 +721,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::DoneDate) != 0)
|
if ((fields_to_load & tr_resume::DoneDate) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_done_date, TR_KEY_done_date_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_done_date))
|
||||||
{
|
{
|
||||||
helper.load_date_done(static_cast<time_t>(*i));
|
helper.load_date_done(static_cast<time_t>(*i));
|
||||||
fields_loaded |= tr_resume::DoneDate;
|
fields_loaded |= tr_resume::DoneDate;
|
||||||
@@ -731,7 +730,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::ActivityDate) != 0)
|
if ((fields_to_load & tr_resume::ActivityDate) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_activity_date, TR_KEY_activity_date_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_activity_date))
|
||||||
{
|
{
|
||||||
tor->set_date_active(*i);
|
tor->set_date_active(*i);
|
||||||
fields_loaded |= tr_resume::ActivityDate;
|
fields_loaded |= tr_resume::ActivityDate;
|
||||||
@@ -740,7 +739,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::TimeSeeding) != 0)
|
if ((fields_to_load & tr_resume::TimeSeeding) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_seeding_time_seconds, TR_KEY_seeding_time_seconds_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_seeding_time_seconds))
|
||||||
{
|
{
|
||||||
helper.load_seconds_seeding_before_current_start(*i);
|
helper.load_seconds_seeding_before_current_start(*i);
|
||||||
fields_loaded |= tr_resume::TimeSeeding;
|
fields_loaded |= tr_resume::TimeSeeding;
|
||||||
@@ -749,7 +748,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::TimeDownloading) != 0)
|
if ((fields_to_load & tr_resume::TimeDownloading) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_downloading_time_seconds, TR_KEY_downloading_time_seconds_kebab }); i)
|
if (auto const i = map.value_if<int64_t>(TR_KEY_downloading_time_seconds))
|
||||||
{
|
{
|
||||||
helper.load_seconds_downloading_before_current_start(*i);
|
helper.load_seconds_downloading_before_current_start(*i);
|
||||||
fields_loaded |= tr_resume::TimeDownloading;
|
fields_loaded |= tr_resume::TimeDownloading;
|
||||||
@@ -758,8 +757,7 @@ tr_resume::fields_t load_from_file(tr_torrent* tor, tr_torrent::ResumeHelper& he
|
|||||||
|
|
||||||
if ((fields_to_load & tr_resume::BandwidthPriority) != 0)
|
if ((fields_to_load & tr_resume::BandwidthPriority) != 0)
|
||||||
{
|
{
|
||||||
if (auto i = map.value_if<int64_t>({ TR_KEY_bandwidth_priority, TR_KEY_bandwidth_priority_kebab });
|
if (auto const i = map.value_if<int64_t>(TR_KEY_bandwidth_priority); i && tr_isPriority(static_cast<tr_priority_t>(*i)))
|
||||||
i && tr_isPriority(static_cast<tr_priority_t>(*i)))
|
|
||||||
{
|
{
|
||||||
tr_torrentSetPriority(tor, static_cast<tr_priority_t>(*i));
|
tr_torrentSetPriority(tor, static_cast<tr_priority_t>(*i));
|
||||||
fields_loaded |= tr_resume::BandwidthPriority;
|
fields_loaded |= tr_resume::BandwidthPriority;
|
||||||
@@ -985,8 +983,9 @@ void save(tr_torrent* const tor, tr_torrent::ResumeHelper const& helper)
|
|||||||
save_labels(map, tor);
|
save_labels(map, tor);
|
||||||
save_group(map, tor);
|
save_group(map, tor);
|
||||||
|
|
||||||
|
auto const out = libtransmission::api_compat::convert_outgoing_data(std::move(map));
|
||||||
auto serde = tr_variant_serde::benc();
|
auto serde = tr_variant_serde::benc();
|
||||||
if (!serde.to_file(std::move(map), tor->resume_file()))
|
if (!serde.to_file(out, tor->resume_file()))
|
||||||
{
|
{
|
||||||
tor->error().set_local_error(fmt::format("Unable to save resume file: {:s}", serde.error_.message()));
|
tor->error().set_local_error(fmt::format("Unable to save resume file: {:s}", serde.error_.message()));
|
||||||
}
|
}
|
||||||
|
|||||||
263
tests/assets/benc2cpp.py
Normal file
263
tests/assets/benc2cpp.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Created by GitHub Copilot (GPT-5.2 (Preview)).
|
||||||
|
#
|
||||||
|
# License: Same terms as Transmission itself (see COPYING). Transmission
|
||||||
|
# permits redistribution/modification under GNU GPLv2, GPLv3, or any future
|
||||||
|
# license endorsed by Mnemosyne LLC.
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Convert a bencoded (benc) file into a C++ concatenated string-literal
|
||||||
|
# fragment that preserves the exact original bytes. Output is whitespace-only
|
||||||
|
# formatted for readability (4-space indentation), similar in spirit to
|
||||||
|
# pretty-printed JSON.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# tests/assets/benc2cpp.py path/to/file.benc > out.cppfrag
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_cpp_string_literal(data: bytes) -> str:
|
||||||
|
r"""Return a single C++ string literal token for arbitrary bytes.
|
||||||
|
|
||||||
|
Uses normal (non-raw) string literals and emits \xNN for bytes that are not
|
||||||
|
safe/pleasant as-is.
|
||||||
|
"""
|
||||||
|
|
||||||
|
out = '"'
|
||||||
|
prev_was_hex_escape = False
|
||||||
|
for b in data:
|
||||||
|
ch = chr(b)
|
||||||
|
|
||||||
|
# C/C++ rule: \x escapes consume *all following hex digits*.
|
||||||
|
# If we emit "\xNN" and then a literal '0'..'9'/'a'..'f'/'A'..'F',
|
||||||
|
# it becomes a single (larger) hex escape and may fail to compile.
|
||||||
|
if (
|
||||||
|
prev_was_hex_escape
|
||||||
|
and (
|
||||||
|
(ord('0') <= b <= ord('9'))
|
||||||
|
or (ord('a') <= b <= ord('f'))
|
||||||
|
or (ord('A') <= b <= ord('F'))
|
||||||
|
)
|
||||||
|
):
|
||||||
|
out += f"\\x{b:02x}"
|
||||||
|
prev_was_hex_escape = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ch == "\\":
|
||||||
|
out += r"\\\\"
|
||||||
|
prev_was_hex_escape = False
|
||||||
|
elif ch == '"':
|
||||||
|
out += r"\\\""
|
||||||
|
prev_was_hex_escape = False
|
||||||
|
elif 0x20 <= b <= 0x7E:
|
||||||
|
out += ch
|
||||||
|
prev_was_hex_escape = False
|
||||||
|
else:
|
||||||
|
out += f"\\x{b:02x}"
|
||||||
|
prev_was_hex_escape = True
|
||||||
|
out += '"'
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def bencode_tokenize(data: bytes) -> list[bytes]:
|
||||||
|
r"""Tokenize bencode into syntactic units without changing bytes.
|
||||||
|
|
||||||
|
Tokens are:
|
||||||
|
- b"d", b"l", b"e"
|
||||||
|
- b"i...e" (entire integer token)
|
||||||
|
- b"<len>:<payload>" (entire string token, including length and colon)
|
||||||
|
|
||||||
|
This is a tokenizer only. It assumes the input is valid bencode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tokens: list[bytes] = []
|
||||||
|
i = 0
|
||||||
|
n = len(data)
|
||||||
|
|
||||||
|
def need(cond: bool, msg: str) -> None:
|
||||||
|
if not cond:
|
||||||
|
raise ValueError(f"Invalid bencode at offset {i}: {msg}")
|
||||||
|
|
||||||
|
while i < n:
|
||||||
|
b = data[i]
|
||||||
|
|
||||||
|
if b in (ord('d'), ord('l'), ord('e')):
|
||||||
|
tokens.append(bytes([b]))
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if b == ord('i'):
|
||||||
|
j = data.find(b'e', i + 1)
|
||||||
|
need(j != -1, "unterminated integer")
|
||||||
|
tokens.append(data[i:j + 1])
|
||||||
|
i = j + 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ord('0') <= b <= ord('9'):
|
||||||
|
j = i
|
||||||
|
while j < n and ord('0') <= data[j] <= ord('9'):
|
||||||
|
j += 1
|
||||||
|
need(j < n and data[j] == ord(':'), "string length missing colon")
|
||||||
|
strlen = int(data[i:j].decode('ascii'))
|
||||||
|
start = j + 1
|
||||||
|
end = start + strlen
|
||||||
|
need(end <= n, "string payload truncated")
|
||||||
|
tokens.append(data[i:end])
|
||||||
|
i = end
|
||||||
|
continue
|
||||||
|
|
||||||
|
msg = f"Invalid bencode at offset {i}: unexpected byte 0x{b:02x}"
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
def render_bencode_tokens_pretty(
|
||||||
|
tokens: list[bytes],
|
||||||
|
*,
|
||||||
|
base_indent: int = 4,
|
||||||
|
indent_step: int = 4,
|
||||||
|
) -> list[str]:
|
||||||
|
"""Render bencode tokens into indented C++ string literal lines.
|
||||||
|
|
||||||
|
Whitespace-only pretty-printing rules:
|
||||||
|
- One token per line by default.
|
||||||
|
- For dictionaries, if a key's value is a scalar (string or integer),
|
||||||
|
render the key and value on the same line separated by a space.
|
||||||
|
|
||||||
|
This changes only whitespace between C string fragments; the concatenated
|
||||||
|
bytes are identical to the input.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lines: list[str] = []
|
||||||
|
|
||||||
|
# Stack entries are either:
|
||||||
|
# ('list', None)
|
||||||
|
# ('dict', expecting_key: bool)
|
||||||
|
stack: list[tuple[str, bool | None]] = []
|
||||||
|
pending_dict_key: bytes | None = None
|
||||||
|
|
||||||
|
def depth() -> int:
|
||||||
|
return len(stack)
|
||||||
|
|
||||||
|
def indent() -> str:
|
||||||
|
return ' ' * (base_indent + depth() * indent_step)
|
||||||
|
|
||||||
|
def is_scalar_token(t: bytes) -> bool:
|
||||||
|
return t.startswith(b'i') or (t[:1].isdigit())
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < len(tokens):
|
||||||
|
tok = tokens[i]
|
||||||
|
|
||||||
|
if tok == b'e':
|
||||||
|
if pending_dict_key is not None:
|
||||||
|
key_lit = bytes_to_cpp_string_literal(pending_dict_key)
|
||||||
|
lines.append(indent() + key_lit)
|
||||||
|
pending_dict_key = None
|
||||||
|
|
||||||
|
if stack:
|
||||||
|
stack.pop()
|
||||||
|
|
||||||
|
lines.append(indent() + bytes_to_cpp_string_literal(tok))
|
||||||
|
|
||||||
|
# If this closed a value container in a dict,
|
||||||
|
# the parent dict is now ready for next key.
|
||||||
|
if stack and stack[-1][0] == 'dict' and stack[-1][1] is False:
|
||||||
|
stack[-1] = ('dict', True)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Dict key collection
|
||||||
|
if stack and stack[-1][0] == 'dict' and stack[-1][1] is True:
|
||||||
|
pending_dict_key = tok
|
||||||
|
stack[-1] = ('dict', False)
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Dict value emission
|
||||||
|
is_dict_value = (
|
||||||
|
stack
|
||||||
|
and stack[-1][0] == 'dict'
|
||||||
|
and stack[-1][1] is False
|
||||||
|
and pending_dict_key is not None
|
||||||
|
)
|
||||||
|
if is_dict_value:
|
||||||
|
if is_scalar_token(tok):
|
||||||
|
lines.append(
|
||||||
|
indent()
|
||||||
|
+ bytes_to_cpp_string_literal(pending_dict_key)
|
||||||
|
+ ' '
|
||||||
|
+ bytes_to_cpp_string_literal(tok)
|
||||||
|
)
|
||||||
|
pending_dict_key = None
|
||||||
|
stack[-1] = ('dict', True)
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Non-scalar (container) value: key on its own line, then container
|
||||||
|
# token.
|
||||||
|
key_lit = bytes_to_cpp_string_literal(pending_dict_key)
|
||||||
|
lines.append(indent() + key_lit)
|
||||||
|
pending_dict_key = None
|
||||||
|
|
||||||
|
lines.append(indent() + bytes_to_cpp_string_literal(tok))
|
||||||
|
if tok == b'd':
|
||||||
|
stack.append(('dict', True))
|
||||||
|
elif tok == b'l':
|
||||||
|
stack.append(('list', None))
|
||||||
|
else:
|
||||||
|
stack[-1] = ('dict', True)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Default emission
|
||||||
|
lines.append(indent() + bytes_to_cpp_string_literal(tok))
|
||||||
|
if tok == b'd':
|
||||||
|
stack.append(('dict', True))
|
||||||
|
elif tok == b'l':
|
||||||
|
stack.append(('list', None))
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if pending_dict_key is not None:
|
||||||
|
lines.append(indent() + bytes_to_cpp_string_literal(pending_dict_key))
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str]) -> int:
|
||||||
|
if len(argv) != 2:
|
||||||
|
sys.stderr.write(f"Usage: {Path(argv[0]).name} path/to/file.benc\n")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
in_path = Path(argv[1])
|
||||||
|
data = in_path.read_bytes()
|
||||||
|
|
||||||
|
tokens = bencode_tokenize(data)
|
||||||
|
pretty_lines = render_bencode_tokens_pretty(tokens)
|
||||||
|
|
||||||
|
sys.stdout.write("// clang-format off\n")
|
||||||
|
sys.stdout.write("constexpr std::string_view Benc =\n")
|
||||||
|
if not pretty_lines:
|
||||||
|
sys.stdout.write(" \"\";\n")
|
||||||
|
else:
|
||||||
|
for line in pretty_lines[:-1]:
|
||||||
|
sys.stdout.write(line)
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
sys.stdout.write(pretty_lines[-1])
|
||||||
|
sys.stdout.write(";\n")
|
||||||
|
sys.stdout.write("// clang-format on\n")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main(sys.argv))
|
||||||
@@ -644,6 +644,306 @@ constexpr std::string_view UnrecognisedInfoLegacyResponse = R"json({
|
|||||||
"tag": 10
|
"tag": 10
|
||||||
})json";
|
})json";
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
constexpr std::string_view LegacyResumeBenc =
|
||||||
|
"d"
|
||||||
|
"13:activity-date" "i1765724117e"
|
||||||
|
"10:added-date" "i1756689559e"
|
||||||
|
"18:bandwidth-priority" "i0e"
|
||||||
|
"7:corrupt" "i0e"
|
||||||
|
"11:destination" "30:/data/trackers/untracked/Books"
|
||||||
|
"3:dnd"
|
||||||
|
"l"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"e"
|
||||||
|
"9:done-date" "i1756689845e"
|
||||||
|
"10:downloaded" "i4830420542e"
|
||||||
|
"24:downloading-time-seconds" "i286e"
|
||||||
|
"5:files"
|
||||||
|
"l"
|
||||||
|
"102:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v01 - Oz - Baum - The Wonderful Wizard of Oz (1990).epub"
|
||||||
|
"100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v02 - Oz - Baum - The Marvelous Land of Oz (1904).epub"
|
||||||
|
"86:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v03 - Oz - Baum - Ozma of Oz (1907).epub"
|
||||||
|
"104:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v04 - Oz - Baum - Dorothy and the Wizard in Oz (1908).epub"
|
||||||
|
"90:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v05 - Oz - Baum - The Road to Oz (1909).epub"
|
||||||
|
"98:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v06 - Oz - Baum - The Emerald City of Oz (1910).epub"
|
||||||
|
"100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v07 - Oz - Baum - The Patchwork Girl of Oz (1913).epub"
|
||||||
|
"e"
|
||||||
|
"5:group" "0:"
|
||||||
|
"10:idle-limit"
|
||||||
|
"d"
|
||||||
|
"10:idle-limit" "i30e"
|
||||||
|
"9:idle-mode" "i0e"
|
||||||
|
"e"
|
||||||
|
"6:labels"
|
||||||
|
"l"
|
||||||
|
"e"
|
||||||
|
"9:max-peers" "i20e"
|
||||||
|
"4:name" "45:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7"
|
||||||
|
"6:paused" "i0e"
|
||||||
|
"6:peers2"
|
||||||
|
"l"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x80\x3b\xac\x8f\x3b\x1c"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xe2\xa1\xe3\x25\x2c\xfa"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xf2\x50\x82\xab\xed\x08"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xeb\xb2\x8c\xa1\x1e\xc6"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xe8\x92\x9e\x87\xd1\xb4"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x0a\xca\x51\xdd\x61\x52"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x3d\xf0\x9c\x23\x55\x20"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x4d\x9f\x2e\xd9\x40\x9e"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i12e"
|
||||||
|
"14:socket_address" "6:\x83\xa6\xd7\x7f\xa3\x4c"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xcd\x4a\xdf\x95\xc8\xaa"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x78\x5b\x6c\x9b\xa8\x38"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x60\xc6\xe0\x11\xc5\x76"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xd4\xfa\x37\x77\x0f\xe4"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xfb\x28\x6c\x4d\xc3\x02"
|
||||||
|
"e"
|
||||||
|
"e"
|
||||||
|
"8:priority"
|
||||||
|
"l"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"e"
|
||||||
|
"8:progress"
|
||||||
|
"d"
|
||||||
|
"6:blocks" "3:all"
|
||||||
|
"6:mtimes"
|
||||||
|
"l"
|
||||||
|
"i1756689844e"
|
||||||
|
"i1756689800e"
|
||||||
|
"i1756689836e"
|
||||||
|
"i1756689812e"
|
||||||
|
"i1756689839e"
|
||||||
|
"i1756689844e"
|
||||||
|
"i1756689804e"
|
||||||
|
"e"
|
||||||
|
"6:pieces" "3:all"
|
||||||
|
"e"
|
||||||
|
"11:ratio-limit"
|
||||||
|
"d"
|
||||||
|
"11:ratio-limit" "8:2.000000"
|
||||||
|
"10:ratio-mode" "i0e"
|
||||||
|
"e"
|
||||||
|
"20:seeding-time-seconds" "i7373039e"
|
||||||
|
"19:sequential_download" "i0e"
|
||||||
|
"30:sequential_download_from_piece" "i0e"
|
||||||
|
"16:speed-limit-down"
|
||||||
|
"d"
|
||||||
|
"9:speed-Bps" "i2000000e"
|
||||||
|
"22:use-global-speed-limit" "i1e"
|
||||||
|
"15:use-speed-limit" "i0e"
|
||||||
|
"e"
|
||||||
|
"14:speed-limit-up"
|
||||||
|
"d"
|
||||||
|
"9:speed-Bps" "i5000000e"
|
||||||
|
"22:use-global-speed-limit" "i1e"
|
||||||
|
"15:use-speed-limit" "i0e"
|
||||||
|
"e"
|
||||||
|
"8:uploaded" "i98667375637e"
|
||||||
|
"e";
|
||||||
|
|
||||||
|
constexpr std::string_view ResumeBenc =
|
||||||
|
"d"
|
||||||
|
"13:activity_date" "i1765724117e"
|
||||||
|
"10:added_date" "i1756689559e"
|
||||||
|
"18:bandwidth_priority" "i0e"
|
||||||
|
"7:corrupt" "i0e"
|
||||||
|
"11:destination" "30:/data/trackers/untracked/Books"
|
||||||
|
"3:dnd"
|
||||||
|
"l"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"e"
|
||||||
|
"9:done_date" "i1756689845e"
|
||||||
|
"10:downloaded" "i4830420542e"
|
||||||
|
"24:downloading_time_seconds" "i286e"
|
||||||
|
"5:files"
|
||||||
|
"l"
|
||||||
|
"102:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v01 - Oz - Baum - The Wonderful Wizard of Oz (1990).epub"
|
||||||
|
"100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v02 - Oz - Baum - The Marvelous Land of Oz (1904).epub"
|
||||||
|
"86:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v03 - Oz - Baum - Ozma of Oz (1907).epub"
|
||||||
|
"104:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v04 - Oz - Baum - Dorothy and the Wizard in Oz (1908).epub"
|
||||||
|
"90:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v05 - Oz - Baum - The Road to Oz (1909).epub"
|
||||||
|
"98:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v06 - Oz - Baum - The Emerald City of Oz (1910).epub"
|
||||||
|
"100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v07 - Oz - Baum - The Patchwork Girl of Oz (1913).epub"
|
||||||
|
"e"
|
||||||
|
"5:group" "0:"
|
||||||
|
"10:idle_limit"
|
||||||
|
"d"
|
||||||
|
"10:idle_limit" "i30e"
|
||||||
|
"9:idle_mode" "i0e"
|
||||||
|
"e"
|
||||||
|
"6:labels"
|
||||||
|
"l"
|
||||||
|
"e"
|
||||||
|
"9:max_peers" "i20e"
|
||||||
|
"4:name" "45:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7"
|
||||||
|
"6:paused" "i0e"
|
||||||
|
"6:peers2"
|
||||||
|
"l"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x80\x3b\xac\x8f\x3b\x1c"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xe2\xa1\xe3\x25\x2c\xfa"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xf2\x50\x82\xab\xed\x08"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xeb\xb2\x8c\xa1\x1e\xc6"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xe8\x92\x9e\x87\xd1\xb4"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x0a\xca\x51\xdd\x61\x52"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x3d\xf0\x9c\x23\x55\x20"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x4d\x9f\x2e\xd9\x40\x9e"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i12e"
|
||||||
|
"14:socket_address" "6:\x83\xa6\xd7\x7f\xa3\x4c"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xcd\x4a\xdf\x95\xc8\xaa"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x78\x5b\x6c\x9b\xa8\x38"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\x60\xc6\xe0\x11\xc5\x76"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xd4\xfa\x37\x77\x0f\xe4"
|
||||||
|
"e"
|
||||||
|
"d"
|
||||||
|
"5:flags" "i0e"
|
||||||
|
"14:socket_address" "6:\xfb\x28\x6c\x4d\xc3\x02"
|
||||||
|
"e"
|
||||||
|
"e"
|
||||||
|
"8:priority"
|
||||||
|
"l"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"i0e"
|
||||||
|
"e"
|
||||||
|
"8:progress"
|
||||||
|
"d"
|
||||||
|
"6:blocks" "3:all"
|
||||||
|
"6:mtimes"
|
||||||
|
"l"
|
||||||
|
"i1756689844e"
|
||||||
|
"i1756689800e"
|
||||||
|
"i1756689836e"
|
||||||
|
"i1756689812e"
|
||||||
|
"i1756689839e"
|
||||||
|
"i1756689844e"
|
||||||
|
"i1756689804e"
|
||||||
|
"e"
|
||||||
|
"6:pieces" "3:all"
|
||||||
|
"e"
|
||||||
|
"11:ratio_limit"
|
||||||
|
"d"
|
||||||
|
"11:ratio_limit" "8:2.000000"
|
||||||
|
"10:ratio_mode" "i0e"
|
||||||
|
"e"
|
||||||
|
"20:seeding_time_seconds" "i7373039e"
|
||||||
|
"19:sequential_download" "i0e"
|
||||||
|
"30:sequential_download_from_piece" "i0e"
|
||||||
|
"16:speed_limit_down"
|
||||||
|
"d"
|
||||||
|
"9:speed_Bps" "i2000000e"
|
||||||
|
"22:use_global_speed_limit" "i1e"
|
||||||
|
"15:use_speed_limit" "i0e"
|
||||||
|
"e"
|
||||||
|
"14:speed_limit_up"
|
||||||
|
"d"
|
||||||
|
"9:speed_Bps" "i5000000e"
|
||||||
|
"22:use_global_speed_limit" "i1e"
|
||||||
|
"15:use_speed_limit" "i0e"
|
||||||
|
"e"
|
||||||
|
"8:uploaded" "i98667375637e"
|
||||||
|
"e";
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(ApiCompatTest, canConvertRpc)
|
TEST(ApiCompatTest, canConvertRpc)
|
||||||
@@ -708,12 +1008,11 @@ TEST(ApiCompatTest, canConvertRpc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ApiCompatTest, canConvertDataFiles)
|
TEST(ApiCompatTest, canConvertJsonDataFiles)
|
||||||
{
|
{
|
||||||
using Style = libtransmission::api_compat::Style;
|
using Style = libtransmission::api_compat::Style;
|
||||||
using TestCase = std::tuple<std::string_view, std::string_view, Style, std::string_view>;
|
using TestCase = std::tuple<std::string_view, std::string_view, Style, std::string_view>;
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static auto constexpr TestCases = std::array<TestCase, 8U>{ {
|
static auto constexpr TestCases = std::array<TestCase, 8U>{ {
|
||||||
{ "settings tr5 -> tr5", CurrentSettingsJson, Style::Tr5, CurrentSettingsJson },
|
{ "settings tr5 -> tr5", CurrentSettingsJson, Style::Tr5, CurrentSettingsJson },
|
||||||
{ "settings tr5 -> tr4", CurrentSettingsJson, Style::Tr4, LegacySettingsJson },
|
{ "settings tr5 -> tr4", CurrentSettingsJson, Style::Tr4, LegacySettingsJson },
|
||||||
@@ -725,14 +1024,39 @@ TEST(ApiCompatTest, canConvertDataFiles)
|
|||||||
{ "stats tr4 -> tr5", LegacyStatsJson, Style::Tr5, CurrentStatsJson },
|
{ "stats tr4 -> tr5", LegacyStatsJson, Style::Tr5, CurrentStatsJson },
|
||||||
{ "stats tr4 -> tr4", LegacyStatsJson, Style::Tr4, LegacyStatsJson },
|
{ "stats tr4 -> tr4", LegacyStatsJson, Style::Tr4, LegacyStatsJson },
|
||||||
} };
|
} };
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
for (auto const& [name, src, tgt_style, expected] : TestCases)
|
for (auto const& [name, src, tgt_style, expected] : TestCases)
|
||||||
{
|
{
|
||||||
auto serde = tr_variant_serde::json();
|
auto serde = tr_variant_serde::json();
|
||||||
|
serde.inplace();
|
||||||
|
|
||||||
auto parsed = serde.parse(src);
|
auto parsed = serde.parse(src);
|
||||||
ASSERT_TRUE(parsed.has_value());
|
ASSERT_TRUE(parsed.has_value());
|
||||||
auto converted = libtransmission::api_compat::convert(*parsed, tgt_style);
|
auto converted = libtransmission::api_compat::convert(*parsed, tgt_style);
|
||||||
EXPECT_EQ(expected, serde.to_string(converted)) << name;
|
EXPECT_EQ(expected, serde.to_string(converted)) << name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ApiCompatTest, canConvertBencDataFiles)
|
||||||
|
{
|
||||||
|
using Style = libtransmission::api_compat::Style;
|
||||||
|
using TestCase = std::tuple<std::string_view, std::string_view, Style, std::string_view>;
|
||||||
|
|
||||||
|
static auto constexpr TestCases = std::array<TestCase, 4U>{ {
|
||||||
|
{ "resume tr5 -> tr5", ResumeBenc, Style::Tr5, ResumeBenc },
|
||||||
|
{ "resume tr5 -> tr4", ResumeBenc, Style::Tr4, LegacyResumeBenc },
|
||||||
|
{ "resume tr4 -> tr5", LegacyResumeBenc, Style::Tr5, ResumeBenc },
|
||||||
|
{ "resume tr4 -> tr4", LegacyResumeBenc, Style::Tr4, LegacyResumeBenc },
|
||||||
|
} };
|
||||||
|
|
||||||
|
for (auto const& [name, src, tgt_style, expected] : TestCases)
|
||||||
|
{
|
||||||
|
auto serde = tr_variant_serde::benc();
|
||||||
|
serde.inplace();
|
||||||
|
|
||||||
|
auto parsed = serde.parse(src);
|
||||||
|
ASSERT_TRUE(parsed.has_value()) << name;
|
||||||
|
auto converted = libtransmission::api_compat::convert(*parsed, tgt_style);
|
||||||
|
EXPECT_EQ(expected, serde.to_string(converted)) << name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user