- 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
7.1 KiB
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.jsonat compile time usinginclude_str! - This external config file maps track codes (e.g.,
"laguna") to human-readable names (e.g.,"Laguna Seca")
Data Structures
Core Types
TripletKey
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
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
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
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:
- Base RQ: Car's natural rarity level
- RQ Range: Tests from car's RQ up to
MAX_RQ(113) - 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:
- Minimum Time: Times below 1.2 seconds are discarded
- Downvote Filter: Community-downvoted times are excluded
- 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
- Update
tracks_repo.jsonwith new track definitions - Add track name mapping in
track_names.json - Add condition mappings in
translate_track_type()
Adding New Analysis Types
- Create new output functions (similar to
print_best_cars()) - Add command parsing in main function
- 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
- Caching Layer: Cache parsed results between runs
- Database Backend: Replace in-memory HashMap with persistent storage
- Configuration Files: External configuration for analysis parameters
- GUI Interface: Web-based interface for non-technical users
- Export Formats: Multiple output formats (JSON, XML, etc.)
Scalability Considerations
- Pagination: Handle large datasets with pagination
- Incremental Updates: Only fetch changed data
- Compression: Compress large timing files
- Streaming: Process data without loading everything into memory