authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-05-15 21:58:30 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-05-15 21:58:30 -07:00
log4cddefa42744d61067567b0b36b5d2bb376e5ae3
treef18968c939ce1efec4f4011f75cfcc6e62cb916d
parent655572fe75ac5172bd981922b3c66068855c9041
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

add forStruct


2 files changed, 65 insertions(+), 0 deletions(-)

src/lib.zig+64
...@@ -95,6 +95,62 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc:...@@ -95,6 +95,62 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc:
95 return (try forEnum(out, in, prompt, alloc, enum { y, n }, .y)) == .y;95 return (try forEnum(out, in, prompt, alloc, enum { y, n }, .y)) == .y;
96}96}
9797
98pub fn forStruct(out: anytype, in: anytype, comptime prompt: []const u8, comptime S: type, comptime name: std.meta.FieldEnum(S), items: []const S) !usize {
99 comptime std.debug.assert(@typeInfo(S) == .@"struct");
100 std.debug.assert(items.len > 0);
101
102 try out.print(comptime ansi.color.Fg(.Green, "? "), .{});
103 try out.print(comptime ansi.color.Bold(prompt ++ "\n"), .{});
104
105 const STDIN_FILENO = 0;
106 const TCSAFLUSH = 2;
107 const ECHO: std.os.linux.tcflag_t = @bitCast(std.os.linux.tc_lflag_t{ .ECHO = true });
108 const ICANON: std.os.linux.tcflag_t = @bitCast(std.os.linux.tc_lflag_t{ .ICANON = true });
109
110 var orig_termios: struct_termios = undefined;
111 _ = tcgetattr(STDIN_FILENO, &orig_termios);
112 defer _ = tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
113
114 var raw_termios: struct_termios = orig_termios;
115 raw_termios.c_lflag &= ~(ECHO | ICANON);
116 _ = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_termios);
117
118 var r: usize = 0;
119 _ = &r;
120 while (true) {
121 for (items, 0..) |itm, i| {
122 if (i == r) {
123 try out.print(comptime ansi.color.Fg(.Cyan, "> {s}\n"), .{@field(itm, @tagName(name))});
124 } else {
125 try out.print(" {s}\n", .{@field(itm, @tagName(name))});
126 }
127 }
128 switch (@as(ansi.ascii, @enumFromInt(try in.readByte()))) {
129 .LF => {
130 break;
131 },
132 .ESC => {
133 std.debug.assert(try in.readByte() == '[');
134 switch (try in.readByte()) {
135 'A' => { //up
136 r = if (r > 0) r - 1 else items.len - 1;
137 },
138 'B' => { //down
139 r += 1;
140 if (r == items.len) r = 0;
141 },
142 else => {},
143 }
144 },
145 else => {},
146 }
147 try clean(out, items.len);
148 }
149 try clean(out, items.len + 1);
150 _ = try answer(out, prompt, []const u8, "{s}", @field(items[r], @tagName(name)));
151 return r;
152}
153
98// pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T {154// pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T {
99// try out.print(comptime ansi.color.Fg(.Green, "? "), .{});155// try out.print(comptime ansi.color.Fg(.Green, "? "), .{});
100// try out.print(comptime ansi.color.Bold(prompt ++ " "), .{});156// try out.print(comptime ansi.color.Bold(prompt ++ " "), .{});
...@@ -134,3 +190,11 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc:...@@ -134,3 +190,11 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc:
134190
135// return value;191// return value;
136// }192// }
193
194extern fn tcgetattr(fd: c_int, termios_p: *struct_termios) c_int;
195extern fn tcsetattr(fd: c_int, optional_actions: c_int, termios_p: *const struct_termios) c_int;
196
197const impdef = @cImport({
198 @cInclude("termios.h");
199});
200const struct_termios = impdef.struct_termios;
test.zig+1
...@@ -8,4 +8,5 @@ test {...@@ -8,4 +8,5 @@ test {
8 _ = &inquirer.forEnum;8 _ = &inquirer.forEnum;
9 _ = &inquirer.forString;9 _ = &inquirer.forString;
10 _ = &inquirer.forConfirm;10 _ = &inquirer.forConfirm;
11 _ = &inquirer.forStruct;
11}12}