123const std = @import("std");4const Io = std.Io;56const types = @import("types.zig");7const errors = @import("errors.zig");8const config = @import("config.zig");9const commands = @import("commands.zig");10const output = @import("output.zig");1112const Args = struct {13 command: []const u8 = "",14 sub: []const u8 = "",15 rest: []const []const u8 = &.{},16 machine: ?[]const u8 = null,17 dry: bool = false,18 check: bool = false,19 no_apply: bool = false,20 all: bool = false,21 json: bool = false,22 last: u32 = 50,23};2425pub fn main(startup: std.process.Init) !void {26 const arena = startup.arena.allocator();27 const gpa = startup.gpa;28 const io = startup.io;2930 31 var stdout_buf: [16384]u8 = undefined;32 var stdout_fw: Io.File.Writer = .init(.stdout(), io, &stdout_buf);33 output.init(&stdout_fw.interface, startup.environ_map, true);34 defer output.flush();3536 const argv = try startup.minimal.args.toSlice(arena);37 const args = try parseArgs(arena, argv);3839 40 if (args.command.len == 0 or std.mem.eql(u8, args.command, "help")) {41 output.help();42 return;43 }4445 46 const cfg = config.load(arena, io, startup.environ_map) catch |err| {47 const msg = errors.error_info.getMessage() orelse errors.staticMessage(err);48 const detail = errors.error_info.getDetail();49 const suggestion = errors.error_info.getSuggestion();50 output.printError(msg, detail, suggestion);51 output.flush();52 std.process.exit(1);53 };5455 output.setTeach(cfg.teach);5657 const machine = config.resolveMachine(&cfg, args.machine) catch |err| {58 const msg = errors.error_info.getMessage() orelse errors.staticMessage(err);59 const detail = errors.error_info.getDetail();60 const suggestion = errors.error_info.getSuggestion();61 output.printError(msg, detail, suggestion);62 output.flush();63 std.process.exit(1);64 };6566 const ctx = commands.Ctx{67 .gpa = gpa,68 .io = io,69 .environ = startup.environ_map,70 .machine = machine,71 .cfg = cfg,72 .args = args.rest,73 .sub = args.sub,74 .dry = args.dry,75 .check = args.check,76 .no_apply = args.no_apply,77 .all = args.all,78 .last = args.last,79 .json = args.json,80 };8182 dispatch(ctx, args.command) catch |err| {83 const msg = errors.error_info.getMessage() orelse errors.staticMessage(err);84 const detail = errors.error_info.getDetail();85 const suggestion = errors.error_info.getSuggestion();86 output.printError(msg, detail, suggestion);87 output.flush();88 std.process.exit(1);89 };90}9192fn dispatch(ctx: commands.Ctx, cmd: []const u8) !void {93 if (std.mem.eql(u8, cmd, "apply")) return commands.apply(ctx);94 if (std.mem.eql(u8, cmd, "check")) return commands.check(ctx);95 if (std.mem.eql(u8, cmd, "back")) return commands.back(ctx);96 if (std.mem.eql(u8, cmd, "go")) return commands.go(ctx);97 if (std.mem.eql(u8, cmd, "history")) return commands.history(ctx);98 if (std.mem.eql(u8, cmd, "gen")) return commands.genCmd(ctx);99 if (std.mem.eql(u8, cmd, "clean")) return commands.clean(ctx);100 if (std.mem.eql(u8, cmd, "status")) return commands.statusCmd(ctx);101 if (std.mem.eql(u8, cmd, "diff")) return commands.diff(ctx);102 if (std.mem.eql(u8, cmd, "doctor")) return commands.doctor(ctx);103 if (std.mem.eql(u8, cmd, "update")) return commands.update(ctx);104 if (std.mem.eql(u8, cmd, "upgrade")) return commands.upgrade(ctx);105 if (std.mem.eql(u8, cmd, "log")) return commands.log(ctx);106 if (std.mem.eql(u8, cmd, "edit")) return commands.edit(ctx);107 if (std.mem.eql(u8, cmd, "fmt")) return commands.fmt(ctx);108 if (std.mem.eql(u8, cmd, "repl")) return commands.repl(ctx);109 if (std.mem.eql(u8, cmd, "develop")) return commands.develop(ctx);110 if (std.mem.eql(u8, cmd, "info")) return commands.info(ctx);111 if (std.mem.eql(u8, cmd, "boot")) return commands.boot(ctx);112 if (std.mem.eql(u8, cmd, "search")) return commands.searchCmd(ctx);113 if (std.mem.eql(u8, cmd, "option")) return commands.optionCmd(ctx);114 if (std.mem.eql(u8, cmd, "install")) return commands.install(ctx);115 if (std.mem.eql(u8, cmd, "remove")) return commands.remove(ctx);116 if (std.mem.eql(u8, cmd, "list")) return commands.list(ctx);117 if (std.mem.eql(u8, cmd, "try")) return commands.tryCmd(ctx);118 if (std.mem.eql(u8, cmd, "service")) return commands.service(ctx);119 if (std.mem.eql(u8, cmd, "flake")) return commands.flake(ctx);120 if (std.mem.eql(u8, cmd, "store")) return commands.store(ctx);121 if (std.mem.eql(u8, cmd, "channel")) return commands.channel(ctx);122 if (std.mem.eql(u8, cmd, "profile")) return commands.profile(ctx);123 if (std.mem.eql(u8, cmd, "pkg")) return commands.pkg(ctx);124 if (std.mem.eql(u8, cmd, "pin")) return commands.pin(ctx);125 if (std.mem.eql(u8, cmd, "unpin")) return commands.unpin(ctx);126 if (std.mem.eql(u8, cmd, "hash")) return commands.hash(ctx);127 if (std.mem.eql(u8, cmd, "fetch")) return commands.fetch(ctx);128 if (std.mem.eql(u8, cmd, "build")) return commands.build(ctx);129 if (std.mem.eql(u8, cmd, "run")) return commands.runCmd(ctx);130 if (std.mem.eql(u8, cmd, "hello")) return commands.hello(ctx);131 if (std.mem.eql(u8, cmd, "help")) { output.help(); return; }132133 errors.error_info.setMessage("unknown command: {s}", .{cmd});134 errors.error_info.setSuggestion("nina help", .{});135 return error.Internal;136}137138fn parseArgs(arena: std.mem.Allocator, argv: []const [:0]const u8) !Args {139 var result = Args{};140 var rest: std.ArrayList([]const u8) = .empty;141 var i: usize = 1; 142143 144 if (i < argv.len and argv[i][0] != '-') {145 result.command = argv[i];146 i += 1;147 }148149 150 if (i < argv.len and argv[i][0] != '-') {151 const multi_word = isMultiWord(result.command);152 if (multi_word) {153 result.sub = argv[i];154 i += 1;155 }156 }157158 while (i < argv.len) : (i += 1) {159 const a = argv[i];160 if (std.mem.eql(u8, a, "--on") or std.mem.eql(u8, a, "-on")) {161 i += 1;162 if (i < argv.len) result.machine = argv[i];163 } else if (std.mem.eql(u8, a, "--dry")) {164 result.dry = true;165 } else if (std.mem.eql(u8, a, "--check")) {166 result.check = true;167 } else if (std.mem.eql(u8, a, "--no-apply")) {168 result.no_apply = true;169 } else if (std.mem.eql(u8, a, "--all")) {170 result.all = true;171 } else if (std.mem.eql(u8, a, "--json")) {172 result.json = true;173 } else if (std.mem.eql(u8, a, "--last")) {174 i += 1;175 if (i < argv.len) result.last = std.fmt.parseInt(u32, argv[i], 10) catch 50;176 } else if (std.mem.eql(u8, a, "-f")) {177 178 try rest.append(arena, "-f");179 } else if (a.len > 0 and a[0] != '-') {180 try rest.append(arena, a);181 }182 183 }184185 result.rest = try rest.toOwnedSlice(arena);186 return result;187}188189fn isMultiWord(cmd: []const u8) bool {190 const multi = [_][]const u8{ "service", "flake", "store", "channel", "profile", "pkg", "gen", "develop", "edit" };191 for (multi) |m| {192 if (std.mem.eql(u8, cmd, m)) return true;193 }194 return false;195}196197test "parse args basic" {198 const gpa = std.testing.allocator;199 const argv = [_][:0]const u8{ "nina", "apply", "--dry" };200 const args = try parseArgs(gpa, &argv);201 defer gpa.free(args.rest);202 try std.testing.expectEqualStrings("apply", args.command);203 try std.testing.expect(args.dry);204}205206test "parse args with machine" {207 const gpa = std.testing.allocator;208 const argv = [_][:0]const u8{ "nina", "status", "--on", "build-server" };209 const args = try parseArgs(gpa, &argv);210 defer gpa.free(args.rest);211 try std.testing.expectEqualStrings("status", args.command);212 try std.testing.expectEqualStrings("build-server", args.machine.?);213}214215test "parse subcommand" {216 const gpa = std.testing.allocator;217 const argv = [_][:0]const u8{ "nina", "service", "list" };218 const args = try parseArgs(gpa, &argv);219 defer gpa.free(args.rest);220 try std.testing.expectEqualStrings("service", args.command);221 try std.testing.expectEqualStrings("list", args.sub);222}