summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Segundo2023-06-12 01:37:45 +0200
committerChristian Segundo2023-06-12 01:37:45 +0200
commit17f3a7b655938eb47efc2bbe884e68786bce6077 (patch)
treec180bca82e03ebd8aa4beb9c9b1fbf4c21a6c264
parent6eaf6a8f3f3880372bdfa0027805213d109a80cc (diff)
downloadzmission-17f3a7b655938eb47efc2bbe884e68786bce6077.tar.gz
add torrent get raw
-rw-r--r--src/main.zig22
-rw-r--r--src/request.zig161
-rw-r--r--src/transmission.zig44
-rw-r--r--src/util.zig15
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;
+}