Backlog/lib/zargs/todo/implementation_plan_v2.md

12 KiB

Implementation Plan v2 - Refined

Critical Changes from v1

  1. Add string handling strategy early (Phase 1.3)
  2. Define error types upfront (Phase 1.4)
  3. Emphasize test-driven development throughout
  4. Clarify memory ownership at every step
  5. Add missing helpers (kebab-case conversion, etc.)

Phase 1: Foundation (Week 1: Days 1-3)

1.1 ArgumentType Enum

File: src/ArgumentType.zig Duration: 1 day

  • Define ArgumentType enum
  • Implement fromZigType(comptime T: type) ArgumentType
  • Implement matches(self, other) bool
  • TESTS: Type detection for all supported types

Key Decision: Support ?T by unwrapping to underlying type


1.2 ParsedValue Union

File: src/ArgumentType.zig Duration: 1 day

  • Define ParsedValue tagged union
  • Implement fromString(type, string, allocator) !ParsedValue
  • Implement toTypedValue(comptime T: type, parsed) T
  • TESTS: Conversions for all types, error cases

Key Decision: Allocate strings into caller-provided arena


1.3 String Handling Strategy

File: src/utils.zig Duration: 0.5 days

  • Implement toKebabCase(comptime name: []const u8) []const u8
    • Convert camelCase/snake_case to kebab-case
    • Comptime function, returns comptime string
  • Document string ownership model:
    • Arena owns all parsed strings
    • Comptime strings (field names, literals) not duplicated
    • Runtime strings (argv) duplicated into arena
  • TESTS: kebab-case conversion edge cases

Key Decision: Use arena allocator for all dynamic strings


1.4 Error Type Definitions

File: src/errors.zig Duration: 0.5 days

  • Define comprehensive error set:
pub const Error = error{
    IncompatibleArgumentType,
    UnknownArgument,
    InvalidValue,
    InvalidIntegerValue,
    InvalidBooleanValue,
    InvalidEnumValue,
    MissingArgumentValue,
    OutOfMemory,
};
  • Document when each error occurs
  • Consider error payloads for context

Key Decision: Separate error type allows clear API contracts


Phase 2: Metadata Extraction (Week 1: Days 4-5)

2.1 Metadata Structures

File: src/metadata.zig Duration: 0.5 days

  • Define ArgumentMetadata struct
  • Define ModuleInfo struct
  • Define FieldMeta (what goes in pub const meta = .{...})
  • Document structure ownership

2.2 Comptime Metadata Extraction

File: src/metadata.zig Duration: 1.5 days

  • extractFieldMetadata(comptime T: type, comptime field: Field) FieldMeta
    • Get T.meta.field_name if exists
    • Generate defaults for missing fields
    • Convert field name to kebab-case
    • Extract default value
  • extractDocComment(comptime T: type, comptime field_name: []const u8) []const u8
    • Return empty for now (future: parse doc comments)
  • formatDefaultValue(comptime T: type, value: T, allocator) ![]const u8
    • Format bool, int, string, enum, list
  • TESTS: Metadata extraction with various struct configurations

Key Decision: All metadata extraction is comptime


Phase 3: Core Registry (Week 2: Days 6-9)

3.1 Registry Structure

File: src/ArgumentRegistry.zig Duration: 1 day

  • Define struct with all fields
  • Implement init(allocator) ArgumentRegistry
  • Implement deinit()
  • TESTS: Init/deinit, memory leak detection

Key Decision: Use StringHashMap for O(1) lookups


3.2 argv Caching

File: src/ArgumentRegistry.zig Duration: 0.5 days

  • Cache argv on first access
  • Implement isHelpRequested() bool
  • TESTS: Help detection, caching behavior

Key Decision: Registry owns argv memory


3.3 Metadata Registration with Collision Detection

