| ... | @@ -0,0 +1,95 @@ |
| 1 | const std = @import("std"); |
| 2 | const gpa = std.heap.c_allocator; |
| 3 | |
| 4 | const zuri = @import("zuri"); |
| 5 | const iguanatls = @import("iguanatls"); |
| 6 | const u = @import("./util/index.zig"); |
| 7 | |
| 8 | // |
| 9 | // |
| 10 | |
| 11 | const Zpm = struct { |
| 12 | pub const Package = struct { |
| 13 | author: []const u8, |
| 14 | name: []const u8, |
| 15 | tags: [][]const u8, |
| 16 | git: []const u8, |
| 17 | root_file: ?[]const u8, |
| 18 | description: []const u8, |
| 19 | }; |
| 20 | }; |
| 21 | |
| 22 | pub fn execute(args: [][]u8) !void { |
| 23 | const url = try zuri.Uri.parse("https://zpm.random-projects.net:443/api/packages", true); |
| 24 | |
| 25 | const sock = try std.net.tcpConnectToHost(gpa, url.host.name, url.port.?); |
| 26 | defer sock.close(); |
| 27 | |
| 28 | var client = try iguanatls.client_connect(.{ |
| 29 | .reader = sock.reader(), |
| 30 | .writer = sock.writer(), |
| 31 | .cert_verifier = .none, |
| 32 | .temp_allocator = gpa, |
| 33 | .ciphersuites = iguanatls.ciphersuites.all, |
| 34 | }, url.host.name); |
| 35 | defer client.close_notify() catch {}; |
| 36 | |
| 37 | const w = client.writer(); |
| 38 | try w.print("GET {s} HTTP/1.1\r\n", .{url.path}); |
| 39 | try w.print("Host: {s}:{}\r\n", .{url.host.name, url.port.?}); |
| 40 | try w.writeAll("Accept: application/json; charset=UTF-8\r\n"); |
| 41 | try w.writeAll("Connection: Close\r\n"); |
| 42 | try w.writeAll("\r\n"); |
| 43 | |
| 44 | const r = client.reader(); |
| 45 | const stdout = std.io.getStdOut(); |
| 46 | var buf: [1]u8 = undefined; |
| 47 | const data = &std.ArrayList(u8).init(gpa); |
| 48 | while (true) { |
| 49 | // const len = try r.read(&buf); |
| 50 | // if (len == 0) { |
| 51 | // break; |
| 52 | // } |
| 53 | // TODO: workaround for https://github.com/alexnask/iguanaTLS/issues/7 |
| 54 | const len = r.read(&buf) catch |err| switch (err) { |
| 55 | error.ServerMalformedResponse => break, |
| 56 | else => 0, |
| 57 | }; |
| 58 | try data.appendSlice(buf[0..len]); |
| 59 | } |
| 60 | |
| 61 | const index = std.mem.indexOf(u8, data.items, "\r\n\r\n").?; |
| 62 | const html_contents = data.items[index..]; |
| 63 | |
| 64 | var stream = std.json.TokenStream.init(html_contents[4..]); |
| 65 | const res = try std.json.parse([]Zpm.Package, &stream, .{ .allocator = gpa, }); |
| 66 | |
| 67 | const found = blk: { |
| 68 | for (res) |pkg| { |
| 69 | if (std.mem.eql(u8, pkg.name, args[0])) { |
| 70 | break :blk pkg; |
| 71 | } |
| 72 | } |
| 73 | u.assert(false, "no package with name '{s}' found", .{args[0]}); |
| 74 | unreachable; |
| 75 | }; |
| 76 | |
| 77 | u.assert(found.root_file != null, "package must have an entry point to be able to be added to your dependencies", .{}); |
| 78 | |
| 79 | const self_module = try u.ModFile.init(gpa, "zig.mod"); |
| 80 | for (self_module.deps) |dep| { |
| 81 | if (std.mem.eql(u8, dep.name, found.name)) { |
| 82 | u.assert(false, "dependency with name '{s}' already exists in your dependencies", .{found.name}); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | const file = try std.fs.cwd().openFile("zig.mod", .{ .read=true, .write=true }); |
| 87 | try file.seekTo(try file.getEndPos()); |
| 88 | |
| 89 | const file_w = file.writer(); |
| 90 | try file_w.print(" - src: git {s}\n", .{found.git}); |
| 91 | try file_w.print(" name: {s}\n", .{found.name}); |
| 92 | try file_w.print(" main: {s}\n", .{found.root_file.?[1..]}); |
| 93 | |
| 94 | std.log.info("Successfully added package {s} by {s}", .{found.name, found.author}); |
| 95 | } |