aboutsummaryrefslogtreecommitdiff
path: root/src/metainfo.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/metainfo.zig')
-rw-r--r--src/metainfo.zig54
1 files changed, 47 insertions, 7 deletions
diff --git a/src/metainfo.zig b/src/metainfo.zig
index 9fa4788..a202b0e 100644
--- a/src/metainfo.zig
+++ b/src/metainfo.zig
@@ -2,6 +2,7 @@
const std = @import("std");
const bencode = @import("bencode.zig");
const MetaInfo = @This();
+pub const Error = (error{Malformatted} || std.mem.Allocator.Error);
pub const Info = struct {
pub const File = struct {
@@ -12,36 +13,75 @@ pub const Info = struct {
piece_length: u64,
pieces: []const u8,
files: []File,
- pub fn parse(b: bencode.BValue) !Info {
+ pub fn parse(a: std.mem.Allocator, b: bencode.BValue) Error!Info {
var d = b.asDict() catch return error.Malformatted;
const pl = d.get("piece length") orelse return error.Malformatted;
const pp = d.get("pieces") orelse return error.Malformatted;
+ var files = std.ArrayList(File).init(a);
+ defer files.deinit();
+ if (d.get("files")) |f| {
+ // multi-file mode
+ const l = f.asList() catch return error.Malformatted;
+ for (l.items) |fi| {
+ const fd = fi.asDict() catch return error.Malformatted;
+ const fin = fd.get("name") orelse return error.Malformatted;
+ const fl = fd.get("length") orelse return error.Malformatted;
+ const fp = fd.get("path") orelse return error.Malformatted;
+ try files.append(.{
+ .name = fin.asString() catch return error.Malformatted,
+ .length = fl.asInt(u64) catch return error.Malformatted,
+ .path = fp.asString() catch return error.Malformatted,
+ });
+ }
+ } else {
+ // single-file mode
+ const fin = d.get("name") orelse return error.Malformatted;
+ const fl = d.get("length") orelse return error.Malformatted;
+ try files.append(.{
+ .name = fin.asString() catch return error.Malformatted,
+ .length = fl.asInt(u64) catch return error.Malformatted,
+ .path = fin.asString() catch return error.Malformatted, // just use the file name as path
+ });
+ }
+
return .{
.piece_length = pl.asInt(u64) catch return error.Malformatted,
.pieces = pp.asString() catch return error.Malformatted,
- .files = &[_]File{},
+ .files = try files.toOwnedSlice(),
};
}
+
+ pub fn deinit(self: *Info, a: std.mem.Allocator) void {
+ a.free(self.files);
+ }
};
info: Info,
announce: []const u8,
-pub fn parse(b: bencode.BValue) !MetaInfo {
+pub fn parse(a: std.mem.Allocator, b: bencode.BValue) Error!MetaInfo {
+ // TODO diagnostics
var d = b.asDict() catch return error.Malformatted;
const i = d.get("info") orelse return error.Malformatted;
- const a = d.get("announce") orelse return error.Malformatted;
+ const an = d.get("announce") orelse return error.Malformatted;
return .{
- .info = try Info.parse(i),
- .announce = a.asString() catch return error.Malformatted,
+ .info = try Info.parse(a, i),
+ .announce = an.asString() catch return error.Malformatted,
};
}
+pub fn deinit(self: *MetaInfo, a: std.mem.Allocator) void {
+ self.info.deinit(a);
+}
+
test "sample" {
const a = std.testing.allocator;
const sample_str = @embedFile("sample.torrent");
var b = try bencode.bdecodeBuf(a, sample_str);
defer b.deinit(a);
- const mi = try MetaInfo.parse(b);
+ var mi = try MetaInfo.parse(a, b);
+ defer mi.deinit(a);
try std.testing.expectEqualStrings("http://bittorrent-test-tracker.codecrafters.io/announce", mi.announce);
+ try std.testing.expectEqual(@as(usize, 1), mi.info.files.len);
+ try std.testing.expectEqualStrings("sample.txt", mi.info.files[0].name);
}