File: src/ArgumentRegistry.zig Duration: 2 days

  • registerMetadata(comptime T: type, opts: ParseOptions) !void
    • Create/get module entry
    • For each field:
      • Extract metadata
      • Check for existing argument
      • If exists and types match: warn, add module
      • If exists and types differ: error with locations
      • If new: store metadata
  • Implement collision detection logic
  • Format error messages with source locations
  • TESTS: Compatible collisions, incompatible collisions, error messages

Key Decision: Source locations captured via @src(), stored as-is (compile-time strings)


3.4 Struct Tracking

File: src/ArgumentRegistry.zig Duration: 0.5 days

  • Track parsed structs by type name
  • Skip re-registration if already parsed
  • TESTS: Multiple parse calls with same struct

Phase 4: Argument Parsing (Week 2-3: Days 10-14)

4.1 Argv Parsing Infrastructure

File: src/parsing.zig Duration: 1.5 days

  • parseArgv() !void
    • Iterate cached argv
    • Dispatch to appropriate parser
  • parseArg(arg: []const u8) !void
    • Handle --long=value
    • Handle --long value
    • Handle --flag (bool)
  • parseShortArg(short: u8) !void
    • Look up by short name
    • Handle value/flag
  • TESTS: All argument formats, unknown arguments

Key Decision: Duplicate parsed strings into arena


4.2 Value Parsing with List Support

File: src/parsing.zig Duration: 1.5 days

  • Parse integers with range checking
  • Parse booleans (true/false, 1/0, yes/no)
  • Parse strings (already strings, but duplicate)
  • Parse lists:
    • Split on comma
    • Also support repeated args: --list=a --list=b
    • Accumulate into single list
  • Parse enums (stringToEnum)
  • TESTS: All types, edge cases, error conditions

Key Decision: Support both comma-separated and repeated arguments for lists


4.3 Struct Reconstruction with Type Safety

File: src/ArgumentRegistry.zig Duration: 1 day

  • reconstructStruct(comptime T: type) T
    • For each field:
      • Get parsed value by long name
      • Convert to field type with comptime assertions
      • Fall back to default if not provided
      • Handle ?T (optional) types
  • Runtime type checking for safety
  • TESTS: Struct reconstruction, optional fields, defaults

Key Decision: Comptime type checks prevent runtime type errors


4.4 Main parse() Integration

File: src/ArgumentRegistry.zig Duration: 1 day

  • parse(comptime T: type, opts: ParseOptions) !T
    • Check parsed_structs
    • If new: registerMetadata, parseArgv
    • reconstructStruct and return
    • Mark as parsed
  • TESTS: Full end-to-end parsing, multiple structs

Key Decision: Single function handles everything


Phase 5: Help Generation (Week 3: Days 15-16)

5.1 Help Text Generation

File: src/help.zig Duration: 1 day

  • getUsageAlloc(allocator) ![]const u8
    • Write header
    • Write global options (--help)
    • For each module:
      • Write module name
      • For each argument:
        • Format -s, --long <VALUE> Help text [default: X]
    • Calculate alignment for readability
  • TESTS: Help text format, alignment, grouping

Key Decision: Generate fresh each time (acceptable performance)


Phase 6: Public API (Week 3-4: Day 17)

6.1 Module Exports and Documentation

File: src/main.zig Duration: 1 day

  • Export all public types
  • Add top-level module documentation
  • Define version constant
  • Document global registry pattern
  • TESTS: Ensure exports are accessible

Phase 7: Comprehensive Testing (Week 4: Days 18-21)

7.1 Unit Test Coverage

Duration: 2 days

  • Achieve 100% coverage of:
    • Type detection and conversion
    • Metadata extraction
    • Collision detection
    • Parsing logic
    • Struct reconstruction
    • Help generation
  • Test error paths
  • Test edge cases

7.2 Integration Tests

Duration: 1 day

  • Multi-module scenarios
  • Parse order independence
  • Help text workflow
  • Persistence workflow

7.3 Memory and Safety Tests

