11 KiB
11 KiB
Implementation Quick Start
Day 1 Morning: Setup
1. Create Directory Structure (5 minutes)
cd /home/sear/Backlog/lib/zargs
mkdir -p src tests examples
2. Create Initial Files (5 minutes)
touch src/main.zig
touch src/ArgumentType.zig
touch src/errors.zig
touch tests/type_test.zig
touch build.zig
3. Setup build.zig (15 minutes)
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Library module
const zargs = b.addModule("zargs", .{
.root_source_file = b.path("src/main.zig"),
});
// Tests
const tests = b.addTest(.{
.root_source_file = b.path("tests/type_test.zig"),
.target = target,
.optimize = optimize,
});
tests.root_module.addImport("zargs", zargs);
const run_tests = b.addRunArtifact(tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_tests.step);
}
4. Verify Setup (2 minutes)
zig build test
# Should compile (no tests yet)
Day 1 Afternoon: ArgumentType (Phase 1.1)
Step 1: Write Test First (30 minutes)
File: tests/type_test.zig
const std = @import("std");
const testing = std.testing;
const ArgumentType = @import("ArgumentType.zig").ArgumentType;
test "ArgumentType.fromZigType - bool" {
const t = ArgumentType.fromZigType(bool);
try testing.expectEqual(.bool, t);
}
test "ArgumentType.fromZigType - u32" {
const t = ArgumentType.fromZigType(u32);
try testing.expectEqual(.u32, t);
}
test "ArgumentType.fromZigType - string" {
const t = ArgumentType.fromZigType([]const u8);
try testing.expectEqual(.string, t);
}
test "ArgumentType.fromZigType - optional unwraps" {
const t = ArgumentType.fromZigType(?u32);
try testing.expectEqual(.u32, t);
}
test "ArgumentType.matches - same types match" {
const t1 = ArgumentType.u32;
const t2 = ArgumentType.u32;
try testing.expect(t1.matches(t2));
}
test "ArgumentType.matches - different types don't match" {
const t1 = ArgumentType.u32;
const t2 = ArgumentType.bool;
try testing.expect(!t1.matches(t2));
}
Step 2: Implement ArgumentType (1.5 hours)
File: src/ArgumentType.zig
const std = @import("std");
pub const ArgumentType = enum {
bool,
u8, u16, u32, u64,
i8, i16, i32, i64,
string,
string_list,
enum_type,
/// Convert a Zig type to ArgumentType at compile time
pub fn fromZigType(comptime T: type) ArgumentType {
const info = @typeInfo(T);
return switch (info) {
.Bool => .bool,
.Int => |int| {
if (int.signedness == .unsigned) {
return switch (int.bits) {
8 => .u8,
16 => .u16,
32 => .u32,
64 => .u64,
else => @compileError("Unsupported unsigned int size: " ++
@typeName(T)),
};
} else {
return switch (int.bits) {
8 => .i8,
16 => .i16,
32 => .i32,
64 => .i64,
else => @compileError("Unsupported signed int size: " ++
@typeName(T)),
};
}
},
.Pointer => |ptr| {
if (ptr.size == .Slice) {
if (ptr.child == u8) return .string;
// Check for []const []const u8 (string list)
const child_info = @typeInfo(ptr.child);
if (child_info == .Pointer) {
const inner_ptr = child_info.Pointer;
if (inner_ptr.size == .Slice and inner_ptr.child == u8) {
return .string_list;
}
}
}
@compileError("Unsupported pointer type: " ++ @typeName(T));
},
.Enum => .enum_type,
.Optional => |opt| fromZigType(opt.child),
else => @compileError("Unsupported argument type: " ++ @typeName(T)),
};
}
/// Check if two ArgumentTypes are compatible
pub fn matches(self: ArgumentType, other: ArgumentType) bool {
return self == other;
}
};
// Compile-time tests
comptime {
_ = ArgumentType.fromZigType(bool);
_ = ArgumentType.fromZigType(u32);
_ = ArgumentType.fromZigType([]const u8);
_ = ArgumentType.fromZigType(?u32);
}
Step 3: Run Tests (5 minutes)
zig build test
# Should pass all tests
Step 4: Update build.zig for ArgumentType (5 minutes)
Add ArgumentType to the tests:
tests.root_module.addAnonymousImport("ArgumentType", .{
.root_source_file = b.path("src/ArgumentType.zig"),
});
Day 1 Success Criteria ✓
At end of Day 1, you should have:
- Project structure created
- build.zig working
- ArgumentType fully implemented
- All type detection tests passing
- Comptime tests verifying common types
Progress: ~10% complete, on track!
Day 2 Morning: ParsedValue (Phase 1.2)
Step 1: Write Tests
Add to tests/type_test.zig:
const ParsedValue = @import("ArgumentType.zig").ParsedValue;
test "ParsedValue.fromString - bool true" {
const allocator = testing.allocator;
const pv = try ParsedValue.fromString(.bool, "true", allocator);
defer pv.deinit(allocator);
try testing.expectEqual(true, pv.bool_val);
}
test "ParsedValue.fromString - u32" {
const allocator = testing.allocator;
const pv = try ParsedValue.fromString(.u32, "42", allocator);
defer pv.deinit(allocator);
try testing.expectEqual(@as(u32, 42), pv.u32_val);
}
test "ParsedValue.toTypedValue - u32" {
const allocator = testing.allocator;
const pv = try ParsedValue.fromString(.u32, "42", allocator);
defer pv.deinit(allocator);
const val = pv.toTypedValue(u32);
try testing.expectEqual(@as(u32, 42), val);
}
Step 2: Implement ParsedValue
Add to src/ArgumentType.zig:
pub const ParsedValue = union(ArgumentType) {
bool: bool,
u8: u8, u16: u16, u32: u32, u64: u64,
i8: i8, i16: i16, i32: i32, i64: i64,
string: []const u8,
string_list: []const []const u8,
enum_type: []const u8,
pub fn fromString(
arg_type: ArgumentType,
s: []const u8,
allocator: std.mem.Allocator,
) !ParsedValue {
return switch (arg_type) {
.bool => .{ .bool = try parseBool(s) },
.u8 => .{ .u8 = try std.fmt.parseInt(u8, s, 10) },
.u16 => .{ .u16 = try std.fmt.parseInt(u16, s, 10) },
.u32 => .{ .u32 = try std.fmt.parseInt(u32, s, 10) },
.u64 => .{ .u64 = try std.fmt.parseInt(u64, s, 10) },
.i8 => .{ .i8 = try std.fmt.parseInt(i8, s, 10) },
.i16 => .{ .i16 = try std.fmt.parseInt(i16, s, 10) },
.i32 => .{ .i32 = try std.fmt.parseInt(i32, s, 10) },
.i64 => .{ .i64 = try std.fmt.parseInt(i64, s, 10) },
.string => .{ .string = try allocator.dupe(u8, s) },
.string_list => .{ .string_list = try parseList(s, allocator) },
.enum_type => .{ .enum_type = try allocator.dupe(u8, s) },
};
}
pub fn toTypedValue(self: ParsedValue, comptime T: type) T {
const arg_type = ArgumentType.fromZigType(T);
return switch (arg_type) {
.bool => self.bool,
.u8 => self.u8,
.u16 => self.u16,
.u32 => self.u32,
.u64 => self.u64,
.i8 => self.i8,
.i16 => self.i16,
.i32 => self.i32,
.i64 => self.i64,
.string => self.string,
.string_list => self.string_list,
.enum_type => {
// For enums, need to convert string to enum at runtime
// This is a placeholder - full implementation in Phase 4
@compileError("Enum conversion not yet implemented");
},
};
}
pub fn deinit(self: ParsedValue, allocator: std.mem.Allocator) void {
switch (self) {
.string => |s| allocator.free(s),
.string_list => |list| {
for (list) |item| allocator.free(item);
allocator.free(list);
},
.enum_type => |s| allocator.free(s),
else => {},
}
}
};
fn parseBool(s: []const u8) !bool {
if (std.mem.eql(u8, s, "true") or std.mem.eql(u8, s, "1") or
std.mem.eql(u8, s, "yes")) {
return true;
} else if (std.mem.eql(u8, s, "false") or std.mem.eql(u8, s, "0") or
std.mem.eql(u8, s, "no")) {
return false;
}
return error.InvalidBooleanValue;
}
fn parseList(s: []const u8, allocator: std.mem.Allocator) ![]const []const u8 {
var list = std.ArrayList([]const u8).init(allocator);
errdefer {
for (list.items) |item| allocator.free(item);
list.deinit();
}
var iter = std.mem.splitScalar(u8, s, ',');
while (iter.next()) |item| {
const trimmed = std.mem.trim(u8, item, " \t");
try list.append(try allocator.dupe(u8, trimmed));
}
return try list.toOwnedSlice();
}
Step 3: Run Tests
zig build test
Momentum Tips
Keep Moving Forward:
- If stuck > 30 minutes: Skip to next task, come back later
- If test fails: Debug immediately, don't move on
- If design unclear: Implement simplest version, refactor later
- Commit often: After each green test
Daily Review (15 minutes EOD):
- What did I accomplish?
- What's blocking me?
- What's tomorrow's priority?
Weekly Review (30 minutes Friday):
- Am I on schedule?
- Do I need to adjust the plan?
- What did I learn?
Common Issues and Solutions
Issue: Comptime too complex
Solution: Move to runtime, optimize later
Issue: Memory leaks in tests
Solution: Add defer immediately after allocation
Issue: Type conversion not working
Solution: Check ArgumentType.fromZigType() logic
Issue: Tests not compiling
Solution: Check imports and build.zig configuration
Morale Boosters
- ✅ Each passing test is progress!
- ✅ Small commits compound into big features
- ✅ Taking breaks prevents burnout
- ✅ Asking for help is strength, not weakness
- ✅ Perfect is the enemy of done - ship it!
You've got this! 💪
Contact/Support
- Review design docs in
research/when unsure - Check
todo/implementation_plan_v2.mdfor detailed steps - Run
zig build testfrequently - Trust the process - you planned well!
START WITH DAY 1 MORNING. BUILD INCREMENTALLY. TEST EVERYTHING. 🚀