1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
pub const Config = @import("Config.zig");
pub const Installation = @import("Installation.zig");
pub const Installations = Installation.Installations;
pub const SubCommand = @import("subcommand.zig").SubCommand;
pub const help = SubCommand(Help);
pub const install = SubCommand(@import("install.zig"));
pub const list = SubCommand(@import("list.zig"));
pub const remove = SubCommand(@import("remove.zig"));
pub const @"switch" = SubCommand(@import("switch.zig"));
pub const update = SubCommand(@import("update.zig"));
pub const version = SubCommand(Version);
const std = @import("std");
const zup_config = @import("zup-config");
const ArgIterator = std.process.ArgIterator;
const ComptimeStringMap = std.ComptimeStringMap;
const GPA = std.heap.GeneralPurposeAllocator(.{});
const Tuple = std.meta.Tuple;
pub fn main() !void {
var gpa = GPA{};
const allocator = gpa.allocator();
defer _ = gpa.deinit();
var config = try Config.init(allocator);
defer config.deinit();
var args = try std.process.argsWithAllocator(allocator);
defer args.deinit();
_ = args.skip();
const cmd = args.next() orelse {
return Help.mainHelp();
};
return dispatch(cmd, "main", unknownCmd, .{ cmd, config, &args });
}
pub fn printVersion() !void {
return std.io.getStdErr().writer().print("{}\n", .{zup_config.version});
}
const Command = enum {
help,
install,
list,
remove,
@"switch",
update,
version,
};
const CommandMap = blk: {
const Pair = Tuple(&.{ []const u8, Command });
const commands = @typeInfo(Command).Enum.fields;
var map_data: [commands.len + 2]Pair = undefined;
map_data[0] = .{ "--help", .help };
map_data[1] = .{ "--version", .version };
var idx: usize = 2;
inline for (commands) |command| {
map_data[idx] = .{ command.name, @intToEnum(Command, command.value) };
idx += 1;
}
break :blk ComptimeStringMap(Command, map_data);
};
fn dispatch(
cmd: []const u8,
comptime fn_name: []const u8,
comptime default_fn: anytype,
args: anytype,
) !void {
// TODO: This can still be improved, currently we're looping through all possible values, it could be somehow made
// into a switch.
const cmd_enum = CommandMap.get(cmd) orelse return @call(.{}, default_fn, args);
const commands = @typeInfo(Command).Enum.fields;
inline for (commands) |command| {
if (@enumToInt(cmd_enum) == command.value) {
// TODO: zig-bug it cries about modifying a constant if
// I just write `return @call(.{}, fun, args);`
const fun = @field(@field(@This(), command.name), fn_name);
return call(fun, args);
}
}
unreachable;
}
inline fn call(fun: anytype, args: anytype) !void {
return @call(.{}, fun, args);
}
fn unknownCmd(cmd: []const u8, _: Config, _: *ArgIterator) !void {
std.log.err("Unknown subcommand: {s}", .{cmd});
return error.ArgError;
}
const Help = struct {
pub const params = "<COMMAND>";
pub const description = "Displays help for the specified <COMMAND>.";
pub const min_args = 0;
pub const max_args = 1;
pub fn main(comptime Result: type, _: Config, res: Result) !void {
if (res.positionals.len == 0) {
return mainHelp();
}
const cmd = res.positionals[0];
return dispatch(cmd, "help", unknownHelp, .{cmd});
}
pub fn mainHelp() !void {
const writer = std.io.getStdErr().writer();
try writer.writeAll(
\\USAGE: zup <COMMAND>
\\
\\These are the common Zup commands:
\\
\\ install Install a Zig version
\\ help See the help for various topics
\\ list List Zig versions
\\ remove Remove an installed Zig version
\\ switch Switch between installed Zig versions
\\ update Update installed Zig versions
\\ version Print the version of Zup
\\
\\You can find out more about a command by running `zup help <command>`.
\\
);
}
fn unknownHelp(cmd: []const u8) !void {
std.log.err("Unknown subcommand: {s}", .{cmd});
try mainHelp();
return error.ArgError;
}
};
const Version = struct {
pub const params = "";
pub const description = "Print the version of Zup and exit.";
pub const min_args = 0;
pub const max_args = 0;
pub fn main(comptime Result: type, _: Config, _: Result) !void {
return printVersion();
}
};
test "basic test" {
try std.testing.expectEqual(10, 3 + 7);
}
|