Skip to content

Commit 5fcffa7

Browse files
chore: wip
1 parent 212ca0f commit 5fcffa7

26 files changed

Lines changed: 430 additions & 193 deletions

build.zig

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,71 @@
11
const std = @import("std");
22

33
pub fn build(b: *std.Build) void {
4-
_ = b.standardTargetOptions(.{});
5-
_ = b.standardOptimizeOption(.{});
4+
const target = b.standardTargetOptions(.{});
5+
const optimize = b.standardOptimizeOption(.{});
66

7-
// Create a module that can be imported by other projects
8-
_ = b.addModule("zig-cli", .{
7+
// ── Library module (public, exposable to dependents) ────────────────
8+
const zig_cli = b.addModule("zig-cli", .{
99
.root_source_file = b.path("src/root.zig"),
10+
.target = target,
11+
.optimize = optimize,
1012
});
13+
14+
// ── Tests ───────────────────────────────────────────────────────────
15+
const test_module = b.createModule(.{
16+
.root_source_file = b.path("src/root.zig"),
17+
.target = target,
18+
.optimize = optimize,
19+
});
20+
const lib_tests = b.addTest(.{
21+
.root_module = test_module,
22+
});
23+
const run_lib_tests = b.addRunArtifact(lib_tests);
24+
25+
const test_step = b.step("test", "Run library tests");
26+
test_step.dependOn(&run_lib_tests.step);
27+
28+
// ── Examples ────────────────────────────────────────────────────────
29+
const example_names = [_][]const u8{
30+
"simple",
31+
"basic",
32+
"typed",
33+
"advanced",
34+
"prompts",
35+
"showcase",
36+
"config",
37+
};
38+
39+
const examples_step = b.step("examples", "Build all examples");
40+
41+
for (example_names) |name| {
42+
const example_module = b.createModule(.{
43+
.root_source_file = b.path(b.fmt("examples/{s}.zig", .{name})),
44+
.target = target,
45+
.optimize = optimize,
46+
.imports = &.{
47+
.{ .name = "zig-cli", .module = zig_cli },
48+
},
49+
});
50+
51+
const example = b.addExecutable(.{
52+
.name = name,
53+
.root_module = example_module,
54+
});
55+
56+
const install_example = b.addInstallArtifact(example, .{});
57+
examples_step.dependOn(&install_example.step);
58+
59+
const run_example = b.addRunArtifact(example);
60+
run_example.step.dependOn(&install_example.step);
61+
if (b.args) |args| {
62+
run_example.addArgs(args);
63+
}
64+
65+
const run_step = b.step(
66+
b.fmt("run-{s}", .{name}),
67+
b.fmt("Run the {s} example", .{name}),
68+
);
69+
run_step.dependOn(&run_example.step);
70+
}
1171
}

build.zig.zon

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.{
2+
.name = .zig_cli,
3+
.version = "0.1.0",
4+
.minimum_zig_version = "0.14.0",
5+
.paths = .{
6+
"build.zig",
7+
"build.zig.zon",
8+
"src",
9+
"examples",
10+
"README.md",
11+
},
12+
.fingerprint = 0xf544320ed4f9c4d7,
13+
}

examples/advanced.zig

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const std = @import("std");
22
const cli = @import("zig-cli");
33

4-
fn createAction(ctx: *cli.Command.ParseContext) !void {
5-
const stdout = std.io.getStdOut().writer();
4+
fn createAction(ctx: *cli.BaseCommand.ParseContext) !void {
5+
var buf: [4096]u8 = undefined;
6+
var file_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &buf);
7+
const stdout = &file_writer.interface;
68

79
const name = ctx.getArgument(0) orelse return error.MissingName;
810
const template = ctx.getOption("template") orelse "default";
@@ -11,10 +13,13 @@ fn createAction(ctx: *cli.Command.ParseContext) !void {
1113
try stdout.print("Creating project: {s}\n", .{name});
1214
try stdout.print("Template: {s}\n", .{template});
1315
try stdout.print("Force: {s}\n", .{if (force) "yes" else "no"});
16+
try stdout.flush();
1417
}
1518

16-
fn buildAction(ctx: *cli.Command.ParseContext) !void {
17-
const stdout = std.io.getStdOut().writer();
19+
fn buildAction(ctx: *cli.BaseCommand.ParseContext) !void {
20+
var buf: [4096]u8 = undefined;
21+
var file_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &buf);
22+
const stdout = &file_writer.interface;
1823

1924
const mode = ctx.getOption("mode") orelse "debug";
2025
const optimize_str = ctx.getOption("optimize") orelse "false";
@@ -29,10 +34,13 @@ fn buildAction(ctx: *cli.Command.ParseContext) !void {
2934
while (ctx.getArgument(i)) |target| : (i += 1) {
3035
try stdout.print("Target: {s}\n", .{target});
3136
}
37+
try stdout.flush();
3238
}
3339

34-
fn testAction(ctx: *cli.Command.ParseContext) !void {
35-
const stdout = std.io.getStdOut().writer();
40+
fn testAction(ctx: *cli.BaseCommand.ParseContext) !void {
41+
var buf: [4096]u8 = undefined;
42+
var file_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &buf);
43+
const stdout = &file_writer.interface;
3644

3745
const filter = ctx.getOption("filter");
3846
const verbose = ctx.hasOption("verbose");
@@ -42,24 +50,25 @@ fn testAction(ctx: *cli.Command.ParseContext) !void {
4250
try stdout.print("Filter: {s}\n", .{f});
4351
}
4452
try stdout.print("Verbose: {s}\n", .{if (verbose) "yes" else "no"});
53+
try stdout.flush();
4554
}
4655

47-
pub fn main() !void {
48-
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
49-
defer _ = gpa.deinit();
50-
const allocator = gpa.allocator();
56+
pub fn main(init: std.process.Init) !void {
57+
const allocator = init.gpa;
5158

52-
// Create CLI application
53-
var app = try cli.CLI.init(
59+
// Create root command
60+
const root_cmd = try cli.BaseCommand.init(
5461
allocator,
5562
"advanced-cli",
56-
"2.0.0",
5763
"An advanced CLI application demonstrating complex features",
5864
);
59-
defer app.deinit();
65+
defer {
66+
root_cmd.deinit();
67+
allocator.destroy(root_cmd);
68+
}
6069

6170
// Create 'create' command
62-
const create_cmd = try cli.Command.init(allocator, "create", "Create a new project");
71+
const create_cmd = try cli.BaseCommand.init(allocator, "create", "Create a new project");
6372

6473
const create_name_arg = cli.Argument.init("name", "Project name", .string);
6574
_ = try create_cmd.addArgument(create_name_arg);
@@ -74,10 +83,10 @@ pub fn main() !void {
7483
_ = try create_cmd.addOption(force_opt);
7584

7685
_ = create_cmd.setAction(createAction);
77-
_ = try app.command(create_cmd);
86+
_ = try root_cmd.addCommand(create_cmd);
7887

7988
// Create 'build' command
80-
const build_cmd = try cli.Command.init(allocator, "build", "Build the project");
89+
const build_cmd = try cli.BaseCommand.init(allocator, "build", "Build the project");
8190

8291
const mode_opt = cli.Option.init("mode", "mode", "Build mode (debug/release)", .string)
8392
.withShort('m')
@@ -94,10 +103,10 @@ pub fn main() !void {
94103
_ = try build_cmd.addArgument(targets_arg);
95104

96105
_ = build_cmd.setAction(buildAction);
97-
_ = try app.command(build_cmd);
106+
_ = try root_cmd.addCommand(build_cmd);
98107

99108
// Create 'test' command
100-
const test_cmd = try cli.Command.init(allocator, "test", "Run tests");
109+
const test_cmd = try cli.BaseCommand.init(allocator, "test", "Run tests");
101110

102111
const filter_opt = cli.Option.init("filter", "filter", "Filter tests by name", .string)
103112
.withShort('f');
@@ -108,11 +117,17 @@ pub fn main() !void {
108117
_ = try test_cmd.addOption(verbose_opt);
109118

110119
_ = test_cmd.setAction(testAction);
111-
_ = try app.command(test_cmd);
112-
113-
// Parse command line arguments
114-
const args = try std.process.argsAlloc(allocator);
115-
defer std.process.argsFree(allocator, args);
120+
_ = try root_cmd.addCommand(test_cmd);
121+
122+
// Collect command line arguments
123+
var args_list = std.ArrayList([]const u8){};
124+
defer args_list.deinit(allocator);
125+
var args_iter = std.process.Args.Iterator.init(init.minimal.args);
126+
_ = args_iter.skip(); // skip program name
127+
while (args_iter.next()) |arg| {
128+
try args_list.append(allocator, arg);
129+
}
116130

117-
try app.parse(args);
131+
var parser = cli.Parser.init(allocator);
132+
try parser.parse(root_cmd, args_list.items);
118133
}

examples/basic.zig

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
11
const std = @import("std");
22
const cli = @import("zig-cli");
33

4-
fn greetAction(ctx: *cli.Command.ParseContext) !void {
4+
fn greetAction(ctx: *cli.BaseCommand.ParseContext) !void {
55
const name = ctx.getOption("name") orelse "World";
66
const count_str = ctx.getOption("count") orelse "1";
77
const count = try std.fmt.parseInt(usize, count_str, 10);
88

9-
const stdout = std.io.getStdOut().writer();
9+
var buf: [4096]u8 = undefined;
10+
var file_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &buf);
11+
const stdout = &file_writer.interface;
1012

1113
var i: usize = 0;
1214
while (i < count) : (i += 1) {
1315
try stdout.print("Hello, {s}!\n", .{name});
1416
}
17+
try stdout.flush();
1518
}
1619

17-
fn infoAction(ctx: *cli.Command.ParseContext) !void {
18-
const stdout = std.io.getStdOut().writer();
20+
fn infoAction(ctx: *cli.BaseCommand.ParseContext) !void {
21+
var buf: [4096]u8 = undefined;
22+
var file_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &buf);
23+
const stdout = &file_writer.interface;
1924
try stdout.print("This is the info command!\n", .{});
2025
try stdout.print("Command: {s}\n", .{ctx.command_name});
26+
try stdout.flush();
2127
}
2228

23-
pub fn main() !void {
24-
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
25-
defer _ = gpa.deinit();
26-
const allocator = gpa.allocator();
29+
pub fn main(init: std.process.Init) !void {
30+
const allocator = init.gpa;
2731

28-
// Create CLI application
29-
var app = try cli.CLI.init(allocator, "example", "1.0.0", "An example CLI application");
30-
defer app.deinit();
32+
// Create root command
33+
const root_cmd = try cli.BaseCommand.init(allocator, "example", "An example CLI application");
34+
defer {
35+
root_cmd.deinit();
36+
allocator.destroy(root_cmd);
37+
}
3138

3239
// Add options to root command
3340
const name_option = cli.Option.init("name", "name", "Your name", .string)
@@ -38,21 +45,27 @@ pub fn main() !void {
3845
.withShort('c')
3946
.withDefault("1");
4047

41-
_ = try app.option(name_option);
42-
_ = try app.option(count_option);
48+
_ = try root_cmd.addOption(name_option);
49+
_ = try root_cmd.addOption(count_option);
4350

4451
// Set action for root command
45-
_ = app.action(greetAction);
52+
_ = root_cmd.setAction(greetAction);
4653

4754
// Create a subcommand
48-
const info_cmd = try cli.Command.init(allocator, "info", "Show information");
55+
const info_cmd = try cli.BaseCommand.init(allocator, "info", "Show information");
4956
_ = info_cmd.setAction(infoAction);
5057

51-
_ = try app.command(info_cmd);
58+
_ = try root_cmd.addCommand(info_cmd);
5259

53-
// Parse command line arguments
54-
const args = try std.process.argsAlloc(allocator);
55-
defer std.process.argsFree(allocator, args);
60+
// Collect command line arguments
61+
var args_list = std.ArrayList([]const u8){};
62+
defer args_list.deinit(allocator);
63+
var args_iter = std.process.Args.Iterator.init(init.minimal.args);
64+
_ = args_iter.skip(); // skip program name
65+
while (args_iter.next()) |arg| {
66+
try args_list.append(allocator, arg);
67+
}
5668

57-
try app.parse(args);
69+
var parser = cli.Parser.init(allocator);
70+
try parser.parse(root_cmd, args_list.items);
5871
}

0 commit comments

Comments
 (0)