summaryrefslogtreecommitdiff
path: root/src/main.zig
blob: 0fe6f16690b8d21c5d85c5fdf9a6c3aa18f9d236 (plain) (blame)
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
pub const Config = @import("Config.zig");
pub const EasyHttp = @import("EasyHttp.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 GPA = std.heap.GeneralPurposeAllocator(.{});
const StaticStringMap = std.StaticStringMap;
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;
    for (commands) |command| {
        map_data[idx] = .{ command.name, @as(Command, @enumFromInt(command.value)) };
        idx += 1;
    }

    break :blk StaticStringMap(Command).initComptime(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(.auto, default_fn, args);
    const commands = @typeInfo(Command).@"enum".fields;
    inline for (commands) |command| {
        if (@intFromEnum(cmd_enum) == command.value) {
            const fun = @field(@field(@This(), command.name), fn_name);
            return @call(.auto, fun, args);
        }
    }
    unreachable;
}

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);
}