Duration: 1 day

  • Memory leak detection on all tests
  • Test cleanup on error paths
  • Arena allocator correctness
  • Stress tests (many arguments, large values)

Phase 8: Examples and Documentation (Week 5: Days 22-24)

8.1 Examples

Duration: 2 days

  • examples/simple.zig - Basic usage
  • examples/game_engine.zig - Multi-module
  • examples/persistence.zig - Help text persistence
  • Ensure all examples compile and run

8.2 Documentation

Duration: 1 day

  • Write comprehensive README
  • Document all public APIs
  • Add usage examples to doc comments
  • Document design decisions and tradeoffs

Phase 9: Build and Integration (Week 5: Day 25)

9.1 Build System

Duration: 0.5 days

  • Configure build.zig
  • Test, example, and install steps
  • Verify Zig 0.14 compatibility

9.2 Engine Integration

Duration: 0.5 days

  • Import into Backlog engine
  • Test with actual engine modules
  • Document engine-specific usage

Phase 10: Polish (Week 5: Day 25)

10.1 Final Review

Duration: 0.5 days

  • Review all error messages
  • Run formatter
  • Check for TODOs
  • Verify no memory leaks
  • Performance check

Daily Checklist Template

For each day of implementation:

  • Write tests FIRST for new functionality
  • Implement feature
  • Ensure tests pass
  • Check for memory leaks
  • Update documentation
  • Commit with clear message

Test-Driven Development Workflow

  1. Write failing test - Define expected behavior
  2. Implement minimum - Make test pass
  3. Refactor - Improve code quality
  4. Repeat - Next feature

Memory Ownership Rules

Simple Rules:

  1. Registry owns: argv, all parsed strings (via arena)
  2. Caller owns: allocator passed to registry
  3. Comptime owns: field names, type names, meta strings
  4. Return values: Structs contain pointers into registry arena
    • Valid until registry.deinit()
    • Document this lifetime requirement

Rule of Thumb:

  • If it comes from argv → duplicate into arena
  • If it's comptime → use as-is
  • If it's dynamically formatted → allocate from arena

Success Metrics

  • All tests pass (100% coverage target)
  • Zero memory leaks detected
  • All examples compile and run
  • Documentation complete and clear
  • Integration with Backlog engine successful
  • Collision detection works correctly
  • Help generation produces readable output
  • Can demonstrate persistence workflow

Open Questions Resolved

  1. Positional arguments? No, deferred to v2
  2. Config files? No, separate feature
  3. Environment variables? No, separate feature
  4. Shell completion? No, v2 feature
  5. Validators? No, v2 feature

All features deferred to maintain focus on core use case.


Confidence Level: 95%

Why higher:

  • Addressed string handling explicitly
  • Clarified memory ownership model
  • Emphasized TDD approach
  • Defined error types upfront
  • Covered missing utility functions

Remaining concerns:

  • Comptime complexity (will discover during Phase 2)
  • Edge cases in parsing (will catch with comprehensive tests)

Mitigation:

  • Build incrementally
  • Test each component in isolation
  • Integration test early (Phase 7)

Go/No-Go Decision Points

After Phase 1 (Day 3):

Check: Type system working correctly?

  • If yes: proceed
  • If no: revisit type design

After Phase 2 (Day 5):

Check: Metadata extraction compiling and working?

  • If yes: proceed
  • If no: simplify metadata approach

After Phase 4 (Day 14):

Check: Full parse cycle working end-to-end?

  • If yes: proceed to polish
  • If no: debug integration issues

After Phase 7 (Day 21):

Check: All tests passing, no leaks?

  • If yes: ready for production
  • If no: fix issues before release

Implementation Notes

  • Keep each file under 500 lines
  • Prefer clarity over cleverness
  • Document all comptime behavior
  • Write tests for every public function
  • Use meaningful error messages
  • Follow Zig style guide

Ready to implement! 🚀