mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Add --version for outputting version, framework for more actions
This commit is contained in:
107
src/cli_action.zig
Normal file
107
src/cli_action.zig
Normal file
@ -0,0 +1,107 @@
|
||||
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 `+<action>` 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 null;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
35
src/main.zig
35
src/main.zig
@ -1,10 +1,12 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const build_config = @import("build_config.zig");
|
||||
const options = @import("build_options");
|
||||
const glfw = @import("glfw");
|
||||
const macos = @import("macos");
|
||||
const tracy = @import("tracy");
|
||||
const cli_action = @import("cli_action.zig");
|
||||
const internal_os = @import("os/main.zig");
|
||||
const xev = @import("xev");
|
||||
const fontconfig = @import("fontconfig");
|
||||
@ -32,6 +34,38 @@ pub fn main() !void {
|
||||
defer state.deinit();
|
||||
const alloc = state.alloc;
|
||||
|
||||
// Before we do anything else, we need to check for special commands
|
||||
// via the CLI flags.
|
||||
if (cli_action.Action.detectCLI(alloc)) |action_| {
|
||||
if (action_) |action| {
|
||||
std.log.info("executing CLI action={}", .{action});
|
||||
std.os.exit(action.run(alloc) catch |err| err: {
|
||||
std.log.err("CLI action failed error={}", .{err});
|
||||
break :err 1;
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else |err| {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
defer std.os.exit(1);
|
||||
const ErrSet = @TypeOf(err) || error{Unknown};
|
||||
switch (@as(ErrSet, @errSetCast(err))) {
|
||||
error.MultipleActions => try stderr.print(
|
||||
"Error: multiple CLI actions specified. You must specify only one\n" ++
|
||||
"action starting with the `+` character.\n",
|
||||
.{},
|
||||
),
|
||||
|
||||
error.InvalidAction => try stderr.print(
|
||||
"Error: unknown CLI action specified. CLI actions are specified with\n" ++
|
||||
"the '+' character.\n",
|
||||
.{},
|
||||
),
|
||||
|
||||
else => try stderr.print("invalid CLI invocation err={}\n", .{err}),
|
||||
}
|
||||
}
|
||||
|
||||
// Create our app state
|
||||
var app = try App.create(alloc);
|
||||
defer app.destroy();
|
||||
@ -187,6 +221,7 @@ test {
|
||||
_ = @import("renderer.zig");
|
||||
_ = @import("termio.zig");
|
||||
_ = @import("input.zig");
|
||||
_ = @import("cli_action.zig");
|
||||
|
||||
// Libraries
|
||||
_ = @import("segmented_pool.zig");
|
||||
|
Reference in New Issue
Block a user