const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const build_config = @import("build_config.zig"); /// Special commands that can be invoked via CLI flags. These are all /// invoked by using `+` as a CLI flag. The only exception is /// "version" which can be invoked additionally with `--version`. pub const Action = enum { /// Output the version and exit version, pub const Error = error{ /// Multiple actions were detected. You can specify at most one /// action on the CLI otherwise the behavior desired is ambiguous. MultipleActions, /// An unknown action was specified. InvalidAction, }; /// Detect the action from CLI args. pub fn detectCLI(alloc: Allocator) !?Action { var iter = try std.process.argsWithAllocator(alloc); defer iter.deinit(); return try detectIter(&iter); } /// Detect the action from any iterator, used primarily for tests. pub fn detectIter(iter: anytype) Error!?Action { var pending: ?Action = null; while (iter.next()) |arg| { // Special case, --version always outputs the version no // matter what, no matter what other args exist. if (std.mem.eql(u8, arg, "--version")) return .version; // Commands must start with "+" if (arg.len == 0 or arg[0] != '+') continue; if (pending != null) return Error.MultipleActions; pending = std.meta.stringToEnum(Action, arg[1..]) orelse return Error.InvalidAction; } return pending; } /// Run the action. This returns the exit code to exit with. pub fn run(self: Action, alloc: Allocator) !u8 { _ = alloc; return switch (self) { .version => try runVersion(), }; } }; fn runVersion() !u8 { const stdout = std.io.getStdOut().writer(); try stdout.print("Ghostty {s}\n", .{build_config.version_string}); return 0; } test "parse action none" { const testing = std.testing; const alloc = testing.allocator; var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--a=42 --b --b-f=false", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action == null); } test "parse action version" { const testing = std.testing; const alloc = testing.allocator; { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--a=42 --b --b-f=false --version", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--version --a=42 --b --b-f=false", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--c=84 --d --version --a=42 --b --b-f=false", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } } test "parse action plus" { const testing = std.testing; const alloc = testing.allocator; { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--a=42 --b --b-f=false +version", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "+version --a=42 --b --b-f=false", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } { var iter = try std.process.ArgIteratorGeneral(.{}).init( alloc, "--c=84 --d +version --a=42 --b --b-f=false", ); defer iter.deinit(); const action = try Action.detectIter(&iter); try testing.expect(action.? == .version); } }