refactor: Broke comma into a separate package.

This commit is contained in:
2026-04-20 17:08:20 -04:00
parent a0cbaaddf1
commit 5a3bc605c7
4 changed files with 90 additions and 74 deletions

View File

@@ -21,6 +21,11 @@ pub fn build(b: *std.Build) void {
// target and optimize options) will be listed when running `zig build --help`
// in this directory.
const comma = b.addModule("comma", .{
.root_source_file = b.path("src/comma.zig"),
.target = target,
});
// This creates a module, which represents a collection of source files alongside
// some compilation options, such as optimization mode and linked system libraries.
// Zig modules are the preferred way of making Zig code available to consumers.
@@ -39,6 +44,9 @@ pub fn build(b: *std.Build) void {
// Later on we'll use this module as the root module of a test executable
// which requires us to specify a target.
.target = target,
.imports = &.{
.{ .name = "comma", .module = comma },
},
});
// Here we define an executable. An executable needs to have a root module
@@ -78,6 +86,7 @@ pub fn build(b: *std.Build) void {
// repeated because you are allowed to rename your imports, which
// can be extremely useful in case of collisions (which can happen
// importing modules from different packages).
.{ .name = "comma", .module = comma },
.{ .name = "envr", .module = mod },
},
}),

75
src/comma.zig Normal file
View File

@@ -0,0 +1,75 @@
//! By convention, root.zig is the root source file when making a package.
const std = @import("std");
// const Io = std.Io;
pub const Command = struct {
name: []const u8,
short: ?[]const u8 = null,
long: ?[]const u8 = null,
subcommands: []const Command = &[0]Command{},
Type: type,
pub fn new(cmd: CommandOptions) Command {
const subcommands: [cmd.subcommands.len]Command = blk: {
var result: [cmd.subcommands.len]Command = undefined;
inline for (cmd.subcommands, 0..) |sub, idx| {
result[idx] = new(sub);
}
break :blk result;
};
return .{
.name = cmd.name,
.short = cmd.short,
.long = cmd.long,
.subcommands = &subcommands,
.Type = cmd.as_enum(),
};
}
pub fn parse(comptime self: @This(), args: []const []const u8) ParseError!self.Type {
if (args.len == 0) {
return ParseError.InvalidType;
}
const target = args[0];
inline for (self.subcommands, 1..) |cmd, idx| {
if (std.mem.eql(u8, target, cmd.name)) {
return @enumFromInt(idx);
}
}
return @enumFromInt(0);
}
};
pub const ParseError = error{
InvalidType,
};
const CommandOptions = struct {
name: []const u8,
short: ?[]const u8 = null,
long: ?[]const u8 = null,
subcommands: []const CommandOptions = &[0]CommandOptions{},
fn as_enum(self: @This()) type {
var field_names: [self.subcommands.len + 1][]const u8 = undefined;
var field_values: [self.subcommands.len + 1]u32 = undefined;
field_names[0] = self.name;
field_values[0] = 0;
inline for (self.subcommands, 1..) |cmd, idx| {
field_names[idx] = cmd.name;
field_values[idx] = idx;
}
return @Enum(
u32,
.exhaustive,
&field_names,
&field_values,
);
}
};

View File

@@ -3,6 +3,7 @@ const Io = std.Io;
const config = @import("config");
const comma = @import("comma");
const envr = @import("envr");
const goBinary = "envr-go";
@@ -22,7 +23,7 @@ fn run(io: Io, args: []const [:0]const u8) !void {
switch (cmd) {
.envr => {
// TODO: Print help
return envr.ParseError.InvalidType;
return comma.ParseError.InvalidType;
},
.version => {
var stdout_buffer: [1024]u8 = undefined;

View File

@@ -2,83 +2,14 @@
const std = @import("std");
const Io = std.Io;
const comma = @import("comma");
const Command = comma.Command;
pub const root: Command = .new(.{
.name = "envr",
.subcommands = &[_]CommandOptions{.{ .name = "version" }},
.subcommands = &.{.{ .name = "version" }},
});
const CommandOptions = struct {
name: []const u8,
short: ?[]const u8 = null,
long: ?[]const u8 = null,
subcommands: []const CommandOptions = &[0]CommandOptions{},
fn as_enum(self: @This()) type {
var field_names: [self.subcommands.len + 1][]const u8 = undefined;
var field_values: [self.subcommands.len + 1]u32 = undefined;
field_names[0] = self.name;
field_values[0] = 0;
inline for (self.subcommands, 1..) |cmd, idx| {
field_names[idx] = cmd.name;
field_values[idx] = idx;
}
return @Enum(
u32,
.exhaustive,
&field_names,
&field_values,
);
}
};
pub const Command = struct {
name: []const u8,
short: ?[]const u8 = null,
long: ?[]const u8 = null,
subcommands: []const Command = &[0]Command{},
Type: type,
pub fn new(cmd: CommandOptions) Command {
const subcommands: [cmd.subcommands.len]Command = blk: {
var result: [cmd.subcommands.len]Command = undefined;
inline for (cmd.subcommands, 0..) |sub, idx| {
result[idx] = new(sub);
}
break :blk result;
};
return .{
.name = cmd.name,
.short = cmd.short,
.long = cmd.long,
.subcommands = &subcommands,
.Type = cmd.as_enum(),
};
}
pub fn parse(comptime self: @This(), args: []const []const u8) ParseError!self.Type {
if (args.len == 0) {
return ParseError.InvalidType;
}
const target = args[0];
inline for (self.subcommands, 1..) |cmd, idx| {
if (std.mem.eql(u8, target, cmd.name)) {
return @enumFromInt(idx);
}
}
return @enumFromInt(0);
}
};
pub const ParseError = error{
InvalidType,
};
test "enum type" {
const got: root.Type = @enumFromInt(1);