summaryrefslogtreecommitdiff
path: root/src/request.zig
diff options
context:
space:
mode:
authorChristian Segundo2023-06-11 22:09:15 +0200
committerChristian Segundo2023-06-11 22:09:15 +0200
commit3bd3f432a95da405634cdbd2a662d79a3a5ba7af (patch)
treeed7ac24e3309d8c6b4f43d1051d95b18a7165a2c /src/request.zig
downloadzmission-3bd3f432a95da405634cdbd2a662d79a3a5ba7af.tar.gz
wip
Diffstat (limited to 'src/request.zig')
-rw-r--r--src/request.zig363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/request.zig b/src/request.zig
new file mode 100644
index 0000000..950a1fc
--- /dev/null
+++ b/src/request.zig
@@ -0,0 +1,363 @@
+const std = @import("std");
+const util = @import("util.zig");
+
+const Method = enum {
+ // Torrent requests: Torrent action
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#31-torrent-action-requests
+ torrent_start,
+ torrent_start_now,
+ torrent_stop,
+ torrent_verify,
+ torrent_reannounce,
+
+ // Torrent requests: Mutator
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#32-torrent-mutator-torrent-set
+ torrent_set,
+
+ // Torrent requests: Torrent accessor
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#33-torrent-accessor-torrent-get
+ torrent_get,
+
+ // Torrent requests: Torrent adding
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#34-adding-a-torrent
+ torrent_add,
+
+ // Torrent requests: Torrent removing
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#35-removing-a-torrent
+ torrent_remove,
+
+ // Torrent requests: Torrent moving
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#36-moving-a-torrent
+ torrent_set_location,
+
+ // Torrent requests: Torrent moving
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#37-renaming-a-torrents-path
+ torrent_rename_path,
+
+ // Session requests: Get
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#4--session-requests
+ session_get,
+
+ // Session requests: Mutator
+ // https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#411-mutators
+ session_set,
+ //session_stats,
+
+ const Self = @This();
+
+ // Transmission RPC uses hyphens instead of underscores, this creates a
+ // table of method names using the same enum field names but with
+ // underscores.
+ pub const Fields = util.RPCFields(Self);
+
+ pub fn str(self: Self) []const u8 {
+ return Fields[@enumToInt(self)];
+ }
+
+ pub fn jsonStringify(self: Self, options: std.json.StringifyOptions, out_stream: anytype) !void {
+ try std.json.stringify(self.str(), options, out_stream);
+ }
+};
+
+pub const SessionSetFields = struct {
+ alt_speed_down: ?usize = null,
+ alt_speed_enabled: ?bool = null,
+ alt_speed_time_begin: ?usize = null,
+ alt_speed_time_day: ?usize = null,
+ alt_speed_time_enabled: ?bool = null,
+ alt_speed_time_end: ?usize = null,
+ alt_speed_up: ?usize = null,
+ blocklist_enabled: ?bool = null,
+ blocklist_url: ?[]u8 = null,
+ cache_size_mb: ?usize = null,
+ default_trackers: ?[]u8 = null,
+ dht_enabled: ?bool = null,
+ download_dir: ?[]u8 = null,
+ download_dir_free_space: ?usize = null,
+ download_queue_enabled: ?bool = null,
+ download_queue_size: ?usize = null,
+ encryption: ?enum {
+ required,
+ preferred,
+ tolerated,
+
+ const Self = @This();
+
+ pub const Fields = util.RPCFields(Self);
+
+ pub fn str(self: Self) []const u8 {
+ return Fields[@enumToInt(self)];
+ }
+
+ pub fn jsonStringify(self: Self, options: std.json.StringifyOptions, out_stream: anytype) !void {
+ try std.json.stringify(self.str(), options, out_stream);
+ }
+ } = null,
+ idle_seeding_limit_enabled: ?bool = null,
+ idle_seeding_limit: ?usize = null,
+ incomplete_dir_enabled: ?bool = null,
+ incomplete_dir: ?[]u8 = null,
+ lpd_enabled: ?bool = null,
+ peer_limit_global: ?usize = null,
+ peer_limit_per_torrent: ?usize = null,
+ peer_port_random_on_start: ?bool = null,
+ peer_port: ?usize = null,
+ pex_enabled: ?bool = null,
+ port_forwarding_enabled: ?bool = null,
+ queue_stalled_enabled: ?bool = null,
+ queue_stalled_minutes: ?usize = null,
+ rename_partial_files: ?bool = null,
+ script_torrent_added_enabled: ?bool = null,
+ script_torrent_added_filename: ?[]u8 = null,
+ script_torrent_done_enabled: ?bool = null,
+ script_torrent_done_filename: ?[]u8 = null,
+ script_torrent_done_seeding_enabled: ?bool = null,
+ script_torrent_done_seeding_filename: ?[]u8 = null,
+ seed_queue_enabled: ?bool = null,
+ seed_queue_size: ?usize = null,
+ seedRatioLimit: ?f64 = null,
+ seedRatioLimited: ?bool = null,
+ speed_limit_down_enabled: ?bool = null,
+ speed_limit_down: ?usize = null,
+ speed_limit_up_enabled: ?bool = null,
+ speed_limit_up: ?usize = null,
+ start_added_torrents: ?bool = null,
+ trash_original_torrent_files: ?bool = null,
+ utp_enabled: ?bool = null,
+};
+
+pub const SessionGetFields = enum {
+ alt_speed_down,
+ alt_speed_enabled,
+ alt_speed_time_begin,
+ alt_speed_time_day,
+ alt_speed_time_enabled,
+ alt_speed_time_end,
+ alt_speed_up,
+ blocklist_enabled,
+ blocklist_size,
+ blocklist_url,
+ cache_size_mb,
+ config_dir,
+ default_trackers,
+ dht_enabled,
+ download_dir,
+ download_dir_free_space,
+ download_queue_enabled,
+ download_queue_size,
+ encryption,
+ idle_seeding_limit_enabled,
+ idle_seeding_limit,
+ incomplete_dir_enabled,
+ incomplete_dir,
+ lpd_enabled,
+ peer_limit_global,
+ peer_limit_per_torrent,
+ peer_port_random_on_start,
+ peer_port,
+ pex_enabled,
+ port_forwarding_enabled,
+ queue_stalled_enabled,
+ queue_stalled_minutes,
+ rename_partial_files,
+ rpc_version_minimum,
+ rpc_version_semver,
+ rpc_version,
+ script_torrent_added_enabled,
+ script_torrent_added_filename,
+ script_torrent_done_enabled,
+ script_torrent_done_filename,
+ script_torrent_done_seeding_enabled,
+ script_torrent_done_seeding_filename,
+ seed_queue_enabled,
+ seed_queue_size,
+ seedRatioLimit,
+ seedRatioLimited,
+ speed_limit_down_enabled,
+ speed_limit_down,
+ speed_limit_up_enabled,
+ speed_limit_up,
+ start_added_torrents,
+ trash_original_torrent_files,
+ units,
+ utp_enabled,
+ version,
+
+ const Self = @This();
+
+ pub const Fields = util.RPCFields(Self);
+
+ pub fn str(self: Self) []const u8 {
+ return Fields[@enumToInt(self)];
+ }
+
+ // We have to add our own custom stringification because the default
+ // one will nest the enum inside the method name.
+ pub fn jsonStringify(self: Self, options: std.json.StringifyOptions, out_stream: anytype) !void {
+ try std.json.stringify(self.str(), options, out_stream);
+ }
+};
+
+pub const TorrentIDs = union(enum) {
+ single: usize,
+ many: []const usize,
+ recently_active,
+
+ const Self = @This();
+
+ pub fn jsonStringify(self: Self, options: std.json.StringifyOptions, out_stream: anytype) !void {
+ switch (self) {
+ .single => |v| try std.json.stringify(v, options, out_stream),
+ .many => |v| try std.json.stringify(v, options, out_stream),
+ .recently_active => try std.json.stringify("recently-active", options, out_stream),
+ }
+ }
+};
+
+pub const TorrentActionFields = struct {
+ ids: TorrentIDs,
+};
+
+pub const Request = struct {
+ method: Method,
+ arguments: union(Method) {
+ torrent_start: TorrentActionFields,
+ torrent_start_now: TorrentActionFields,
+ torrent_stop: TorrentActionFields,
+ torrent_verify: TorrentActionFields,
+ torrent_reannounce: TorrentActionFields,
+
+ torrent_set: u8,
+ torrent_get: u8,
+ torrent_add: u8,
+ torrent_remove: u8,
+ torrent_set_location: u8,
+ torrent_rename_path: u8,
+
+ session_get: struct {
+ fields: []const SessionGetFields,
+ },
+
+ session_set: SessionSetFields,
+
+ const Self = @This();
+
+ pub fn jsonStringify(self: Self, _: std.json.StringifyOptions, out_stream: anytype) !void {
+ const options = std.json.StringifyOptions{
+ .emit_null_optional_fields = false,
+ };
+
+ switch (self) {
+ .torrent_start,
+ .torrent_start_now,
+ .torrent_stop,
+ .torrent_verify,
+ .torrent_reannounce,
+ => |v| try std.json.stringify(v, options, out_stream),
+
+ .session_set => |v| try std.json.stringify(v, options, out_stream),
+
+ .session_get => |v| try std.json.stringify(v, options, out_stream),
+ else => unreachable,
+ }
+ }
+ },
+};
+
+test "json request encoding" {
+ const test_case = struct {
+ name: []const u8,
+ expected: []const u8,
+ request: Request,
+
+ fn run(self: @This()) !void {
+ var req = std.ArrayList(u8).init(std.testing.allocator);
+ defer req.deinit();
+ try std.json.stringify(self.request, .{}, req.writer());
+ try std.testing.expect(std.mem.eql(u8, req.items, self.expected));
+ }
+ };
+
+ const test_cases = [_]test_case{
+ .{
+ .name = "session-get",
+ .request = .{
+ .method = .session_get,
+ .arguments = .{
+ .session_get = .{
+ .fields = &[_]SessionGetFields{
+ .version,
+ .utp_enabled,
+ },
+ },
+ },
+ },
+ .expected =
+ \\{"method":"session-get","arguments":{"fields":["version","utp-enabled"]}}
+ ,
+ },
+
+ .{
+ .name = "session-set",
+ .request = .{
+ .method = .session_set,
+ .arguments = .{
+ .session_set = .{
+ .lpd_enabled = true,
+ .encryption = .required,
+ },
+ },
+ },
+ .expected =
+ \\{"method":"session-set","arguments":{"encryption":"required","lpd_enabled":true}}
+ ,
+ },
+
+ .{
+ .name = "torrent-reannounce single id",
+ .request = .{
+ .method = .torrent_reannounce,
+ .arguments = .{
+ .torrent_reannounce = .{
+ .ids = .{ .single = 1 },
+ },
+ },
+ },
+ .expected =
+ \\{"method":"torrent-reannounce","arguments":{"ids":1}}
+ ,
+ },
+
+ .{
+ .name = "torrent-reannounce multiple id",
+ .request = .{
+ .method = .torrent_reannounce,
+ .arguments = .{
+ .torrent_reannounce = .{
+ .ids = .{ .many = &[_]usize{ 1, 2 } },
+ },
+ },
+ },
+ .expected =
+ \\{"method":"torrent-reannounce","arguments":{"ids":[1,2]}}
+ ,
+ },
+
+ .{
+ .name = "torrent-reannounce recently-active",
+ .request = .{
+ .method = .torrent_reannounce,
+ .arguments = .{
+ .torrent_reannounce = .{
+ .ids = .recently_active,
+ },
+ },
+ },
+ .expected =
+ \\{"method":"torrent-reannounce","arguments":{"ids":"recently-active"}}
+ ,
+ },
+ };
+
+ for (test_cases) |tc| try tc.run();
+}