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
This commit is contained in:
Jan-Peter von Hunnius 2026-05-06 20:20:15 +02:00
parent 6a68469239
commit 89fa69e3c8
14 changed files with 874 additions and 2037 deletions

View file

@ -2,7 +2,7 @@
## System Overview
The Top Drives Records Analyzer consists of two main components working together to provide comprehensive car performance analysis for the Top Drives racing game.
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
@ -16,6 +16,9 @@ The Top Drives Records Analyzer consists of two main components working together
- **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
```
@ -32,28 +35,17 @@ User Collection (my_cars.json)
Output: CSV/Terminal Results
```
### 2. Club Module (`src/club.rs`)
### 2. Shared Library (`src/lib.rs`)
**Alternative data source** that scrapes live data from topdrives.club website.
**Common utilities** shared across the project.
#### Key Responsibilities
- **Web Scraping**: Fetches HTML content from topdrives.club
- **HTML Parsing**: Extracts car performance data using CSS selectors
- **Real-time Data**: Provides fresh data without pre-downloaded files
- **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()`
#### Data Flow
```
topdrives.club Website
↓
HTML Content
↓
CSS Selector Parsing
↓
Car Data Extraction
↓
Output: Terminal Results
```
#### 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
@ -69,7 +61,7 @@ type TripletKey = (String, u8, String);
**Purpose**: Composite key for uniquely identifying track conditions
**Usage**: HashMap key for storing best car records
#### Car (Records Module)
#### Car
```rust
struct Car {
rid: String, // Repository ID
@ -97,18 +89,6 @@ struct BestCarRecord {
}
```
#### Car (Club Module)
```rust
struct Car {
class: String, // Class letter (S, A, B, etc.)
rq: u8, // Rarity level
manufacturer: String, // Brand
model: String, // Model
year: u16, // Year
upgrade: String, // Upgrade config
}
```
## Global State Management
### BEST_CARS Cache
@ -127,7 +107,7 @@ static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>
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 maximum (115)
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
@ -142,9 +122,9 @@ Multiple validation layers ensure data quality:
### Concurrent Processing
#### Records Module
#### Bounded Concurrency Pattern
- **Task Spawning**: One async task per car file
- **Rate Limiting**: Semaphore limits concurrent downloads (20 max)
- **Semaphore**: Limits concurrent operations to 20 (bounded concurrency)
- **Error Isolation**: Individual car failures don't stop batch processing
#### Synchronization
@ -157,6 +137,7 @@ Multiple validation layers ensure data quality:
```
├── 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
@ -167,8 +148,13 @@ Multiple validation layers ensure data quality:
### Output Files
```
├── best.csv # Best cars analysis results
├── missing.csv # Missing recommended cars
└── details.csv # Detailed timing data
├── 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
@ -178,12 +164,7 @@ Multiple validation layers ensure data quality:
- **Memory Usage**: Moderate (loads all timing data into memory)
- **Accuracy**: High (comprehensive dataset)
- **Update Frequency**: Manual refresh required
### Club Module
- **Startup Time**: Slow (web scraping required)
- **Memory Usage**: Low (processes one track at a time)
- **Accuracy**: Variable (depends on website structure)
- **Update Frequency**: Real-time on each run
- **Concurrency**: Bounded at 20 simultaneous operations via semaphore
## Error Handling Strategy
@ -195,13 +176,12 @@ Multiple validation layers ensure data quality:
### User Feedback
- **Progress Indication**: Shows fetching/download progress
- **Error Messages**: Clear descriptions of what went wrong
- **Fallback Options**: Multiple data sources available
## Extension Points
### Adding New Tracks
1. Update `tracks_repo.json` with new track definitions
2. Add track name mappings in `translate_track_name()`
2. Add track name mapping in `track_names.json`
3. Add condition mappings in `translate_track_type()`
### Adding New Analysis Types
@ -209,17 +189,12 @@ Multiple validation layers ensure data quality:
2. Add command parsing in main function
3. Update help text and documentation
### Custom Data Sources
1. Implement new scraping module (similar to club.rs)
2. Add data source selection logic
3. Update configuration system
## 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
- **Rate Limiting**: Respects server limits with controlled concurrency (semaphore limit of 20)
### Local File Access
- **Read Only**: No modification of game files
@ -229,7 +204,7 @@ Multiple validation layers ensure data quality:
## Future Enhancements
### Potential Improvements
1. **Caching Layer**: Cache web scraping results
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
@ -239,4 +214,4 @@ Multiple validation layers ensure data quality:
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
4. **Streaming**: Process data without loading everything into memory