First commit, things mostly work locally, on linux x86
This commit is contained in:
commit
86bce941de
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
zig-cache/
|
||||||
|
zig-out/
|
70
build.zig
Normal file
70
build.zig
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "zim",
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
// This declares intent for the executable to be installed into the
|
||||||
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
// step when running `zig build`).
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
// This *creates* a Run step in the build graph, to be executed when another
|
||||||
|
// step is evaluated that depends on it. The next line below will establish
|
||||||
|
// such a dependency.
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
// By making the run step depend on the install step, it will be run from the
|
||||||
|
// installation directory rather than directly from within the cache directory.
|
||||||
|
// This is not necessary, however, if the application depends on other installed
|
||||||
|
// files, this ensures they will be present and in the expected location.
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
// This allows the user to pass arguments to the application in the build
|
||||||
|
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||||
|
// and can be selected like this: `zig build run`
|
||||||
|
// This will evaluate the `run` step rather than the default, which is "install".
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
|
// but does not run it.
|
||||||
|
const unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_unit_tests = b.addRunArtifact(unit_tests);
|
||||||
|
|
||||||
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
|
// running the unit tests.
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_unit_tests.step);
|
||||||
|
}
|
237
src/main.zig
Normal file
237
src/main.zig
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zim = @import("zim.zig");
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
|
||||||
|
const string = []const u8;
|
||||||
|
|
||||||
|
pub const ZIM_MAJOR_VER = 0;
|
||||||
|
pub const ZIM_MINOR_VER = 0;
|
||||||
|
pub const ZIM_BUILD = 1;
|
||||||
|
|
||||||
|
/// The availble arguments for the ZiM program
|
||||||
|
const Args = enum(u8) {
|
||||||
|
help,
|
||||||
|
install,
|
||||||
|
list,
|
||||||
|
use,
|
||||||
|
gir,
|
||||||
|
version,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A type of argument for the ZiM program
|
||||||
|
const ArgType = enum {
|
||||||
|
single,
|
||||||
|
expectsOneParam,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Allocates a string with the version of Zim, call `allocator.free()` with
|
||||||
|
/// the string when done using it.
|
||||||
|
pub fn versionStr(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
|
var buf = try allocator.alloc(u8, 10);
|
||||||
|
_ = try std.fmt.bufPrint(buf, "{d}.{d}.{d}", .{ ZIM_MAJOR_VER, ZIM_MINOR_VER, ZIM_BUILD });
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns what each argument expects to follow it
|
||||||
|
fn get_arg_parse_type(arg: Args) ArgType {
|
||||||
|
return switch (arg) {
|
||||||
|
.help => ArgType.single,
|
||||||
|
.install => ArgType.expectsOneParam,
|
||||||
|
.list => ArgType.expectsOneParam,
|
||||||
|
.use => ArgType.expectsOneParam,
|
||||||
|
.gir => ArgType.single,
|
||||||
|
.version => ArgType.single,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For a given `Args`, returns a string description of what each does
|
||||||
|
fn command_desc(arg: Args) string {
|
||||||
|
return switch (arg) {
|
||||||
|
.help => " help\t\tDisplays this help message",
|
||||||
|
.install => " install\tInstall the zig version",
|
||||||
|
.list => " list\t\tLists all versions available on the system",
|
||||||
|
.use => " use\t\tSets the active version of zig to use",
|
||||||
|
.version => " version\t\tPrint out the zim version you are using",
|
||||||
|
.gir => " gir\t\tPrint out ascii Gir",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows the main help menu for the program
|
||||||
|
fn show_help() void {
|
||||||
|
std.debug.print("Welcome to the ZIg version Manager (ZIM)\nHelp Menu:\n\n", .{});
|
||||||
|
inline for (@typeInfo(Args).Enum.fields) |arg_type| {
|
||||||
|
const tag = utils.nameToEnumTag(arg_type.name, Args) catch {
|
||||||
|
unreachable;
|
||||||
|
};
|
||||||
|
std.debug.print("{s}\n", .{command_desc(tag)});
|
||||||
|
}
|
||||||
|
std.debug.print("\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows a help menu for a specific `Args` action, like a help sub-menu
|
||||||
|
fn show_context_help(arg: Args) void {
|
||||||
|
switch (arg) {
|
||||||
|
.install => {
|
||||||
|
std.debug.print(
|
||||||
|
\\zim install <version>
|
||||||
|
\\ Used to install zig versions, that will then be available to
|
||||||
|
\\ the `zim use <version>` command.
|
||||||
|
\\
|
||||||
|
\\ To see all available versions to install, run `zim list global`
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
},
|
||||||
|
.list => {
|
||||||
|
std.debug.print(
|
||||||
|
\\zim list <local or global>
|
||||||
|
\\ Used to list zig versions, either locally or remotely available.
|
||||||
|
\\ Running zim global will make a network request to fetch the latest
|
||||||
|
\\ versions of zig.
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
},
|
||||||
|
.use => {
|
||||||
|
std.debug.print(
|
||||||
|
\\zim use <version>
|
||||||
|
\\ Used to switch the actively used zig version on the system. If
|
||||||
|
\\ your system PATH environment is set to use `~/.config/zim/zig`.
|
||||||
|
\\ If your system is not setup, you can run `zim init` and cross
|
||||||
|
\\ your fingers that it supports your setup.
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
},
|
||||||
|
.gir, .version, .help => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given `arg`, does the action with the provided optional `param`
|
||||||
|
fn do_arg_action(allocator: std.mem.Allocator, arg: Args, param: ?[]const u8) void {
|
||||||
|
switch (arg) {
|
||||||
|
.help => {
|
||||||
|
show_help();
|
||||||
|
},
|
||||||
|
.gir => {
|
||||||
|
utils.printGir();
|
||||||
|
},
|
||||||
|
.version => {
|
||||||
|
const ver = versionStr(allocator) catch {
|
||||||
|
std.debug.print("Error getting version\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer allocator.free(ver);
|
||||||
|
|
||||||
|
if (param) |command| {
|
||||||
|
std.debug.print("{s}\nRunning ZiM Version {s}\n", .{ command, ver });
|
||||||
|
} else {
|
||||||
|
std.debug.print("Running ZiM Version {s}\n", .{ver});
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.printGir();
|
||||||
|
},
|
||||||
|
.install => {
|
||||||
|
if (param == null) {
|
||||||
|
std.debug.print("Error: Expected version to follow\n\n", .{});
|
||||||
|
std.os.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const p = param.?;
|
||||||
|
if (std.mem.eql(u8, p, "help")) {
|
||||||
|
show_context_help(arg);
|
||||||
|
std.os.exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zim.install(allocator, param.?) catch |err| {
|
||||||
|
std.debug.print("Error running `install {s}`\n{any}", .{ param.?, err });
|
||||||
|
std.os.exit(1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.list => {
|
||||||
|
if (param == null) {
|
||||||
|
std.debug.print("Error: Expected `local` or `global` to follow.\nRun `zim list help` for details.\n\n", .{});
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
const p = param.?;
|
||||||
|
if (std.mem.eql(u8, p, "help")) {
|
||||||
|
show_context_help(arg);
|
||||||
|
std.os.exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!std.mem.eql(u8, p, "local") and !std.mem.eql(u8, p, "global")) {
|
||||||
|
std.debug.print("Error: List available versions either `list local` or `list global`\n\n", .{});
|
||||||
|
show_context_help(arg);
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
zim.list(allocator, param.?) catch |err| {
|
||||||
|
std.debug.print("Error running `list {s}`\n{any}", .{ param.?, err });
|
||||||
|
std.os.exit(1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.use => {
|
||||||
|
if (param == null) {
|
||||||
|
std.debug.print("Error: Expected version to follow\n\n", .{});
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
const p = param.?;
|
||||||
|
if (std.mem.eql(u8, p, "help")) {
|
||||||
|
show_context_help(arg);
|
||||||
|
std.os.exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zim.use(allocator, param.?) catch |err| {
|
||||||
|
std.debug.print("Error running `use {s}`\n{any}", .{ param.?, err });
|
||||||
|
std.os.exit(1);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
var args_iter = try std.process.argsWithAllocator(allocator);
|
||||||
|
defer args_iter.deinit();
|
||||||
|
|
||||||
|
// var parsed_args = std.ArrayList(string).init(allocator);
|
||||||
|
// defer parsed_args.deinit();
|
||||||
|
|
||||||
|
const valid = arg_loop: {
|
||||||
|
var index: u8 = 0;
|
||||||
|
const command = args_iter.next();
|
||||||
|
|
||||||
|
while (args_iter.next()) |arg| {
|
||||||
|
index += 1;
|
||||||
|
const arg_tag = utils.nameToEnumTag(arg, Args) catch {
|
||||||
|
break :arg_loop false;
|
||||||
|
};
|
||||||
|
var param: ?[]const u8 = null;
|
||||||
|
if (get_arg_parse_type(arg_tag) == ArgType.expectsOneParam) {
|
||||||
|
param = args_iter.next();
|
||||||
|
}
|
||||||
|
if (arg_tag == .version) {
|
||||||
|
do_arg_action(
|
||||||
|
allocator,
|
||||||
|
arg_tag,
|
||||||
|
command,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
do_arg_action(allocator, arg_tag, param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index == 0) {
|
||||||
|
show_help();
|
||||||
|
}
|
||||||
|
break :arg_loop true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
std.debug.print("Something is not right with the command, try running `zim help` for details.\n", .{});
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
}
|
47
src/utils.zig
Normal file
47
src/utils.zig
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const enumError = error{EnumFieldNotFound};
|
||||||
|
|
||||||
|
pub fn nameToEnumTag(name: []const u8, comptime Enum: type) enumError!Enum {
|
||||||
|
comptime {
|
||||||
|
if (@typeInfo(Enum) != .Enum) {
|
||||||
|
@compileError("Non enum type passed into function: " ++ @typeName(Enum));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (@typeInfo(Enum).Enum.fields) |field| {
|
||||||
|
if (std.mem.eql(u8, name, field.name)) {
|
||||||
|
const tag: Enum = @enumFromInt(field.value);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enumError.EnumFieldNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
const State = enum {
|
||||||
|
good,
|
||||||
|
bad,
|
||||||
|
};
|
||||||
|
|
||||||
|
const enum_tag = try nameToEnumTag("good", State);
|
||||||
|
try std.testing.expect(enum_tag == State.good);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn printGir() void {
|
||||||
|
std.debug.print(
|
||||||
|
\\ n n
|
||||||
|
\\ o ( )
|
||||||
|
\\ ___/_ / I am \
|
||||||
|
\\ / / . o ( your )
|
||||||
|
\\ 0> 0 / \ Doom! /
|
||||||
|
\\ Lu__/ ( )
|
||||||
|
\\ || - u -
|
||||||
|
\\ o^ ^ o
|
||||||
|
\\ _/ / / \_
|
||||||
|
\\ |__|
|
||||||
|
\\ O=> (O
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
}
|
101
src/version.zig
Normal file
101
src/version.zig
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const string = []const u8;
|
||||||
|
|
||||||
|
pub const ZigVersion = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
version_string: string,
|
||||||
|
tarball_url: ?string,
|
||||||
|
src_tarball_url: ?string,
|
||||||
|
platform_string: ?string,
|
||||||
|
docs_url: ?string,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator, scanner: *std.json.Scanner, ver_string: string) ZigVersion {
|
||||||
|
var version_string = allocator.dupe(u8, ver_string) catch unreachable;
|
||||||
|
var docs_url: ?string = null;
|
||||||
|
var tarball_url: ?string = null;
|
||||||
|
var platform_string: ?string = null;
|
||||||
|
var src_tarball_url: ?string = null;
|
||||||
|
|
||||||
|
var scanning = true;
|
||||||
|
while (scanning) {
|
||||||
|
if (scanner.*.next()) |token| {
|
||||||
|
switch (token) {
|
||||||
|
.object_begin => {},
|
||||||
|
.object_end => {
|
||||||
|
const t_type = scanner.*.peekNextTokenType() catch {
|
||||||
|
unreachable;
|
||||||
|
};
|
||||||
|
if (t_type == std.json.TokenType.object_end) {
|
||||||
|
scanning = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.string => {
|
||||||
|
if (std.mem.eql(u8, token.string, "docs")) {
|
||||||
|
const docs_token = scanner.*.next() catch unreachable;
|
||||||
|
docs_url = allocator.dupe(u8, docs_token.string) catch unreachable;
|
||||||
|
} else if (std.mem.eql(u8, token.string, "src")) {
|
||||||
|
// ignore the object begin
|
||||||
|
checkNextToken(scanner, .object_begin);
|
||||||
|
_ = scanner.*.next() catch unreachable;
|
||||||
|
// ignore tarball string
|
||||||
|
checkNextToken(scanner, .string);
|
||||||
|
_ = scanner.*.next() catch unreachable;
|
||||||
|
const tar_token = scanner.*.next() catch unreachable;
|
||||||
|
src_tarball_url = allocator.dupe(u8, tar_token.string) catch unreachable;
|
||||||
|
} else if (std.mem.eql(u8, token.string, "x86_64-linux")) {
|
||||||
|
platform_string = allocator.dupe(u8, token.string) catch unreachable;
|
||||||
|
// ignore object begin
|
||||||
|
checkNextToken(scanner, .object_begin);
|
||||||
|
_ = scanner.*.next() catch unreachable;
|
||||||
|
checkNextToken(scanner, .string);
|
||||||
|
const tar_token = scanner.*.next() catch unreachable;
|
||||||
|
if (std.mem.eql(u8, tar_token.string, "tarball")) {
|
||||||
|
const tar_url_token = scanner.*.next() catch unreachable;
|
||||||
|
tarball_url = allocator.dupe(u8, tar_url_token.string) catch unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else |err| {
|
||||||
|
std.debug.print("Error scanning in ZigVersion: {any}\n", .{err});
|
||||||
|
scanning = false;
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ZigVersion{ .allocator = allocator, .version_string = version_string, .tarball_url = tarball_url, .src_tarball_url = src_tarball_url, .platform_string = platform_string, .docs_url = docs_url };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {}
|
||||||
|
|
||||||
|
pub fn fmtPrint(zv: *const ZigVersion) void {
|
||||||
|
std.debug.print(" zig-{s}\n", .{zv.version_string});
|
||||||
|
if (zv.platform_string) |plat| {
|
||||||
|
std.debug.print("\tplatform: {s}\n", .{plat});
|
||||||
|
}
|
||||||
|
if (zv.docs_url) |docs| {
|
||||||
|
std.debug.print("\tdocs_url: {s}\n", .{docs});
|
||||||
|
}
|
||||||
|
if (zv.tarball_url) |tar| {
|
||||||
|
std.debug.print("\ttar_url: {s}\n", .{tar});
|
||||||
|
}
|
||||||
|
if (zv.src_tarball_url) |tar| {
|
||||||
|
std.debug.print("\tsrc_tar_url: {s}\n", .{tar});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hasTar(zv: *ZigVersion) bool {
|
||||||
|
return zv.tarball_url != null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn checkNextToken(scanner: *std.json.Scanner, t_type: std.json.TokenType) void {
|
||||||
|
const t = scanner.*.peekNextTokenType() catch unreachable;
|
||||||
|
if (t != t_type) {
|
||||||
|
std.debug.print("Expected to find {any} but got {any}\n", .{ t_type, t });
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
348
src/zim.zig
Normal file
348
src/zim.zig
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
const version = @import("version.zig");
|
||||||
|
const string = []const u8;
|
||||||
|
|
||||||
|
const ZigVersion = version.ZigVersion;
|
||||||
|
|
||||||
|
const ZimError = error{
|
||||||
|
BadParameter,
|
||||||
|
MalformedJson,
|
||||||
|
NetworkError,
|
||||||
|
NotImplemented,
|
||||||
|
NoHomeConfigured,
|
||||||
|
Unexpected,
|
||||||
|
};
|
||||||
|
|
||||||
|
const BIN_SYM_ZIG_PATH = "bin/zig";
|
||||||
|
|
||||||
|
fn createSubDir(dir: std.fs.Dir, path: string) ZimError!void {
|
||||||
|
dir.makeDir(path) catch |err| blk: {
|
||||||
|
if (err == error.PathAlreadyExists) {
|
||||||
|
// std.debug.print("{s} already exists\n", .{path});
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
std.debug.print("Encountered error creating directory {s}: {any}\n", .{ path, err });
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openZimDir(allocator: std.mem.Allocator) ZimError!std.fs.Dir {
|
||||||
|
const home_dir = std.os.getenv("HOME");
|
||||||
|
if (home_dir == null) {
|
||||||
|
return ZimError.NoHomeConfigured;
|
||||||
|
}
|
||||||
|
// std.debug.print("Found home env to be: {s}\n", .{home_dir.?});
|
||||||
|
const zimPath = std.mem.concat(allocator, u8, &[_][]const u8{ home_dir.?, "/.config/zim" }) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
defer allocator.free(zimPath);
|
||||||
|
|
||||||
|
// std.debug.print("Creating zim directory in {s}\n", .{zimPath});
|
||||||
|
std.fs.makeDirAbsolute(zimPath) catch |err| blk: {
|
||||||
|
if (err == error.PathAlreadyExists) {
|
||||||
|
// std.debug.print("Do not need to create new directory\n", .{});
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
std.debug.print("Error creating directory: {any}\n", .{err});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
var zim_dir = std.fs.openDirAbsolute(zimPath, .{}) catch {
|
||||||
|
// std.debug.print("Encountered error opening directory: {any}\n", .{err});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
try createSubDir(zim_dir, "bin");
|
||||||
|
try createSubDir(zim_dir, "versions");
|
||||||
|
return zim_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getLocalVersionsList(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
zim_dir: std.fs.Dir,
|
||||||
|
) ZimError!std.ArrayList(string) {
|
||||||
|
var versions_dir = zim_dir.openIterableDir("versions", .{}) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
defer versions_dir.close();
|
||||||
|
|
||||||
|
var ver_walker = versions_dir.walk(allocator) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
defer ver_walker.deinit();
|
||||||
|
|
||||||
|
var versions_list = std.ArrayList(string).init(allocator);
|
||||||
|
|
||||||
|
var walking = true;
|
||||||
|
while (walking) blk: {
|
||||||
|
var ver = ver_walker.next() catch {
|
||||||
|
walking = false;
|
||||||
|
break :blk;
|
||||||
|
};
|
||||||
|
if (ver == null) {
|
||||||
|
walking = false;
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
// Dont recursively enter into any of the zig directories
|
||||||
|
if (!std.mem.eql(u8, ver.?.basename, ver.?.path)) {
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
if (ver.?.kind == .directory) {
|
||||||
|
const path = ver.?.path;
|
||||||
|
const start_name = path[0..3];
|
||||||
|
if (std.mem.eql(u8, start_name, "zig")) {
|
||||||
|
var p = allocator.dupe(u8, path) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
versions_list.append(p) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// std.debug.print("Got entry in `versions:` base: {s}, path: {s}, kind: {any}\n", .{ ver.?.basename, ver.?.path, ver.?.kind });
|
||||||
|
}
|
||||||
|
return versions_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getRemoteVersionsList(allocator: std.mem.Allocator) ZimError!std.ArrayList(ZigVersion) {
|
||||||
|
const versions_json_url = "https://ziglang.org/download/index.json";
|
||||||
|
const versions_json_uri = std.Uri.parse(versions_json_url) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
var headers = std.http.Headers{ .allocator = allocator };
|
||||||
|
defer headers.deinit();
|
||||||
|
|
||||||
|
// Accept anything.
|
||||||
|
headers.append("accept", "*/*") catch {
|
||||||
|
std.debug.print("Error adding headers\n", .{});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
var client = std.http.Client{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
var request = client.request(.GET, versions_json_uri, headers, .{}) catch {
|
||||||
|
std.debug.print("Error creating request\n", .{});
|
||||||
|
return ZimError.NetworkError;
|
||||||
|
};
|
||||||
|
defer request.deinit();
|
||||||
|
|
||||||
|
request.start() catch {
|
||||||
|
std.debug.print("Error starting request\n", .{});
|
||||||
|
return ZimError.NetworkError;
|
||||||
|
};
|
||||||
|
std.debug.print("Querying ziglang.org for latest zig versions...\n", .{});
|
||||||
|
request.wait() catch {
|
||||||
|
std.debug.print("Error waiting for request\n", .{});
|
||||||
|
return ZimError.NetworkError;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sixty_four_kilobytes = 65536;
|
||||||
|
const body = request.reader().readAllAlloc(allocator, sixty_four_kilobytes) catch |err| {
|
||||||
|
std.debug.print("Error getting body: {any}\n", .{err});
|
||||||
|
return ZimError.NetworkError;
|
||||||
|
};
|
||||||
|
defer allocator.free(body);
|
||||||
|
|
||||||
|
var diag = std.json.Diagnostics{};
|
||||||
|
var body_scanner = std.json.Scanner.initCompleteInput(allocator, body);
|
||||||
|
body_scanner.enableDiagnostics(&diag);
|
||||||
|
|
||||||
|
var remote_zig_versions = std.ArrayList(ZigVersion).init(allocator);
|
||||||
|
|
||||||
|
var scanning = true;
|
||||||
|
var last_version: string = "";
|
||||||
|
while (scanning) {
|
||||||
|
if (body_scanner.next()) |token| {
|
||||||
|
switch (token) {
|
||||||
|
.object_begin => {
|
||||||
|
const zv = ZigVersion.init(allocator, &body_scanner, last_version);
|
||||||
|
remote_zig_versions.append(zv) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.end_of_document => {
|
||||||
|
scanning = false;
|
||||||
|
},
|
||||||
|
.string => {
|
||||||
|
last_version = token.string;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else |err| switch (err) {
|
||||||
|
error.SyntaxError => {
|
||||||
|
std.debug.print("Syntax error at line {d} col {d}\n", .{ diag.line_number, diag.getColumn() });
|
||||||
|
scanning = false;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.debug.print("Got error {any}\n", .{err});
|
||||||
|
scanning = false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remote_zig_versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShellType = enum {
|
||||||
|
bash,
|
||||||
|
zsh,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn shellName(s: ?ShellType) string {
|
||||||
|
comptime switch (s) {
|
||||||
|
.bash => "bash",
|
||||||
|
.zsh => "zsh",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printZimPathHelp(shell_tag: ?ShellType, zim_path: string) void {
|
||||||
|
if (shell_tag == undefined) {
|
||||||
|
std.debug.print("Unrecognized shell\n", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const t = "something";
|
||||||
|
_ = t;
|
||||||
|
switch (shell_tag.?) {
|
||||||
|
.bash => {
|
||||||
|
std.debug.print("\nDetected shell as {s}\n", .{@tagName(shell_tag.?)});
|
||||||
|
std.debug.print("For ZiM to work, you need to add\n'{s}'\nto your PATH. To do that, you can\nrun the following command:\n\n", .{zim_path});
|
||||||
|
std.debug.print("\techo \"export PATH={s}:$PATH\" >> ~/.zshrc\n\nOr edit your .zshrc to add it manually.", .{zim_path});
|
||||||
|
},
|
||||||
|
.zsh => {
|
||||||
|
std.debug.print("\nDetected shell as {s}\n", .{@tagName(shell_tag.?)});
|
||||||
|
std.debug.print("For ZiM to work, you need to add\n'{s}'\nto your PATH. To do that, you can\nrun the following command:\n\n", .{zim_path});
|
||||||
|
std.debug.print("\techo \"export PATH={s}:$PATH\" >> ~/.zshrc\n\nOr edit your .zshrc to add it manually.", .{zim_path});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(allocator: std.mem.Allocator, param: string) ZimError!void {
|
||||||
|
_ = param;
|
||||||
|
|
||||||
|
var zim_dir = try openZimDir(allocator);
|
||||||
|
defer zim_dir.close();
|
||||||
|
|
||||||
|
return ZimError.NotImplemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use(allocator: std.mem.Allocator, param: string) ZimError!void {
|
||||||
|
var zim_dir = try openZimDir(allocator);
|
||||||
|
defer zim_dir.close();
|
||||||
|
|
||||||
|
var versions_list = try getLocalVersionsList(allocator, zim_dir);
|
||||||
|
defer versions_list.deinit();
|
||||||
|
|
||||||
|
const version_num = std.fmt.parseInt(u8, param, 0) catch null;
|
||||||
|
if (version_num != null) {
|
||||||
|
const num = version_num.?;
|
||||||
|
if (num > versions_list.items.len) {
|
||||||
|
std.debug.print("{d} exceeds the range of available versions: {d}\n", .{ num, versions_list.items.len });
|
||||||
|
return ZimError.BadParameter;
|
||||||
|
}
|
||||||
|
const ver_path = versions_list.items[version_num.? - 1];
|
||||||
|
std.debug.print("Using version {s}\n", .{ver_path});
|
||||||
|
|
||||||
|
const zim_path = zim_dir.realpathAlloc(allocator, "./") catch "err";
|
||||||
|
defer allocator.free(zim_path);
|
||||||
|
|
||||||
|
const target_version_path = std.mem.concat(allocator, u8, &[_][]const u8{ zim_path, "/versions/", ver_path }) catch {
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
defer allocator.free(target_version_path);
|
||||||
|
|
||||||
|
zim_dir.deleteFile(BIN_SYM_ZIG_PATH) catch |err| {
|
||||||
|
if (err != error.FileNotFound) {
|
||||||
|
std.debug.print("Error deleting old link: {any}\n", .{err});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std.debug.print("Creating sym link to {s}\n", .{target_version_path});
|
||||||
|
zim_dir.symLink(target_version_path, BIN_SYM_ZIG_PATH, .{ .is_directory = true }) catch |err| {
|
||||||
|
std.debug.print("Could not link new version: {any}\n", .{err});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
const path_environment = std.os.getenv("PATH");
|
||||||
|
if (path_environment) |path_env| {
|
||||||
|
if (!std.mem.containsAtLeast(u8, path_env, 1, zim_path)) {
|
||||||
|
var shell_tag: ?ShellType = null;
|
||||||
|
const shell_env = std.os.getenv("SHELL");
|
||||||
|
if (shell_env) |s| {
|
||||||
|
var split = std.mem.splitSequence(u8, s, "/");
|
||||||
|
var shell: []const u8 = "";
|
||||||
|
while (split.next()) |field| {
|
||||||
|
shell = field;
|
||||||
|
}
|
||||||
|
shell_tag = utils.nameToEnumTag(shell, ShellType) catch null;
|
||||||
|
}
|
||||||
|
const shell_bin_path =
|
||||||
|
std.mem.concat(allocator, u8, &[_][]const u8{ zim_path, "/", BIN_SYM_ZIG_PATH }) catch "<ZimERR>";
|
||||||
|
printZimPathHelp(
|
||||||
|
shell_tag,
|
||||||
|
shell_bin_path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
std.debug.print("Using raw string version {s}\n", .{param});
|
||||||
|
return ZimError.NotImplemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
// std.debug.print("Local Zig Versions:\n", .{});
|
||||||
|
// var i: u8 = 0;
|
||||||
|
// while (versions_list.popOrNull()) |version| {
|
||||||
|
// std.debug.print("\n [{d}]\tZig Version {s}", .{ i + 1, version });
|
||||||
|
// i += 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZimListType = enum { local, global };
|
||||||
|
|
||||||
|
/// Runs the `list` subcommand
|
||||||
|
pub fn list(allocator: std.mem.Allocator, param: string) !void {
|
||||||
|
const list_type = utils.nameToEnumTag(param, ZimListType) catch {
|
||||||
|
std.debug.print("Unexpected parameter to `list`: {s}\n", .{param});
|
||||||
|
return ZimError.BadParameter;
|
||||||
|
};
|
||||||
|
switch (list_type) {
|
||||||
|
.local => {
|
||||||
|
var zim_dir = try openZimDir(allocator);
|
||||||
|
defer zim_dir.close();
|
||||||
|
|
||||||
|
var versions_list = try getLocalVersionsList(allocator, zim_dir);
|
||||||
|
defer versions_list.deinit();
|
||||||
|
|
||||||
|
std.debug.print("Local Zig Versions:\n", .{});
|
||||||
|
var i: u8 = 0;
|
||||||
|
while (versions_list.popOrNull()) |version_str| {
|
||||||
|
std.debug.print("\n [{d}]\tZig Version {s}", .{ i + 1, version_str });
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
std.debug.print("\n", .{});
|
||||||
|
std.debug.print(
|
||||||
|
\\
|
||||||
|
\\ Run `zim use` to select which version you want active in your environment.
|
||||||
|
\\ You can also just specify the index of the version you would like to use.
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
.global => {
|
||||||
|
var remote_versions = try getRemoteVersionsList(allocator);
|
||||||
|
if (remote_versions.items.len == 0) {
|
||||||
|
std.debug.print("Failed to get remote versions\n", .{});
|
||||||
|
return ZimError.Unexpected;
|
||||||
|
}
|
||||||
|
std.debug.print("Retrieved remote zig versions:\n", .{});
|
||||||
|
for (remote_versions.items) |remote_version| {
|
||||||
|
remote_version.fmtPrint();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user