487 lines
12 KiB
Markdown
487 lines
12 KiB
Markdown
# 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:
|
|
```zig
|
|
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!** 🚀
|