diff options
author | Christian Segundo | 2023-06-12 01:37:45 +0200 |
---|---|---|
committer | Christian Segundo | 2023-06-12 01:37:45 +0200 |
commit | 17f3a7b655938eb47efc2bbe884e68786bce6077 (patch) | |
tree | c180bca82e03ebd8aa4beb9c9b1fbf4c21a6c264 | |
parent | 6eaf6a8f3f3880372bdfa0027805213d109a80cc (diff) | |
download | zmission-17f3a7b655938eb47efc2bbe884e68786bce6077.tar.gz |
add torrent get raw
-rw-r--r-- | src/main.zig | 22 | ||||
-rw-r--r-- | src/request.zig | 161 | ||||
-rw-r--r-- | src/transmission.zig | 44 | ||||
-rw-r--r-- | src/util.zig | 15 |
4 files changed, 202 insertions, 40 deletions
diff --git a/src/main.zig b/src/main.zig index 27f4ed2..074be58 100644 --- a/src/main.zig +++ b/src/main.zig @@ -14,13 +14,23 @@ export fn add(a: i32, b: i32) i32 { var client = transmission.Client.init(allocator, clientOptions); defer client.deinit(); - const body = transmission.session_get_raw(&client, null) catch |err| { - std.debug.print("error: {any}\n", .{err}); - unreachable; - }; - defer allocator.free(body); + { + const body = transmission.session_get_raw(&client, null) catch |err| { + std.debug.print("error: {any}\n", .{err}); + unreachable; + }; + defer allocator.free(body); + std.debug.print("body: {s}\n", .{body}); + } - std.debug.print("body: {s}\n", .{body}); + { + const body = transmission.torrent_get_raw(&client, null) catch |err| { + std.debug.print("error: {any}\n", .{err}); + unreachable; + }; + defer allocator.free(body); + std.debug.print("body: {s}\n", .{body}); + } return a + b; } diff --git a/src/request.zig b/src/request.zig index 950a1fc..8641776 100644 --- a/src/request.zig +++ b/src/request.zig @@ -48,7 +48,7 @@ const Method = enum { // 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 const Fields = util.enumFieldsToStringSlice(Self); pub fn str(self: Self) []const u8 { return Fields[@enumToInt(self)]; @@ -83,7 +83,7 @@ pub const SessionSetFields = struct { const Self = @This(); - pub const Fields = util.RPCFields(Self); + pub const Fields = util.enumFieldsToStringSlice(Self); pub fn str(self: Self) []const u8 { return Fields[@enumToInt(self)]; @@ -126,6 +126,100 @@ pub const SessionSetFields = struct { utp_enabled: ?bool = null, }; +pub const TorrentGetFields = enum { + activityDate, + addedDate, + availability, + bandwidthPriority, + comment, + corruptEver, + creator, + dateCreated, + desiredAvailable, + doneDate, + downloadDir, + downloadedEver, + downloadLimit, + downloadLimited, + editDate, + @"error", + errorString, + eta, + etaIdle, + file_count, + files, + fileStats, + group, + hashString, + haveUnchecked, + haveValid, + honorsSessionLimits, + id, + isFinished, + isPrivate, + isStalled, + labels, + leftUntilDone, + magnetLink, + manualAnnounceTime, + maxConnectedPeers, + metadataPercentComplete, + name, + peer_limit, + peers, + peersConnected, + peersFrom, + peersGettingFromUs, + peersSendingToUs, + percentComplete, + percentDone, + pieces, + pieceCount, + pieceSize, + priorities, + primary_mime_type, + queuePosition, + rateDownload, + rateUpload, + recheckProgress, + secondsDownloading, + secondsSeeding, + seedIdleLimit, + seedIdleMode, + seedRatioLimit, + seedRatioMode, + sequentialDownload, + sizeWhenDone, + startDate, + status, + trackers, + trackerList, + trackerStats, + totalSize, + torrentFile, + uploadedEver, + uploadLimit, + uploadLimited, + uploadRatio, + wanted, + webseeds, + webseedsSendingToUs, + + const Self = @This(); + + pub const Fields = util.enumFieldsToStringSlice(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 SessionGetFields = enum { alt_speed_down, alt_speed_enabled, @@ -185,7 +279,7 @@ pub const SessionGetFields = enum { const Self = @This(); - pub const Fields = util.RPCFields(Self); + pub const Fields = util.enumFieldsToStringSlice(Self); pub fn str(self: Self) []const u8 { return Fields[@enumToInt(self)]; @@ -218,27 +312,41 @@ pub const TorrentActionFields = struct { ids: TorrentIDs, }; +pub const TorrentStart = TorrentActionFields; +pub const TorrentStartNow = TorrentActionFields; +pub const TorrentStop = TorrentActionFields; +pub const TorrentVerify = TorrentActionFields; +pub const TorrentReannounce = TorrentActionFields; + +pub const TorrentGet = struct { + ids: ?TorrentIDs = null, + fields: []const TorrentGetFields, +}; + +pub const SessionGet = struct { + fields: []const SessionGetFields, +}; + +pub const SessionSet = SessionSetFields; + 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_start: TorrentStart, + torrent_start_now: TorrentStartNow, + torrent_stop: TorrentStop, + torrent_verify: TorrentVerify, + torrent_reannounce: TorrentReannounce, torrent_set: u8, - torrent_get: u8, + torrent_get: TorrentGet, torrent_add: u8, torrent_remove: u8, torrent_set_location: u8, torrent_rename_path: u8, - session_get: struct { - fields: []const SessionGetFields, - }, - - session_set: SessionSetFields, + session_get: SessionGet, + session_set: SessionSet, const Self = @This(); @@ -255,6 +363,8 @@ pub const Request = struct { .torrent_reannounce, => |v| try std.json.stringify(v, options, out_stream), + .torrent_get => |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), @@ -274,7 +384,9 @@ test "json request encoding" { 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)); + std.testing.expect(std.mem.eql(u8, req.items, self.expected)) catch { + std.debug.panic("{s}\n", .{req.items}); + }; } }; @@ -357,6 +469,25 @@ test "json request encoding" { \\{"method":"torrent-reannounce","arguments":{"ids":"recently-active"}} , }, + + .{ + .name = "torrent-get", + .request = .{ + .method = .torrent_get, + .arguments = .{ + .torrent_get = .{ + .fields = &[_]TorrentGetFields{ + .id, + .name, + }, + .ids = .recently_active, + }, + }, + }, + .expected = + \\{"method":"torrent-get","arguments":{"ids":"recently-active","fields":["id","name"]}} + , + }, }; for (test_cases) |tc| try tc.run(); diff --git a/src/transmission.zig b/src/transmission.zig index 93fe7f4..e25d30b 100644 --- a/src/transmission.zig +++ b/src/transmission.zig @@ -1,7 +1,11 @@ const std = @import("std"); +const util = @import("util.zig"); const Request = @import("request.zig").Request; const SessionGetFields = @import("request.zig").SessionGetFields; +const SessionGet = @import("request.zig").SessionGet; +const TorrentGet = @import("request.zig").TorrentGet; +const TorrentGetFields = @import("request.zig").TorrentGetFields; pub const ClientOptions = extern struct { host: [*:0]const u8, @@ -87,29 +91,37 @@ pub const Client = struct { return self.do(req); } - const body = try real_req.reader().readAllAlloc(self.allocator, 9000); - return body; + var body = std.ArrayList(u8).init(self.allocator); + + // TODO making max_append_size this large + // all the time doesn't feel right + try real_req.reader().readAllArrayList(&body, 9000000000); + + return body.toOwnedSlice(); } }; -pub fn session_get_raw(client: *Client, fields: ?[]SessionGetFields) ![]u8 { - const all_fields = comptime blk: { - var all_fields: []const SessionGetFields = &[_]SessionGetFields{}; - inline for (@typeInfo(SessionGetFields).Enum.fields) |enumField| { - all_fields = all_fields ++ &[_]SessionGetFields{ - @field(SessionGetFields, enumField.name), - }; - } - break :blk all_fields; +pub fn session_get_raw(client: *Client, session_get: ?SessionGet) ![]u8 { + const default: SessionGet = .{ + .fields = comptime util.enumFieldsToSlice(SessionGetFields), }; const r = Request{ .method = .session_get, - .arguments = .{ - .session_get = .{ - .fields = fields orelse all_fields, - }, - }, + .arguments = .{ .session_get = session_get orelse default }, + }; + const body = try client.do(r); + return body; +} + +pub fn torrent_get_raw(client: *Client, torrent_get: ?TorrentGet) ![]u8 { + const default: TorrentGet = .{ + .fields = comptime util.enumFieldsToSlice(TorrentGetFields), + }; + + const r = Request{ + .method = .torrent_get, + .arguments = .{ .torrent_get = torrent_get orelse default }, }; const body = try client.do(r); return body; diff --git a/src/util.zig b/src/util.zig index b701ff6..41c3584 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,8 +1,7 @@ const std = @import("std"); -// Returns an array of all the fields in the given -// type replacing all '_' with '-'. -pub fn RPCFields(comptime E: type) []const []const u8 { +/// Returns an slice of all the fields in the given type replacing all '_' with '-'. +pub fn enumFieldsToStringSlice(comptime E: type) []const []const u8 { @setEvalBranchQuota(10000); var names: []const []const u8 = &[_][]const u8{}; for (std.meta.fields(E)) |field| { @@ -12,3 +11,13 @@ pub fn RPCFields(comptime E: type) []const []const u8 { } return names; } + +pub fn enumFieldsToSlice(comptime T: type) []const T { + var fields: []const T = &[_]T{}; + inline for (@typeInfo(T).Enum.fields) |enumField| { + fields = fields ++ &[_]T{ + @field(T, enumField.name), + }; + } + return fields; +} |