15 KiB
15 KiB
wttr.in Zig Rewrite Strategy
Goals
- Single binary - Replace Python + Go with one Zig executable
- No external binaries - Eliminate wego, pyphoon dependencies
- Maintain compatibility - All existing API endpoints work identically
- Improve performance - Faster response times, lower memory usage
- Add tests - Comprehensive test coverage from day one
- Simplify deployment - Single binary + data files
Non-Goals
- Rewriting weather APIs (still use met.no/WWO)
- Changing API surface (maintain backward compatibility)
- Rewriting geolocator service (keep as separate service for now)
Phase 1: Analysis & Design ✓
Status: Complete (this document)
Deliverables:
- ARCHITECTURE.md - System overview
- API_ENDPOINTS.md - API reference
- DATA_FLOW.md - Request processing flow
- REWRITE_STRATEGY.md - This document
Phase 2: Foundation (Week 1-2)
2.1 Project Setup
Tasks:
- Create
build.zigwith proper structure - Set up module organization
- Configure dependencies (if any)
- Set up test framework
- Create CI/CD pipeline (GitHub Actions)
Modules:
src/
├── main.zig # Entry point
├── server.zig # HTTP server
├── router.zig # Request routing
├── config.zig # Configuration
├── cache/
│ ├── lru.zig # LRU cache implementation
│ └── file.zig # File-backed cache
├── location/
│ ├── resolver.zig # Location resolution
│ ├── geoip.zig # GeoIP lookups
│ └── normalize.zig # Location normalization
├── weather/
│ ├── client.zig # Weather API client
│ ├── metno.zig # met.no API
│ └── wwo.zig # WorldWeatherOnline API
├── render/
│ ├── ansi.zig # ANSI rendering
│ ├── html.zig # HTML rendering
│ ├── json.zig # JSON rendering
│ ├── png.zig # PNG rendering
│ ├── line.zig # One-line format
│ └── v2.zig # v2 format
├── i18n/
│ ├── translations.zig # Translation system
│ └── loader.zig # Load translation files
└── utils/
├── http.zig # HTTP utilities
├── ip.zig # IP address handling
└── time.zig # Time utilities
2.2 Core HTTP Server
Tasks:
- Implement HTTP server using std.http.Server
- Request parsing (headers, query params, path)
- Response building (status, headers, body)
- Basic routing (exact match, wildcard)
- Static file serving
Tests:
- Parse GET requests
- Parse query parameters
- Route to handlers
- Serve static files
- Handle 404s
Acceptance Criteria:
- Server listens on configurable port
- Handles concurrent requests
- Routes to placeholder handlers
- Serves files from share/ directory
2.3 Configuration System
Tasks:
- Load environment variables
- Load config files (if needed)
- Validate configuration
- Provide defaults
Configuration:
const Config = struct {
listen_host: []const u8,
listen_port: u16,
geolite_path: []const u8,
cache_dir: []const u8,
log_level: LogLevel,
// API keys
ip2location_key: ?[]const u8,
ipinfo_token: ?[]const u8,
wwo_key: ?[]const u8,
};
Tests:
- Load from environment
- Apply defaults
- Validate paths exist
Phase 3: Caching Layer (Week 2-3)
3.1 LRU Cache
Tasks:
- Implement generic LRU cache
- Thread-safe operations (Mutex)
- TTL support
- Eviction policy
- Cache statistics
Tests:
- Insert and retrieve
- LRU eviction
- TTL expiration
- Concurrent access
- Memory limits
Acceptance Criteria:
- Configurable size (default 12,800)
- O(1) get/put operations
- Thread-safe
- TTL-based expiration
3.2 File Cache
Tasks:
- Store large responses to disk
- MD5 hash for filenames
- Read/write with proper locking
- Cleanup old files
Tests:
- Write and read files
- Handle binary data
- Concurrent access
- Disk space limits
3.3 Cache Integration
Tasks:
- Generate cache keys
- Check cache before processing
- Store responses after processing
- Handle InProgress flag (prevent thundering herd)
Tests:
- Cache hit returns cached response
- Cache miss processes request
- Concurrent requests for same key
- Cache key generation
Phase 4: Location Resolution (Week 3-4)
4.1 Location Parsing
Tasks:
- Parse location from URL
- Normalize location names
- Handle special prefixes (~, @)
- Parse cyclic locations (:)
- Load aliases file
- Load blacklist file
Tests:
- Parse city names
- Parse coordinates
- Parse IATA codes
- Parse special locations (Moon, etc.)
- Normalize names
- Apply aliases
- Check blacklist
4.2 GeoIP Lookup
Tasks:
- Read MaxMind GeoLite2 database
- IP to location lookup
- Cache results
Options:
- Use C library (libmaxminddb) via @cImport
- Or: Parse MMDB format in pure Zig
Tests:
- Lookup IPv4 address
- Lookup IPv6 address
- Handle not found
- Cache lookups
4.3 External Geolocation
Tasks:
- HTTP client for geolocator service
- Parse JSON responses
- Error handling
Tests:
- Query geolocator
- Parse response
- Handle errors
- Timeout handling
4.4 IP Location APIs
Tasks:
- IP2Location API client
- IPInfo API client
- File-based caching
- Fallback chain
Tests:
- Query each API
- Parse responses
- Cache results
- Fallback on error
Phase 5: Weather Data (Week 4-5)
5.1 HTTP Client
Tasks:
- Generic HTTP client
- Connection pooling
- Timeout handling
- Retry logic
- User-Agent setting
Tests:
- GET requests
- POST requests
- Headers
- Timeouts
- Retries
5.2 met.no Client
Tasks:
- API endpoint construction
- Parse XML/JSON responses
- Transform to internal format
- Error handling
Tests:
- Fetch weather data
- Parse response
- Handle API errors
- Handle rate limits
5.3 WorldWeatherOnline Client
Tasks:
- API endpoint construction
- Parse JSON responses
- Transform to internal format
- Caching (proxy cache)
Tests:
- Fetch weather data
- Parse response
- Handle API errors
- Cache responses
5.4 Weather Data Model
Tasks:
- Define internal weather data structure
- Conversion from met.no format
- Conversion from WWO format
- JSON serialization
Tests:
- Convert met.no data
- Convert WWO data
- Serialize to JSON
- Validate data
Phase 6: Rendering (Week 5-7)
6.1 ANSI Renderer
Tasks:
- Generate ANSI weather report
- ASCII art for weather conditions
- Color codes
- Box drawing characters
- Temperature graphs
- Wind direction arrows
Tests:
- Render current weather
- Render forecast
- Apply colors
- Handle narrow mode
- Handle inverted colors
Acceptance Criteria:
- Output matches wego format
- All weather conditions supported
- Configurable width
6.2 One-Line Renderer
Tasks:
- Parse format string
- Replace format codes (%c, %t, etc.)
- Handle predefined formats (1-4)
- Emoji support
Tests:
- Format 1-4
- Custom format strings
- All format codes
- Multiple locations
6.3 JSON Renderer
Tasks:
- Serialize weather data to JSON
- Match WWO API format
- Pretty printing
Tests:
- Serialize current conditions
- Serialize forecast
- Match expected format
6.4 HTML Renderer
Tasks:
- Convert ANSI to HTML
- Apply CSS styling
- Add interactive buttons
- Template system
Tests:
- Convert ANSI codes
- Apply colors
- Render buttons
- Template rendering
6.5 PNG Renderer
Tasks:
- Render ANSI to image
- Font rendering
- Color support
- Transparency
Options:
- Use C library (libpng, freetype) via @cImport
- Or: Pure Zig implementation (more work)
Tests:
- Render text
- Apply colors
- Apply transparency
- Handle fonts
6.6 v2 Renderer
Tasks:
- Data-rich format
- Temperature graphs
- Moon phases
- Astronomical times
Tests:
- Render all sections
- Handle different locations
- Match expected format
6.7 Prometheus Renderer
Tasks:
- Convert weather data to metrics
- Prometheus text format
- Metric naming
Tests:
- Render metrics
- Match Prometheus format
- All weather fields
Phase 7: Translation System (Week 7-8)
7.1 Translation Loader
Tasks:
- Load translation files
- Parse translation format
- Build translation tables
- Language detection
Tests:
- Load all language files
- Parse translations
- Detect language from header
- Detect language from subdomain
7.2 Message Translation
Tasks:
- Translate weather conditions
- Translate UI messages
- Fallback to English
Tests:
- Translate conditions
- Translate messages
- Handle missing translations
- Fallback logic
Phase 8: Request Processing (Week 8-9)
8.1 Query Parser
Tasks:
- Parse query parameters
- Parse single-letter options
- Parse PNG filenames
- Serialize/deserialize state
Tests:
- Parse all options
- Parse PNG filenames
- Serialize state
- Deserialize state
8.2 Request Handler
Tasks:
- Main request handler
- Fast path (cache + static)
- Full path (location + weather + render)
- Error handling
- Rate limiting
Tests:
- Handle cached requests
- Handle static pages
- Handle weather queries
- Handle errors
- Enforce rate limits
8.3 Rate Limiting
Tasks:
- Per-IP counters
- Time buckets (minute, hour, day)
- Whitelist support
- Return 429 on limit
Tests:
- Count requests
- Enforce limits
- Reset counters
- Whitelist bypass
Phase 9: Integration & Testing (Week 9-10)
9.1 End-to-End Tests
Tasks:
- Test all API endpoints
- Test all output formats
- Test all query options
- Test error cases
- Compare with Python output
Test Cases:
- Basic weather query
- Location resolution
- All output formats
- All languages
- PNG rendering
- Rate limiting
- Error handling
9.2 Performance Testing
Tasks:
- Benchmark request latency
- Benchmark throughput
- Memory profiling
- Cache hit rates
- Compare with Python/Go
Metrics:
- Requests per second
- Average latency
- P95/P99 latency
- Memory usage
- Cache hit rate
9.3 Compatibility Testing
Tasks:
- Run integration test (test/query.sh)
- Compare SHA1 hashes
- Fix any differences
- Document intentional changes
Phase 10: Deployment (Week 10-11)
10.1 Packaging
Tasks:
- Build release binary
- Package data files
- Create Docker image
- Write deployment docs
10.2 Migration Plan
Tasks:
- Deploy Zig version alongside Python/Go
- Route small percentage of traffic
- Monitor errors and performance
- Gradually increase traffic
- Full cutover
Rollback Plan:
- Keep Python/Go running
- Route traffic back if issues
- Fix issues in Zig version
- Retry migration
10.3 Documentation
Tasks:
- Update README
- Installation instructions
- Configuration guide
- API documentation
- Development guide
Risks & Mitigations
Risk: PNG Rendering Complexity
Mitigation:
- Start with external library (libpng, freetype)
- Consider pure Zig later if needed
- Or: Keep PNG rendering in separate service
Risk: GeoIP Database Parsing
Mitigation:
- Use C library (libmaxminddb) initially
- Consider pure Zig parser later
- Well-documented format
Risk: ANSI Rendering Differences
Mitigation:
- Extensive testing against wego output
- Pixel-perfect comparison for PNG
- Accept minor differences if functionally equivalent
Risk: Performance Regression
Mitigation:
- Benchmark early and often
- Profile hot paths
- Optimize critical sections
- Compare with Python/Go baseline
Risk: Translation File Parsing
Mitigation:
- Simple format (key: value)
- Robust parser with error handling
- Validate all files on startup
Risk: Weather API Changes
Mitigation:
- Abstract API clients
- Version API responses
- Monitor for changes
- Fallback to other API
Success Criteria
Functional
- All API endpoints work
- All output formats match
- All languages supported
- All query options work
- Integration tests pass
Performance
- Latency < Python/Go
- Throughput > Python/Go
- Memory < Python/Go
- Binary size < 10MB
Quality
- >80% test coverage
- No memory leaks
- No crashes under load
- Clean error handling
Operational
- Single binary deployment
- No external dependencies (except data files)
- Easy configuration
- Good logging
- Metrics/monitoring
Timeline
Total: 10-11 weeks
- Week 1-2: Foundation
- Week 2-3: Caching
- Week 3-4: Location
- Week 4-5: Weather
- Week 5-7: Rendering
- Week 7-8: Translation
- Week 8-9: Integration
- Week 9-10: Testing
- Week 10-11: Deployment
Milestones:
- Week 2: HTTP server running, basic routing
- Week 4: Location resolution working
- Week 6: Weather data fetching working
- Week 8: ANSI rendering working
- Week 10: All features complete, testing done
- Week 11: Deployed to production
Alternative: Incremental Approach
Instead of full rewrite, replace components incrementally:
Option A: Replace Go Proxy First
- Rewrite Go proxy in Zig (Week 1-2)
- Keep Python backend
- Test and deploy
- Then rewrite Python backend (Week 3-11)
Advantages:
- Smaller initial scope
- Faster time to value
- De-risks Zig HTTP handling
- Can abandon if issues
Option B: Replace Python Backend First
- Keep Go proxy
- Rewrite Python backend in Zig (Week 1-10)
- Test and deploy
- Then replace Go proxy (Week 11)
Advantages:
- Most complexity in backend
- Proves out rendering logic
- Can keep Go proxy if it works well
Recommendation: Option A
Start with Go proxy replacement:
- Smaller scope (400 lines vs 5000+)
- Clear interface boundary
- Tests caching/HTTP in Zig
- Quick win (2 weeks)
- De-risks larger rewrite
Next Steps
- Review this document - Get feedback on approach
- Choose strategy - Full rewrite vs incremental
- Set up project - Create build.zig, directory structure
- Start coding - Begin with HTTP server or Go proxy replacement
- Iterate - Build, test, refine
Questions to Answer
- Use C libraries (libpng, freetype, libmaxminddb) or pure Zig?
- Keep geolocator as separate service or integrate?
- Keep wego/pyphoon or rewrite rendering?
- Full rewrite or incremental replacement?
- Target Zig version (0.11, 0.12, 0.13)?
- Async I/O strategy (std.event, manual, blocking)?