authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-07-29 03:58:50 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-07-29 03:58:50 -07:00
logbf61c37a36b9801148566e63e0cca4505e9b6547
tree1f6ea2e4b1ae77777764bca81db0f8774e26bfe6
parent687d38bea1d2080db17fa33cde1d528fd5962653

add initial code implementation


6 files changed, 202 insertions(+), 0 deletions(-)

.gitignore created+3
...@@ -0,0 +1,3 @@
1/zig-*
2/.zigmod
3/deps.zig
build.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2const deps = @import("./deps.zig");
3
4pub fn build(b: *std.build.Builder) void {
5 const target = b.standardTargetOptions(.{});
6
7 const mode = b.standardReleaseOptions();
8
9 const exe = b.addExecutable("zig-inquirer", "src/main.zig");
10 exe.setTarget(target);
11 exe.setBuildMode(mode);
12 deps.addAllTo(exe);
13 exe.install();
14
15 const run_cmd = exe.run();
16 run_cmd.step.dependOn(b.getInstallStep());
17 if (b.args) |args| {
18 run_cmd.addArgs(args);
19 }
20
21 const run_step = b.step("run", "Run the app");
22 run_step.dependOn(&run_cmd.step);
23}
src/lib.zig created+135
...@@ -0,0 +1,135 @@
1const std = @import("std");
2const ansi = @import("ansi");
3const range = @import("range").range;
4
5pub fn answer(comptime prompt: []const u8, comptime T: type, comptime valfmt: []const u8, value: T) T {
6 std.debug.print(comptime ansi.color.Fg(.Green, "? "), .{});
7 std.debug.print(comptime ansi.color.Bold(prompt ++ " "), .{});
8 std.debug.print(comptime ansi.color.Fg(.Cyan, valfmt ++ "\n"), .{value});
9 return value;
10}
11
12const PromptRet = struct {
13 n: usize,
14 value: []const u8,
15};
16
17fn doprompt(in: std.fs.File.Reader, alloc: *std.mem.Allocator, default: ?[]const u8) !PromptRet {
18 var n: usize = 1;
19 var value: []const u8 = undefined;
20 while (true) : (n += 1) {
21 std.debug.print(comptime ansi.color.Faint("> "), .{});
22 const input = try in.readUntilDelimiterAlloc(alloc, '\n', 100);
23
24 if (input.len == 0) if (default) |d| {
25 value = d;
26 break;
27 };
28 if (input.len > 0) {
29 value = input;
30 break;
31 }
32 }
33 return PromptRet{ .n = n, .value = value };
34}
35
36fn clean(n: usize) void {
37 for (range(n)) |_| {
38 std.debug.print(comptime ansi.csi.CursorUp(1), .{});
39 std.debug.print(comptime ansi.csi.EraseInLine(0), .{});
40 }
41}
42
43pub fn forEnum(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime options: type, default: ?options) !options {
44 comptime std.debug.assert(@typeInfo(options) == .Enum);
45
46 std.debug.print(comptime ansi.color.Fg(.Green, "? "), .{});
47 std.debug.print(comptime ansi.color.Bold(prompt ++ " "), .{});
48
49 std.debug.print(ansi.style.Faint ++ "(", .{});
50 inline for (std.meta.fields(options)) |f, i| {
51 if (i != 0) std.debug.print("/", .{});
52 std.debug.print(f.name, .{});
53 }
54 std.debug.print(")" ++ ansi.style.ResetIntensity ++ " ", .{});
55
56 const stdin = std.io.getStdIn().reader();
57 var value: options = undefined;
58 var i: usize = 0;
59 while (true) {
60 const def: ?[]const u8 = if (default) |d| @tagName(d) else null;
61 const p = try doprompt(stdin, alloc, def);
62 defer if (!std.mem.eql(u8, p.value, def orelse "")) alloc.free(p.value);
63
64 i += p.n;
65 if (std.meta.stringToEnum(options, p.value)) |cap| {
66 value = cap;
67 break;
68 }
69 }
70 clean(i);
71 _ = answer(prompt, []const u8, "{s}", @tagName(value));
72
73 return value;
74}
75
76pub fn forString(comptime prompt: []const u8, alloc: *std.mem.Allocator, default: ?[]const u8) ![]const u8 {
77 std.debug.print(comptime ansi.color.Fg(.Green, "? "), .{});
78 std.debug.print(comptime ansi.color.Bold(prompt ++ " "), .{});
79
80 if (default != null) {
81 std.debug.print(ansi.style.Faint ++ "(", .{});
82 std.debug.print("{s}", .{default.?});
83 std.debug.print(")" ++ ansi.style.ResetIntensity ++ " ", .{});
84 }
85
86 const stdin = std.io.getStdIn().reader();
87 const p = try doprompt(stdin, alloc, default);
88 clean(p.n);
89 return answer(prompt, []const u8, "{s}", p.value);
90}
91
92pub fn forConfirm(comptime prompt: []const u8, alloc: *std.mem.Allocator) !bool {
93 return (try forEnum(prompt, alloc, enum { y, n }, .y)) == .y;
94}
95
96// pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T {
97// std.debug.print(comptime ansi.color.Fg(.Green, "? "), .{});
98// std.debug.print(comptime ansi.color.Bold(prompt ++ " "), .{});
99
100// if (default != null) {
101// std.debug.print(ansi.style.Faint ++ "(", .{});
102// std.debug.print("{d}", .{default.?});
103// std.debug.print(")" ++ ansi.style.ResetIntensity ++ " ", .{});
104// }
105
106// const stdin = std.io.getStdIn().reader();
107// var value: T = undefined;
108// var i: usize = 0;
109// while (true) {
110// const n = if (default != null) try std.fmt.allocPrint(alloc, "{d}", .{default}) else null;
111// defer if (n != null) alloc.free(n.?);
112// const p = try doprompt(stdin, alloc, if (default != null) n else null);
113// defer alloc.free(p.value);
114// i += p.n;
115// switch (@typeInfo(T)) {
116// .Int => {
117// if (std.fmt.parseInt(T, p.value, 10) catch null) |cap| {
118// value = cap;
119// break;
120// }
121// },
122// .Float => {
123// if (std.fmt.parseFloat(T, p.value) catch null) |cap| {
124// value = cap;
125// break;
126// }
127// },
128// else => @compileError("expected number type instead got: " ++ @typeName(T)),
129// }
130// }
131// clean(i);
132// _ = answer(prompt, T, "{d}", value);
133
134// return value;
135// }
src/main.zig created+30
...@@ -0,0 +1,30 @@
1const std = @import("std");
2const inquirer = @import("inquirer");
3
4// Comparison adaptation of https://github.com/SBoudrias/Inquirer.js/blob/master/packages/inquirer/examples/pizza.js
5
6pub fn main() !void {
7 std.log.info("All your codebase are belong to us.", .{});
8
9 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
10 const alloc = &gpa.allocator;
11
12 _ = try inquirer.forConfirm("Is this for delivery?", alloc);
13
14 _ = try inquirer.forString("What's your phone number?", alloc, null);
15
16 _ = try inquirer.forEnum("What size do you need?", alloc, enum { Small, Medium, Large }, null);
17
18 // _ = try inquirer.forNumber("How many do you need?", alloc, u32, 1);
19 // TODO forNumber is causing a compiler crash
20
21 // TODO toppings for string list
22
23 _ = try inquirer.forEnum("You also get a free 2L:", alloc, enum { Pepsi, @"7up", Coke }, null);
24
25 const comment = try inquirer.forString("Any comments on your purchase experience?", alloc, "Nope, all good!");
26
27 if (std.mem.eql(u8, comment, "")) {
28 _ = try inquirer.forEnum("For leaving a comment, you get a freebie:", alloc, enum { Cake, Fries }, null);
29 }
30}
zig.mod created+8
...@@ -0,0 +1,8 @@
1id: c1xirp1ota5pzuzs3i1ixyci6jkkr7xekpi516f1e1238a9z
2name: inquirer
3main: src/lib.zig
4license: MIT
5description: A collection of utilities for prompting information from the user on the CLI
6dependencies:
7 - src: git https://github.com/nektro/zig-ansi
8 - src: git https://github.com/nektro/zig-range
zigmod.lock created+3
...@@ -0,0 +1,3 @@
12
2git https://github.com/nektro/zig-ansi commit-d4a53bcac5b87abecc65491109ec22aaf5f3dc2f
3git https://github.com/nektro/zig-range commit-890ca308fe09b3d5c866d5cfb3b3d7a95dbf939f