top_drives/docs/ARCHITECTURE.md
Jan-Peter von Hunnius 89fa69e3c8 refactor: extract lib.rs, remove club module, add bounded concurrency
- Extract shared types (Car, BestCarRecord, AnalysisContext) into src/lib.rs
- Replace global static BEST_CARS with injected AnalysisContext for testability
- Add semaphore-bounded concurrency (limit 20) in parse_all_times()
- Replace hardcoded track name match with track_names.json loaded via include_str!
- Replace magic number 115 with const MAX_RQ: u8 = 113
- Improve floating-point tie comparison with TIME_TOLERANCE (5ms) constant
- Remove src/club.rs binary (topdrives.club no longer maintained)
- Remove scraper dependency from Cargo.toml
- Remove tracks.txt (replaced by track_names.json)
- Remove empty/placeholder test bodies
- Improve error handling in get_file() for response body read
- Add load_my_cars_data() combining owned + wishlist extraction in one I/O
- Update CHANGELOG, README, and all docs to reflect changes
2026-05-06 20:20:15 +02:00

217 lines
7.1 KiB
Markdown

# Architecture Documentation
## System Overview
The Top Drives Records Analyzer consists of a main analysis binary and a shared library working together to provide comprehensive car performance analysis for the Top Drives racing game.
## Component Architecture
### 1. Records Module (`src/records.rs`)
**Primary analysis engine** that processes pre-downloaded JSON timing data.
#### Key Responsibilities
- **Data Fetching**: Downloads car and track databases from GitHub repository
- **File Processing**: Parses JSON timing files for each car configuration
- **Performance Analysis**: Compares timing data across RQ ranges and track conditions
- **Collection Management**: Identifies owned cars and optimization opportunities
#### Key Constants
- `MAX_RQ: u8 = 113` — Maximum RQ level used for range analysis (replaces former magic number 115)
#### Data Flow
```
GitHub Repository
↓
cars_repo.json + tracks_repo.json
↓
times/*.json (1000+ car files)
↓
JSON Parsing → BestCarRecord Analysis
↓
User Collection (my_cars.json)
↓
Output: CSV/Terminal Results
```
### 2. Shared Library (`src/lib.rs`)
**Common utilities** shared across the project.
#### Key Responsibilities
- **Track Name Translation**: Converts track codes to human-readable names via `translate_track_name()`
- **Track Type Translation**: Converts condition codes to surface descriptions via `translate_track_type()`
#### Configuration
- Track name mappings are loaded from `track_names.json` at compile time using `include_str!`
- This external config file maps track codes (e.g., `"laguna"`) to human-readable names (e.g., `"Laguna Seca"`)
## Data Structures
### Core Types
#### TripletKey
```rust
type TripletKey = (String, u8, String);
// ↓ ↓ ↓
// Track ID RQ Level Surface Condition
```
**Purpose**: Composite key for uniquely identifying track conditions
**Usage**: HashMap key for storing best car records
#### Car
```rust
struct Car {
rid: String, // Repository ID
brand: String, // Manufacturer
only_name: String, // Model name
year: u16, // Model year
rq: u8, // Rarity level
prize: bool, // Prize car status
guid: String, // Game GUID
}
```
#### BestCarRecord
```rust
struct BestCarRecord {
car_rid: String, // Car identifier
brand: String, // Manufacturer
only_name: String, // Model name
year: u16, // Year
rq: u8, // RQ level
config: String, // Tune config (111, 233, etc.)
time: f64, // Track time (seconds)
owned: bool, // User ownership status
guid: String, // Game identifier
}
```
## Global State Management
### BEST_CARS Cache
```rust
static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>
```
**Thread Safety**: Protected by `Mutex` for concurrent access during parsing
**Initialization**: Uses `once_cell::Lazy` for one-time setup
**Structure**: HashMap with composite keys for efficient lookups
## Algorithm Details
### RQ Range Analysis
The system analyzes car performance across multiple RQ brackets:
1. **Base RQ**: Car's natural rarity level
2. **RQ Range**: Tests from car's RQ up to `MAX_RQ` (113)
3. **Cross-RQ Competition**: Allows cars to compete above their base level
**Example**: A RQ 85 car might perform well at RQ 90-95 levels against higher-rated cars
### Time Validation
Multiple validation layers ensure data quality:
1. **Minimum Time**: Times below 1.2 seconds are discarded
2. **Downvote Filter**: Community-downvoted times are excluded
3. **Special Cases**: Test Bowl track uses inverted time calculation (2000.0 / time)
### Concurrent Processing
#### Bounded Concurrency Pattern
- **Task Spawning**: One async task per car file
- **Semaphore**: Limits concurrent operations to 20 (bounded concurrency)
- **Error Isolation**: Individual car failures don't stop batch processing
#### Synchronization
- **Join Handles**: Waits for all parsing tasks to complete
- **Mutex Protection**: Safe concurrent access to shared BEST_CARS cache
## File Organization
### Input Files
```
├── cars_repo.json # Car database from repository
├── tracks_repo.json # Track definitions
├── track_names.json # Track code → name mappings (compiled in)
├── my_cars.json # User's car collection (game export)
└── times/ # Individual car timing files
├── Ferrari_F40_1987.json
├── McLaren_F1_1993.json
└── ...
```
### Output Files
```
├── best.csv # Best cars analysis results
├── details.csv # Detailed timing data
├── add.csv # Cars to add to wishlist
├── remove.csv # Cars to remove from wishlist
├── trash.csv # Owned cars safe to remove
├── sorted.csv # Best cars sorted by count
├── very_best.csv # Best cars with count ≥ 10
└── wishlist.csv # Missing cars already on wishlist
```
## Performance Characteristics
### Records Module
- **Startup Time**: Fast (uses cached JSON files)
- **Memory Usage**: Moderate (loads all timing data into memory)
- **Accuracy**: High (comprehensive dataset)
- **Update Frequency**: Manual refresh required
- **Concurrency**: Bounded at 20 simultaneous operations via semaphore
## Error Handling Strategy
### Graceful Degradation
- **Missing Files**: Continue with available data
- **Network Errors**: Log errors but don't crash
- **Parse Failures**: Use default values or skip invalid entries
### User Feedback
- **Progress Indication**: Shows fetching/download progress
- **Error Messages**: Clear descriptions of what went wrong
## Extension Points
### Adding New Tracks
1. Update `tracks_repo.json` with new track definitions
2. Add track name mapping in `track_names.json`
3. Add condition mappings in `translate_track_type()`
### Adding New Analysis Types
1. Create new output functions (similar to `print_best_cars()`)
2. Add command parsing in main function
3. Update help text and documentation
## Security Considerations
### Network Access
- **HTTPS Only**: All external requests use HTTPS
- **Certificate Validation**: Accepts invalid certificates for compatibility
- **Rate Limiting**: Respects server limits with controlled concurrency (semaphore limit of 20)
### Local File Access
- **Read Only**: No modification of game files
- **User Data**: Only reads exported collection data
- **No Persistence**: Analysis results are output-only
## Future Enhancements
### Potential Improvements
1. **Caching Layer**: Cache parsed results between runs
2. **Database Backend**: Replace in-memory HashMap with persistent storage
3. **Configuration Files**: External configuration for analysis parameters
4. **GUI Interface**: Web-based interface for non-technical users
5. **Export Formats**: Multiple output formats (JSON, XML, etc.)
### Scalability Considerations
1. **Pagination**: Handle large datasets with pagination
2. **Incremental Updates**: Only fetch changed data
3. **Compression**: Compress large timing files
4. **Streaming**: Process data without loading everything into memory