summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Segundo2023-06-17 02:54:34 +0200
committerChristian Segundo2023-06-17 02:54:34 +0200
commitc7412022c3c607c809f774db2459f9259ba95038 (patch)
tree72b7c416c02f5aa189388bcf7851050ce75f3325
parent831740fcacfe67b8124436460afa77af15493f93 (diff)
downloadzmission-c7412022c3c607c809f774db2459f9259ba95038.tar.gz
one step closer
-rw-r--r--src/main.zig61
-rw-r--r--src/request.zig14
-rw-r--r--src/response.zig47
-rw-r--r--src/transmission.zig68
-rw-r--r--src/types.zig189
5 files changed, 324 insertions, 55 deletions
diff --git a/src/main.zig b/src/main.zig
index a52079a..b01188b 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -14,32 +14,43 @@ 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);
- std.debug.print("body: {s}\n", .{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});
+ //}
{
- const body = transmission.torrent_get_raw(&client, null) catch |err| {
+ const body = transmission.torrent_get_(&client, null) catch |err| {
std.debug.print("error: {any}\n", .{err});
unreachable;
};
- defer allocator.free(body);
- std.debug.print("body: {s}\n", .{body});
+ //defer allocator.free(body);
+ for (body.arguments.torrent_get.torrents.?) |t| {
+ std.debug.print("name: {any}\n", .{t});
+ }
}
- {
- const body = transmission.session_set_raw(&client, .{ .peer_port = 51413 }) catch |err| {
- std.debug.print("error: {any}\n", .{err});
- unreachable;
- };
- defer allocator.free(body);
- std.debug.print("body: {s}\n", .{body});
- }
+ //{
+ //const body = transmission.torrent_get_(&client, null) catch |err| {
+ //std.debug.print("error: {any}\n", .{err});
+ //unreachable;
+ //};
+ ////defer allocator.free(body);
+ //std.debug.print("body: {any}\n", .{body});
+ //}
+
+ //{
+ //const body = transmission.session_set_raw(&client, .{ .peer_port = 51413 }) catch |err| {
+ //std.debug.print("error: {any}\n", .{err});
+ //unreachable;
+ //};
+ //defer allocator.free(body);
+ //std.debug.print("body: {s}\n", .{body});
+ //}
return a + b;
}
@@ -76,7 +87,7 @@ export fn c_session_get(client: ?*anyopaque, buf: [*]u8) c_int {
return 0;
}
-export fn c_torrent_get(client: ?*anyopaque) c_int {
+export fn c_torrent_get(client: ?*anyopaque) [*:0]u8 {
var real_client: *transmission.Client = @ptrCast(*transmission.Client, @alignCast(
@alignOf(transmission.Client),
client.?,
@@ -88,8 +99,12 @@ export fn c_torrent_get(client: ?*anyopaque) c_int {
};
defer allocator.free(body);
- std.debug.print("body: {s}\n", .{body});
- return 0;
+ // TODO: use the same pointer of body but gotta go fast :(
+ var foo = std.ArrayList(u8).init(allocator);
+ foo.insertSlice(0, body) catch unreachable;
+
+ //std.debug.print("body: {s}\n", .{body});
+ return foo.toOwnedSliceSentinel(0) catch unreachable;
}
test "c api" {
@@ -100,7 +115,7 @@ test "c api" {
};
var foo = c_client_init(clientOptions);
_ = c_session_get(foo, undefined);
- _ = c_torrent_get(foo, undefined);
+ _ = c_torrent_get(foo);
c_client_deinit(foo);
}
diff --git a/src/request.zig b/src/request.zig
index 478999b..214f997 100644
--- a/src/request.zig
+++ b/src/request.zig
@@ -1,7 +1,9 @@
const std = @import("std");
const util = @import("util.zig");
-const Method = enum {
+pub const Request = @This();
+
+pub const Method = enum {
// Torrent requests: Torrent action
// https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#31-torrent-action-requests
torrent_start,
@@ -53,8 +55,8 @@ const Method = enum {
};
pub const TorrentIDs = union(enum) {
- single: usize,
- many: []const usize,
+ single: i32,
+ many: []const i32,
recently_active,
pub fn jsonStringify(self: TorrentIDs, options: std.json.StringifyOptions, out_stream: anytype) !void {
@@ -309,7 +311,7 @@ pub const SessionSet = struct {
}
};
-pub const Request = struct {
+pub const Object = struct {
method: Method,
arguments: union(Method) {
torrent_start: TorrentStart,
@@ -358,7 +360,7 @@ test "json request encoding" {
const test_case = struct {
name: []const u8,
expected: []const u8,
- request: Request,
+ request: Object,
fn run(self: @This()) !void {
var req = std.ArrayList(u8).init(std.testing.allocator);
@@ -429,7 +431,7 @@ test "json request encoding" {
.method = .torrent_reannounce,
.arguments = .{
.torrent_reannounce = .{
- .ids = .{ .many = &[_]usize{ 1, 2 } },
+ .ids = .{ .many = &[_]i32{ 1, 2 } },
},
},
},
diff --git a/src/response.zig b/src/response.zig
new file mode 100644
index 0000000..8964de4
--- /dev/null
+++ b/src/response.zig
@@ -0,0 +1,47 @@
+const std = @import("std");
+const Request = @import("request.zig");
+const Types = @import("types.zig");
+
+pub const Response = @This();
+
+pub const TorrentGet = struct {
+ pub const Arguments = struct {
+ torrents: ?[]Types.Torrent = null,
+ };
+
+ result: []const u8,
+ arguments: Arguments,
+};
+
+pub const Object = struct {
+ pub const Arguments = union(Request.Method) {
+ torrent_start,
+ torrent_start_now,
+ torrent_stop,
+ torrent_verify,
+ torrent_reannounce,
+ torrent_set,
+ torrent_get: TorrentGet.Arguments,
+ torrent_add,
+ torrent_remove,
+ torrent_set_location,
+ torrent_rename_path,
+ session_get,
+ session_set,
+ };
+
+ result: []const u8,
+ arguments: Arguments,
+
+ pub fn init(arguments: Arguments, result: []const u8) Object {
+ switch (arguments) {
+ .torrent_get => {
+ return Object{
+ .result = result,
+ .arguments = arguments,
+ };
+ },
+ else => unreachable,
+ }
+ }
+};
diff --git a/src/transmission.zig b/src/transmission.zig
index dd058bb..409b4b1 100644
--- a/src/transmission.zig
+++ b/src/transmission.zig
@@ -1,10 +1,10 @@
const std = @import("std");
+const http = std.http;
+
const util = @import("util.zig");
-const Request = @import("request.zig").Request;
-const SessionGet = @import("request.zig").SessionGet;
-const SessionSet = @import("request.zig").SessionSet;
-const TorrentGet = @import("request.zig").TorrentGet;
+const Request = @import("request.zig");
+const Response = @import("response.zig");
pub const ClientOptions = extern struct {
host: [*:0]const u8,
@@ -17,8 +17,8 @@ pub const ClientOptions = extern struct {
pub const Client = struct {
uri: std.Uri,
allocator: std.mem.Allocator,
- http_client: std.http.Client,
- http_headers: std.http.Headers,
+ http_client: http.Client,
+ http_headers: http.Headers,
current_session_id: []u8 = "",
pub fn init(allocator: std.mem.Allocator, opts: ClientOptions) Client {
@@ -42,8 +42,8 @@ pub const Client = struct {
return Client{
.uri = base_url,
.allocator = allocator,
- .http_client = std.http.Client{ .allocator = allocator },
- .http_headers = std.http.Headers{ .allocator = allocator },
+ .http_client = http.Client{ .allocator = allocator },
+ .http_headers = http.Headers{ .allocator = allocator },
};
}
@@ -53,18 +53,18 @@ pub const Client = struct {
self.allocator.free(self.current_session_id);
}
- fn setSessionId(self: *Client, r: std.http.Client.Response) !void {
+ fn setSessionId(self: *Client, r: http.Client.Response) !void {
self.allocator.free(self.current_session_id);
const session_id = r.headers.getFirstValue("X-Transmission-Session-Id") orelse return error.NoSessionId;
self.current_session_id = try std.fmt.allocPrint(self.allocator, "{s}", .{session_id});
}
- fn newRequest(self: *Client) !std.http.Client.Request {
+ fn newRequest(self: *Client) !http.Client.Request {
try self.http_headers.append("X-Transmission-Session-Id", self.current_session_id);
return try self.http_client.request(.POST, self.uri, self.http_headers, .{});
}
- pub fn do(self: *Client, req: Request) ![]u8 {
+ pub fn do(self: *Client, req: Request.Object) ![]u8 {
var real_req = try self.newRequest();
defer real_req.deinit();
@@ -73,7 +73,7 @@ pub const Client = struct {
try std.json.stringify(req, .{}, payload.writer());
- real_req.transfer_encoding = std.http.Client.RequestTransfer{
+ real_req.transfer_encoding = http.Client.RequestTransfer{
.content_length = payload.items.len,
};
@@ -82,19 +82,17 @@ pub const Client = struct {
return error.InvalidSize;
}
- std.debug.print("request: {s}\n", .{payload.items});
-
try real_req.finish();
try real_req.wait();
- if (real_req.response.status == std.http.Status.conflict) {
+ if (real_req.response.status == http.Status.conflict) {
try self.setSessionId(real_req.response);
return self.do(req);
}
var body = std.ArrayList(u8).init(self.allocator);
- // TODO making max_append_size this large
+ // TODO: making max_append_size this large
// all the time doesn't feel right
try real_req.reader().readAllArrayList(&body, 9000000000);
@@ -102,12 +100,12 @@ pub const Client = struct {
}
};
-pub fn session_get_raw(client: *Client, session_get: ?SessionGet) ![]u8 {
- const default: SessionGet = .{
- .fields = SessionGet.all_fields,
+pub fn session_get_raw(client: *Client, session_get: ?Request.SessionGet) ![]u8 {
+ const default: Request.SessionGet = .{
+ .fields = Request.SessionGet.all_fields,
};
- const r = Request{
+ const r = Request.Object{
.method = .session_get,
.arguments = .{ .session_get = session_get orelse default },
};
@@ -115,12 +113,15 @@ pub fn session_get_raw(client: *Client, session_get: ?SessionGet) ![]u8 {
return body;
}
-pub fn torrent_get_raw(client: *Client, torrent_get: ?TorrentGet) ![]u8 {
- const default: TorrentGet = .{
- .fields = TorrentGet.all_fields,
+pub fn torrent_get_raw(client: *Client, torrent_get: ?Request.TorrentGet) ![]u8 {
+ const default: Request.TorrentGet = .{
+ .fields = Request.TorrentGet.all_fields,
};
+ //const default: Request.TorrentGet = .{
+ //.fields = &[_]Request.TorrentGet.Fields{.name},
+ //};
- const r = Request{
+ const r = Request.Object{
.method = .torrent_get,
.arguments = .{ .torrent_get = torrent_get orelse default },
};
@@ -128,8 +129,23 @@ pub fn torrent_get_raw(client: *Client, torrent_get: ?TorrentGet) ![]u8 {
return body;
}
-pub fn session_set_raw(client: *Client, session_set: SessionSet) ![]u8 {
- const r = Request{
+pub fn torrent_get_(client: *Client, torrent_get: ?Request.TorrentGet) !Response.Object {
+ @setEvalBranchQuota(100000);
+ const body = try torrent_get_raw(client, torrent_get);
+ const decoded: Response.TorrentGet = try std.json.parseFromSlice(
+ Response.TorrentGet,
+ client.allocator,
+ body,
+ std.json.ParseOptions{
+ .ignore_unknown_fields = true,
+ .duplicate_field_behavior = .@"error",
+ },
+ );
+ return Response.Object.init(.{ .torrent_get = decoded.arguments }, decoded.result);
+}
+
+pub fn session_set_raw(client: *Client, session_set: Request.SessionSet) ![]u8 {
+ const r = Request.Object{
.method = .session_set,
.arguments = .{ .session_set = session_set },
};
diff --git a/src/types.zig b/src/types.zig
new file mode 100644
index 0000000..b8029ca
--- /dev/null
+++ b/src/types.zig
@@ -0,0 +1,189 @@
+// types are from:
+// https://github.com/transmission/transmission/blob/76166d8fa71f351fe46221d737f8849b23f551f7/libtransmission/transmission.h#L1604
+// where:
+// time_t => i64
+// float => f32
+// uint16_t => u16
+pub const Torrent = struct {
+ // The last time we uploaded or downloaded piece data on this torrent.
+ activityDate: ?i64 = null,
+
+ // When the torrent was first added.
+ addedDate: ?i64 = null,
+
+ //availability: []struct {}, array (see below) tr_torrepieceCountntAvailability()
+
+ //bandwidthPriority: ?usize = null,
+ comment: ?[]u8 = null,
+
+ // Byte count of all the corrupt data you've ever downloaded for
+ // this torrent. If you're on a poisoned torrent, this number can
+ // grow very large.
+ corruptEver: ?u64 = null,
+
+ creator: ?[]u8 = null,
+ //dateCreated: ?usize = null,
+
+ // Byte count of all the piece data we want and don't have yet,
+ // but that a connected peer does have.
+ desiredAvailable: ?u64 = null,
+
+ // When the torrent finished downloading.
+ doneDate: ?i64 = null,
+
+ downloadDir: ?[]u8 = null,
+
+ // Byte count of all the non-corrupt data you've ever downloaded
+ // for this torrent. If you deleted the files and downloaded a second
+ // time, this will be `2*totalSize`.
+ downloadedEver: ?u64 = null,
+
+ //downloadLimit: ?usize = null,
+ downloadLimited: ?bool = null,
+
+ // The last time during this session that a rarely-changing field
+ // changed -- e.g. fields like trackers, filenames, name
+ // or download directory. RPC clients can monitor this to know when
+ // to reload fields that rarely change.
+ editDate: ?i64 = null,
+
+ //@"error": ?usize = null,
+ errorString: ?[]u8 = null,
+
+ // If downloading, estimated number of seconds left until the torrent is done.
+ // If seeding, estimated number of seconds left until seed ratio is reached.
+ eta: ?i64 = null,
+
+ // If seeding, number of seconds left until the idle time limit is reached.
+ etaIdle: ?i64 = null,
+
+ //@"file-count": ?usize = null,
+ ////files array (see below) n/a
+ ////fileStats array (see below) n/a
+ group: ?[]u8 = null,
+ hashString: ?[]u8 = null,
+
+ // Byte count of all the partial piece data we have for this torrent.
+ // As pieces become complete, this value may decrease as portions of it
+ // are moved to `corrupt` or `haveValid`.
+ haveUnchecked: ?u64 = null,
+
+ // Byte count of all the checksum-verified data we have for this torrent.
+ haveValid: ?u64 = null,
+
+ honorsSessionLimits: ?bool = null,
+
+ id: ?i32 = null,
+
+ isFinished: ?bool = null,
+ isPrivate: ?bool = null,
+ isStalled: ?bool = null,
+ labels: ?[][]u8 = null,
+
+ // Byte count of how much data is left to be downloaded until we've got
+ // all the pieces that we want.
+ leftUntilDone: ?u64 = null,
+
+ magnetLink: ?[]u8 = null,
+ //manualAnnounceTime: ?usize = null,
+ //maxConnectedPeers: ?usize = null,
+
+ // How much of the metadata the torrent has.
+ // For torrents added from a torrent this will always be 1.
+ // For magnet links, this number will from from 0 to 1 as the metadata is downloaded.
+ // Range is [0..1]
+ metadataPercentComplete: ?f32 = null,
+
+ name: ?[]u8 = null,
+ //@"peer-limit": ?usize = null,
+ ////peers array (see below) n/a
+
+ // Number of peers that we're connected to
+ peersConnected: ?u16 = null,
+
+ ////peersFrom object (see below) n/a
+
+ // Number of peers that we're sending data to.
+ peersGettingFromUs: ?u16 = null,
+
+ // Number of peers that are sending data to us.
+ peersSendingToUs: ?u16 = null,
+
+ // How much has been downloaded of the entire torrent.
+ // Range is [0..1]
+ percentComplete: ?f32 = null,
+
+ // How much has been downloaded of the files the user wants. This differs
+ // from percentComplete if the user wants only some of the torrent's files.
+ // Range is [0..1]
+ // See `leftUntilDone`
+ percentDone: ?f32 = null,
+
+ pieces: ?[]u8 = null,
+ //pieceCount: ?usize = null,
+ //pieceSize: ?usize = null,
+ ////priorities array (see below) n/a
+ @"primary-mime-type": ?[]u8 = null,
+
+ // This torrent's queue position.
+ // All torrents have a queue position, even if it's not queued.
+ queuePosition: ?usize = null,
+
+ //@"rateDownload (B/s)": ?usize = null,
+ //@"rateUpload (B/s)": ?usize = null,
+
+ // When `tr_stat.activity` is `TR_STATUS_CHECK` or `TR_STATUS_CHECK_WAIT`,
+ // this is the percentage of how much of the files has been
+ // verified. When it gets to 1, the verify process is done.
+ // Range is [0..1]
+ // @see `tr_stat.activity`
+ recheckProgress: ?f32 = null,
+
+ // Number of seconds since the last activity (or since started).
+ // -1 if activity is not seeding or downloading.
+ secondsDownloading: ?f32 = null,
+
+ // Cumulative seconds the torrent's ever spent seeding.
+ secondsSeeding: ?f32 = null,
+
+ //seedIdleLimit: ?usize = null,
+ //seedIdleMode: ?usize = null,
+
+ // ??
+ seedRatioLimit: ?f32 = null,
+
+ //seedRatioMode: ?usize = null,
+ sequentialDownload: ?bool = null,
+
+ // Byte count of all the piece data we'll have downloaded when we're done,
+ // whether or not we have it yet. This may be less than `totalSize`
+ // if only some of the torrent's files are wanted.
+ sizeWhenDone: ?u64 = null,
+
+ // When the torrent was last started.
+ startDate: ?i64 = null,
+
+ //status: ?usize = null,
+ ////trackers array (see below) n/a
+ trackerList: ?[]u8 = null,
+ ////trackerStats array (see below) n/a
+ //totalSize: ?usize = null,
+ torrentFile: ?[]u8 = null,
+
+ // Byte count of all data you've ever uploaded for this torrent.
+ uploadedEver: ?u64 = null,
+
+ //uploadLimit: ?usize = null,
+ uploadLimited: ?bool = null,
+
+ // ??
+ uploadRatio: ?f32 = null,
+
+ ////wanted array (see below) n/a
+
+ // ??
+ webseeds: ?[][]u8 = null,
+
+ // Number of webseeds that are sending data to us.
+ webseedsSendingToUs: ?u16 = null,
+};