zigvm

zigvm: Zig Version Manager
git clone git://code.mfashby.net:/zigvm
Log | Files | Refs | README

commit e163eb0e9cd9e2451ca7b5e1e639ed69db50ecab
parent 526335ffac7ca5ee620a3c6cbc654317dbddcc5f
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Mon, 27 May 2024 22:12:27 +0100

Add list command

Diffstat:
Msrc/main.zig | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 66 insertions(+), 6 deletions(-)

diff --git a/src/main.zig b/src/main.zig @@ -43,12 +43,20 @@ pub fn main() !void { .defaultargvalue = defaultCacheDir, }; try ap.addFlag(&cacheDirFlag); + var listFlag: ArgParse.Flag = .{ + .long = "list", + .short = "l", + .description = "List available zig versions", + .hasarg = false, + }; + try ap.addFlag(&listFlag); if (!try ap.parseOrHelp()) { return; } const installDirPath = installDirFlag.argvalue orelse return error.MissingArg; const cacheDirPath = cacheDirFlag.argvalue orelse return error.MissingArg; const version = versionArg.value orelse return error.MissingArg; + const currentDirName = "current"; // Check the install dir is present in PATH, warn if it isn't var toks = std.mem.splitScalar(u8, path, ':'); @@ -95,11 +103,57 @@ pub fn main() !void { defer rdr.deinit(); var doc = try std.json.parseFromTokenSource(std.json.Value, a, &rdr, .{}); defer doc.deinit(); + + if (listFlag.waspresent) { + // List the files present in .zigvm, store in a hashmap + var hm: std.StringHashMapUnmanaged(void) = .{}; + defer { + var i = hm.iterator(); + while (i.next()) |*e| { + a.free(e.key_ptr.*); + } + hm.deinit(a); + } + var it = cacheDir.iterate(); + while (try it.next()) |e| { + try hm.put(a, try a.dupe(u8, e.name), undefined); + } + // Check what the 'current' directory is symlinked to + const currentLink = try cacheDir.realpathAlloc(a, currentDirName); + defer a.free(currentLink); + const currentLinkBasename = std.fs.path.basename(currentLink); + const currentTarball = try std.fmt.allocPrint(a, "{s}.tar.xz", .{currentLinkBasename}); + defer a.free(currentTarball); + const wtr = std.io.getStdOut().writer(); + var vit = doc.value.object.iterator(); + while (vit.next()) |e| { + const versionObj = e.value_ptr.object; + const versionObjForArch = versionObj.get(tuple) orelse continue; + const tarballUrl = versionObjForArch.object.get("tarball").?.string; + const tarballName = try baseNameFromUrl(a, tarballUrl); + defer a.free(tarballName); + const isPresent = hm.contains(tarballName); + const isCurrent = std.mem.eql(u8, tarballName, currentTarball); + try std.fmt.format(wtr, "version: {s}", .{e.key_ptr.*}); + if (versionObj.get("version")) |vs| { + try std.fmt.format(wtr, " ({s})", .{vs.string}); + } + if (isPresent) { + try wtr.writeAll(", downloaded"); + } + if (isCurrent) { + try wtr.writeAll(", current"); + } + try wtr.writeByte('\n'); + } + return; + } + const versionDoc: std.json.Value = doc.value.object.get(version) orelse return error.ZigVersionNotFound; const versionString = if (versionDoc.object.get("version")) |versionObj| versionObj.string else version; std.log.info("version {s} mapped to {s}", .{ version, versionString }); - // // Fetch the version if necessary + // Fetch the version if necessary const systemVersion = versionDoc.object.get(tuple) orelse return error.SystemNotFound; const tarballUrlObj = systemVersion.object.get("tarball") orelse return error.InvalidIndex; const tarballHashObj = systemVersion.object.get("shasum") orelse return error.InvalidIndex; @@ -110,10 +164,7 @@ pub fn main() !void { return error.UnsupportedFileFormat; } - const tarballUri = try std.Uri.parse(tarballUrl); - const tarballUriPath = try std.fmt.allocPrint(a, "{path}", .{tarballUri.path}); - defer a.free(tarballUriPath); - const tarballBasename = std.fs.path.basename(tarballUriPath); + const tarballBasename = try baseNameFromUrl(a, tarballUrl); std.log.info("basename: {s}", .{tarballBasename}); const tarballPath = try std.fs.path.join(a, &.{ cacheDirPath, tarballBasename }); defer a.free(tarballPath); @@ -163,7 +214,7 @@ pub fn main() !void { }; // then symlink the selected dir to 'current' - const currentDirName = "current"; + cacheDir.deleteFile(currentDirName) catch |e| switch (e) { error.FileNotFound => {}, else => return e, @@ -184,6 +235,15 @@ pub fn main() !void { std.log.info("done!", .{}); } +// Caller owns the result and must free it with a. +fn baseNameFromUrl(a: std.mem.Allocator, url: []const u8) ![]const u8 { + const tarballUri = try std.Uri.parse(url); + const tarballUriPath = try std.fmt.allocPrint(a, "{path}", .{tarballUri.path}); + defer a.free(tarballUriPath); + const tarballBasename = std.fs.path.basename(tarballUriPath); + return try a.dupe(u8, tarballBasename); +} + const ClientWithBuffer = struct { client: *std.http.Client, buffer: [std.mem.page_size]u8 = .{0} ** std.mem.page_size,