1const std = @import("std");
2const string = []const u8;
3const gpa = std.heap.c_allocator;
4const extras = @import("extras");
5const json = @import("json");
6const nio = @import("nio");
7const root = @import("root");
8
9const u = @import("./../util/funcs.zig");
10
11//
12//
13
14pub const commands = struct {
15 pub const add = @import("./zpm/add.zig");
16 pub const showjson = @import("./zpm/showjson.zig");
17 pub const tags = @import("./zpm/tags.zig");
18 pub const search = @import("./zpm/search.zig");
19};
20
21pub const server_root = "https://zig.pm/api";
22
23pub const Package = struct {
24 author: string,
25 name: string,
26 tags: []const string,
27 git: string,
28 root_file: string,
29 description: string,
30 source: u32,
31 links: []const string,
32};
33
34pub fn execute(self_name: []const u8, args: []const [:0]const u8) !void {
35 if (args.len == 0) {
36 std.debug.print("{s}\n", .{
37 \\This is a subcommand for use with https://github.com/zigtools/zpm-server instances but has no default behavior on its own aside from showing you this nice help text.
38 \\
39 \\The default remote is https://zig.pm/.
40 \\
41 \\The subcommands available are:
42 \\ - add Append this package to your dependencies
43 \\ - showjson Print raw json from queried API responses
44 \\ - tags Print the list of tags available on the server.
45 \\ - search Search the api for available packages and print them.
46 });
47 return;
48 }
49
50 inline for (comptime std.meta.declarations(commands)) |decl| {
51 if (std.mem.eql(u8, args[0], decl.name)) {
52 const cmd = @field(commands, decl.name);
53 try cmd.execute(self_name, args[1..]);
54 return;
55 }
56 }
57 u.fail("unknown command \"{s}\" for \"zigmod zpm\"", .{args[0]});
58}
59
60pub fn server_fetch(url: string) !json.Document {
61 const io = root.io;
62 var buf: [4096]u8 = @splat(0);
63 var client: std.http.Client = .{ .io = io, .allocator = gpa };
64 defer client.deinit();
65
66 var req = try client.request(.GET, try std.Uri.parse(url), .{
67 .headers = .{ .accept_encoding = .{ .override = "identity" } },
68 });
69 defer req.deinit();
70 try req.sendBodiless();
71 var resp = try req.receiveHead(&.{});
72 if (resp.head.status != .ok) u.fail("expected: 200 from '{s}' got: {s}", .{ url, @tagName(resp.head.status) });
73 const content = try resp.reader(&buf).allocRemaining(gpa, .limited(1024 * 1024 * 5));
74 return json.parseFromSlice(gpa, "", content, .{ .support_trailing_commas = true, .maximum_depth = 100 });
75}
76
77pub fn server_fetchArray(url: string) ![]const Package {
78 const doc = try server_fetch(url);
79 doc.acquire();
80 defer doc.release();
81 var list = std.array_list.Managed(Package).init(gpa);
82 errdefer list.deinit();
83
84 for (doc.root.array()) |item| {
85 const obj = item.object();
86 if (obj.getS("root_file") == null) continue;
87 try list.append(Package{
88 .name = obj.getS("name").?,
89 .author = obj.getS("author").?,
90 .description = obj.getS("description").?,
91 .tags = try valueStrArray(obj.getA("tags").?),
92 .git = obj.getS("git").?,
93 .root_file = obj.getS("root_file").?,
94 .source = obj.getN("source").?.get(u32),
95 .links = try valueLinks(obj.getO("links").?),
96 });
97 }
98 return list.toOwnedSlice();
99}
100
101fn valueStrArray(vals: json.Array) ![]string {
102 var list = std.array_list.Managed(string).init(gpa);
103 errdefer list.deinit();
104
105 for (vals) |item| {
106 if (item.v() != .string) continue;
107 try list.append(item.string());
108 }
109 return list.toOwnedSlice();
110}
111
112fn valueLinks(vals: json.ObjectIndex) ![]string {
113 var list = std.array_list.Managed(string).init(gpa);
114 errdefer list.deinit();
115
116 if (vals.getS("github")) |x| try list.append(x);
117 if (vals.getS("aquila")) |x| try list.append(x);
118 if (vals.getS("astrolabe")) |x| try list.append(x);
119 return list.toOwnedSlice();
120}