diff options
| author | 2022-04-25 05:09:55 +0300 | |
|---|---|---|
| committer | 2022-04-25 23:34:05 +0300 | |
| commit | d303b53f2ced75703bf022a5d337ee3ba530b288 (patch) | |
| tree | f2e64057120d01ee8a821596ea01b8fc37c53c2c /src/Installation.zig | |
| download | zup-d303b53f2ced75703bf022a5d337ee3ba530b288.tar.gz zup-d303b53f2ced75703bf022a5d337ee3ba530b288.tar.xz zup-d303b53f2ced75703bf022a5d337ee3ba530b288.zip | |
Initial commit0.1.0
Diffstat (limited to 'src/Installation.zig')
| -rw-r--r-- | src/Installation.zig | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/Installation.zig b/src/Installation.zig new file mode 100644 index 0000000..3b0382a --- /dev/null +++ b/src/Installation.zig | |||
| @@ -0,0 +1,196 @@ | |||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const curl = @import("curl"); | ||
| 3 | const std = @import("std"); | ||
| 4 | const xdg = @import("xdg"); | ||
| 5 | |||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | const ChildProcess = std.ChildProcess; | ||
| 8 | const JsonValue = std.json.Value; | ||
| 9 | const SemanticVersion = std.SemanticVersion; | ||
| 10 | const StringHashMap = std.StringHashMap; | ||
| 11 | |||
| 12 | const Installation = @This(); | ||
| 13 | |||
| 14 | pub const Installations = StringHashMap(Installation); | ||
| 15 | |||
| 16 | allocator: Allocator, | ||
| 17 | ver_str: []u8, | ||
| 18 | version: SemanticVersion, | ||
| 19 | url: ?[:0]u8, | ||
| 20 | |||
| 21 | pub fn deinit(self: *Installation) void { | ||
| 22 | self.allocator.free(self.ver_str); | ||
| 23 | if (self.url) |url| self.allocator.free(url); | ||
| 24 | |||
| 25 | self.* = undefined; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn deinitMap(allocator: Allocator, installations: *Installations) void { | ||
| 29 | var it = installations.iterator(); | ||
| 30 | while (it.next()) |kv| { | ||
| 31 | allocator.free(kv.key_ptr.*); | ||
| 32 | kv.value_ptr.deinit(); | ||
| 33 | } | ||
| 34 | |||
| 35 | installations.deinit(); | ||
| 36 | |||
| 37 | installations.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn getActiveName(allocator: Allocator) !?[]u8 { | ||
| 41 | var bin_home = try xdg.openBinHome(allocator); | ||
| 42 | defer bin_home.close(); | ||
| 43 | |||
| 44 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 45 | const linkname = bin_home.readLink("zig", &buf) catch |err| { | ||
| 46 | if (err == error.NotLink or err == error.FileNotFound) { | ||
| 47 | return null; | ||
| 48 | } else { | ||
| 49 | return err; | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | |||
| 53 | var linkpath = try bin_home.realpathAlloc(allocator, linkname); | ||
| 54 | defer allocator.free(linkpath); | ||
| 55 | |||
| 56 | var zup_dir = try xdg.getDataHome(allocator, "zup"); | ||
| 57 | defer allocator.free(zup_dir); | ||
| 58 | |||
| 59 | const rel_path = try std.fs.path.relative(allocator, zup_dir, linkpath); | ||
| 60 | defer allocator.free(rel_path); | ||
| 61 | |||
| 62 | return try allocator.dupe(u8, std.fs.path.dirname(rel_path).?); | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn isInstalled(allocator: Allocator, name: []const u8) !bool { | ||
| 66 | var zup_data = try xdg.openDataHome(allocator, "zup"); | ||
| 67 | defer zup_data.close(); | ||
| 68 | |||
| 69 | zup_data.access(name, .{}) catch return false; | ||
| 70 | return true; | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn getInstalledList(allocator: Allocator) !Installations { | ||
| 74 | var zup_data = try xdg.openDataHome(allocator, "zup"); | ||
| 75 | defer zup_data.close(); | ||
| 76 | |||
| 77 | var installations = Installations.init(allocator); | ||
| 78 | errdefer Installation.deinitMap(allocator, &installations); | ||
| 79 | |||
| 80 | var it = zup_data.iterate(); | ||
| 81 | while (try it.next()) |item| { | ||
| 82 | if (item.kind != .Directory) { | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | |||
| 86 | var inst_dir = try zup_data.openDir(item.name, .{}); | ||
| 87 | defer inst_dir.close(); | ||
| 88 | |||
| 89 | var zig_exe = inst_dir.realpathAlloc(allocator, "zig") catch |err| { | ||
| 90 | if (err == error.FileNotFound) { | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | return err; | ||
| 94 | }; | ||
| 95 | defer allocator.free(zig_exe); | ||
| 96 | |||
| 97 | const res = try ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ zig_exe, "version" } }); | ||
| 98 | errdefer allocator.free(res.stdout); | ||
| 99 | allocator.free(res.stderr); | ||
| 100 | |||
| 101 | if (res.term != .Exited or res.term.Exited != 0) { | ||
| 102 | std.log.warn("Failed to execute '{s} version'", .{zig_exe}); | ||
| 103 | allocator.free(res.stdout); | ||
| 104 | continue; | ||
| 105 | } | ||
| 106 | |||
| 107 | const trimmed_ver_str = std.mem.trim(u8, res.stdout, &std.ascii.spaces); | ||
| 108 | const version = try SemanticVersion.parse(trimmed_ver_str); | ||
| 109 | const name = try allocator.dupe(u8, item.name); | ||
| 110 | |||
| 111 | try installations.putNoClobber(name, Installation{ | ||
| 112 | .allocator = allocator, | ||
| 113 | .ver_str = res.stdout, | ||
| 114 | .version = version, | ||
| 115 | .url = null, | ||
| 116 | }); | ||
| 117 | } | ||
| 118 | |||
| 119 | return installations; | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn getAvailableList(allocator: Allocator) !Installations { | ||
| 123 | var json_str = try curl.easyDownload(allocator, "https://ziglang.org/download/index.json"); | ||
| 124 | defer allocator.free(json_str); | ||
| 125 | |||
| 126 | var parser = std.json.Parser.init(allocator, false); | ||
| 127 | defer parser.deinit(); | ||
| 128 | |||
| 129 | var vt = try parser.parse(json_str); | ||
| 130 | defer vt.deinit(); | ||
| 131 | |||
| 132 | if (vt.root != .Object) { | ||
| 133 | return error.JsonSchema; | ||
| 134 | } | ||
| 135 | |||
| 136 | var installations = Installations.init(allocator); | ||
| 137 | errdefer Installation.deinitMap(allocator, &installations); | ||
| 138 | |||
| 139 | var it = vt.root.Object.iterator(); | ||
| 140 | while (it.next()) |kv| { | ||
| 141 | if (try parseInstallation(allocator, kv.key_ptr.*, kv.value_ptr.*)) |*installation| { | ||
| 142 | errdefer installation.deinit(); | ||
| 143 | |||
| 144 | var name = try allocator.dupe(u8, kv.key_ptr.*); | ||
| 145 | errdefer allocator.free(name); | ||
| 146 | |||
| 147 | try installations.putNoClobber(name, installation.*); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | return installations; | ||
| 152 | } | ||
| 153 | |||
| 154 | fn parseInstallation(allocator: Allocator, name: []const u8, value: JsonValue) !?Installation { | ||
| 155 | if (value != .Object) { | ||
| 156 | return error.JsonSchema; | ||
| 157 | } | ||
| 158 | const map = value.Object; | ||
| 159 | |||
| 160 | const triple = @tagName(builtin.target.cpu.arch) ++ "-" ++ @tagName(builtin.target.os.tag); | ||
| 161 | |||
| 162 | const url_root = map.get(triple) orelse { | ||
| 163 | return null; | ||
| 164 | }; | ||
| 165 | if (url_root != .Object) { | ||
| 166 | return error.JsonSchema; | ||
| 167 | } | ||
| 168 | const url_src = url_root.Object.get("tarball") orelse { | ||
| 169 | return error.JsonSchema; | ||
| 170 | }; | ||
| 171 | if (url_src != .String) { | ||
| 172 | return error.JsonSchema; | ||
| 173 | } | ||
| 174 | var url = try allocator.dupeZ(u8, url_src.String); | ||
| 175 | errdefer allocator.free(url); | ||
| 176 | |||
| 177 | const version_src = if (map.get("version")) |ver| blk: { | ||
| 178 | if (ver != .String) { | ||
| 179 | return error.JsonSchema; | ||
| 180 | } else { | ||
| 181 | break :blk ver.String; | ||
| 182 | } | ||
| 183 | } else blk: { | ||
| 184 | break :blk name; | ||
| 185 | }; | ||
| 186 | var ver_str = try allocator.dupe(u8, version_src); | ||
| 187 | errdefer allocator.free(ver_str); | ||
| 188 | const version = try SemanticVersion.parse(ver_str); | ||
| 189 | |||
| 190 | return Installation{ | ||
| 191 | .allocator = allocator, | ||
| 192 | .ver_str = ver_str, | ||
| 193 | .version = version, | ||
| 194 | .url = url, | ||
| 195 | }; | ||
| 196 | } | ||