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:
parent
6a68469239
commit
89fa69e3c8
14 changed files with 874 additions and 2037 deletions
166
docs/API.md
166
docs/API.md
|
|
@ -23,7 +23,7 @@ async fn main()
|
|||
**Purpose**: Command-line interface and operation dispatcher
|
||||
|
||||
**Arguments**: Command line arguments
|
||||
- `args[1]`: Command (`fetch`, `best`, `missing`, `trash`, `details`, `track`)
|
||||
- `args[1]`: Command (`fetch`, `best`, `missing`, `trash`, `details`, `track`, `full_script`)
|
||||
|
||||
**Commands**:
|
||||
- `fetch`: Download all data files
|
||||
|
|
@ -32,6 +32,7 @@ async fn main()
|
|||
- `trash`: Show removable cars
|
||||
- `details`: Export all timing data
|
||||
- `track`: Track-specific analysis
|
||||
- `full_script`: Run fetch + all analysis commands
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
|
|
@ -148,7 +149,7 @@ async fn parse_all_times()
|
|||
|
||||
**Purpose**: Coordinates parallel processing of car timing files
|
||||
|
||||
**Concurrency**: One async task per car file
|
||||
**Concurrency**: One async task per car file, bounded by a semaphore (limit 20)
|
||||
**Synchronization**: Waits for all tasks to complete
|
||||
|
||||
##### `update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCarRecord)`
|
||||
|
|
@ -161,12 +162,12 @@ fn update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCa
|
|||
**Purpose**: Implements core best-car selection algorithm
|
||||
|
||||
**RQ Range Analysis**:
|
||||
- Tests car from base RQ up to maximum (115)
|
||||
- Tests car from base RQ up to `MAX_RQ` (113)
|
||||
- Compares against existing best times
|
||||
- Updates records for improved performance
|
||||
|
||||
**Logic**:
|
||||
1. For each RQ level from car_rq to 115:
|
||||
1. For each RQ level from car_rq to `MAX_RQ` (113):
|
||||
2. If new time is better → replace record
|
||||
3. If new time ties → add to list (if not duplicate)
|
||||
4. If new time is worse → ignore
|
||||
|
|
@ -205,7 +206,7 @@ async fn fetch_times()
|
|||
|
||||
**Purpose**: Batch download of car timing data
|
||||
|
||||
**Rate Limiting**: 20 concurrent downloads maximum
|
||||
**Rate Limiting**: 20 concurrent downloads maximum (semaphore)
|
||||
**Progress**: Shows download progress for each file
|
||||
|
||||
##### `fetch_car(rid: String)`
|
||||
|
|
@ -258,6 +259,10 @@ async fn get_file(file_url: &str, local_file_path: &str)
|
|||
- Error handling with graceful failure
|
||||
- Progress indication through errors
|
||||
|
||||
### Shared Library (`src/lib.rs`)
|
||||
|
||||
Common utilities shared across the project.
|
||||
|
||||
##### `translate_track_name(track_code: &str) -> &str`
|
||||
**Converts track codes to human-readable names**
|
||||
|
||||
|
|
@ -267,7 +272,9 @@ fn translate_track_name(track_code: &str) -> &str
|
|||
|
||||
**Purpose**: User-friendly track name display
|
||||
|
||||
**Coverage**: 200+ track mappings
|
||||
**Implementation**: Loads mappings from `track_names.json` at compile time via `include_str!`
|
||||
|
||||
**Coverage**: All tracks defined in `track_names.json`
|
||||
**Fallback**: Returns original code if not found
|
||||
|
||||
##### `translate_track_type(track_code: &str) -> &str`
|
||||
|
|
@ -299,89 +306,6 @@ fn split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a s
|
|||
split_prefix_postfix("laguna_a00", "_a") // Some(("laguna", "00"))
|
||||
```
|
||||
|
||||
### Club Module (`src/club.rs`)
|
||||
|
||||
Alternative data source using web scraping from topdrives.club.
|
||||
|
||||
#### Public Functions
|
||||
|
||||
##### `main()`
|
||||
**Entry point for club scraper**
|
||||
|
||||
```rust
|
||||
#[tokio::main]
|
||||
async fn main()
|
||||
```
|
||||
|
||||
**Purpose**: Web scraping interface for live data
|
||||
|
||||
**Arguments**:
|
||||
- Command line arguments for RQ and track filtering
|
||||
- `-d` flag for detailed output
|
||||
|
||||
##### `get_page(url: &str) -> String`
|
||||
**Fetches HTML content from URLs**
|
||||
|
||||
```rust
|
||||
async fn get_page(url: &str) -> String
|
||||
```
|
||||
|
||||
**Purpose**: Downloads web pages for scraping
|
||||
|
||||
**Security**: Accepts invalid SSL certificates for compatibility
|
||||
|
||||
##### `parse_best_car(text: String) -> Car`
|
||||
**Extracts car data from HTML content**
|
||||
|
||||
```rust
|
||||
fn parse_best_car(text: String) -> Car
|
||||
```
|
||||
|
||||
**Purpose**: Parses HTML tables to extract car information
|
||||
|
||||
**CSS Selectors**:
|
||||
- `.carList__item-manufacturer`: Car brand
|
||||
- `.carList__item-model`: Car model
|
||||
- `.carList__item-year`: Model year
|
||||
- `.carList__item-level`: Class and RQ (complex parsing)
|
||||
- `.carList__item-upgrade`: Upgrade configuration
|
||||
|
||||
##### `parse_tracks(file_name: &str) -> HashMap<u16, String>`
|
||||
**Loads track definitions from file**
|
||||
|
||||
```rust
|
||||
fn parse_tracks(file_name: &str) -> HashMap<u16, String>
|
||||
```
|
||||
|
||||
**Purpose**: Creates track ID to name mappings
|
||||
|
||||
**Format**: Text file with `ID NAME` format per line
|
||||
|
||||
##### `get_best_car(track: u16, track_name: String, max_level: u16, detailed: bool) -> String`
|
||||
**Scrapes best car data for specific track**
|
||||
|
||||
```rust
|
||||
async fn get_best_car(track: u16, track_name: String, max_level: u16, detailed: bool) -> String
|
||||
```
|
||||
|
||||
**Purpose**: Fetches and parses best car from topdrives.club
|
||||
|
||||
**API Integration**:
|
||||
- Constructs topdrives.club API URLs
|
||||
- Parses HTML response tables
|
||||
- Formats output based on detail level
|
||||
|
||||
##### `parse_argument(args: &Vec<String>, index: usize, min: u16, max: u16) -> u16`
|
||||
**Validates command line arguments**
|
||||
|
||||
```rust
|
||||
fn parse_argument(args: &Vec<String>, index: usize, min: u16, max: u16) -> u16
|
||||
```
|
||||
|
||||
**Purpose**: Input validation with range checking
|
||||
|
||||
**Panics**: On invalid values with descriptive messages
|
||||
|
||||
## Data Structures
|
||||
|
||||
### Records Module Data Types
|
||||
|
|
@ -436,22 +360,15 @@ type TripletKey = (String, u8, String);
|
|||
// Track ID RQ Level Surface Condition
|
||||
```
|
||||
|
||||
### Club Module Data Types
|
||||
|
||||
#### `Car`
|
||||
**Scraped car data from website**
|
||||
### Constants
|
||||
|
||||
#### `MAX_RQ`
|
||||
```rust
|
||||
struct Car {
|
||||
class: String, // Class letter (S, A, B, etc.)
|
||||
rq: u8, // Rarity level
|
||||
manufacturer: String, // Brand name
|
||||
model: String, // Model name
|
||||
year: u16, // Model year
|
||||
upgrade: String, // Upgrade config
|
||||
}
|
||||
const MAX_RQ: u8 = 113;
|
||||
```
|
||||
|
||||
**Purpose**: Maximum RQ level for range analysis. Replaces former magic number 115 with a named constant.
|
||||
|
||||
## Global State
|
||||
|
||||
### `BEST_CARS` Cache
|
||||
|
|
@ -533,7 +450,7 @@ fetch_times().await;
|
|||
- **Per Car**: ~1-2KB metadata + timing data
|
||||
|
||||
### Concurrency
|
||||
- **Downloads**: 20 concurrent connections max
|
||||
- **Downloads**: 20 concurrent connections max (semaphore bounded)
|
||||
- **Processing**: One task per car file
|
||||
- **Thread Safety**: Mutex-protected shared state
|
||||
|
||||
|
|
@ -544,41 +461,16 @@ fetch_times().await;
|
|||
2. Implement analysis function
|
||||
3. Update help documentation
|
||||
|
||||
### Adding New Data Sources
|
||||
1. Implement new module (similar to `club.rs`)
|
||||
2. Add data source selection logic
|
||||
3. Update configuration system
|
||||
### Adding New Tracks
|
||||
1. Update `tracks_repo.json` with new track definitions
|
||||
2. Add track name mapping to `track_names.json`
|
||||
3. Track names are compiled in at build time
|
||||
|
||||
### Custom Analysis Types
|
||||
1. Create new output formatting functions
|
||||
2. Add aggregation algorithms
|
||||
3. Extend data structures as needed
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_parse_car_times() {
|
||||
// Test implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
- Full workflow testing
|
||||
- File I/O validation
|
||||
- Network request mocking
|
||||
|
||||
### Benchmarking
|
||||
- Parse performance measurement
|
||||
- Memory usage profiling
|
||||
- Concurrent operation testing
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Network Security
|
||||
|
|
@ -643,12 +535,14 @@ cargo flamegraph --bin records -- best
|
|||
- **Future**: Semantic versioning maintained
|
||||
|
||||
### Breaking Changes
|
||||
- None currently planned
|
||||
- Advance notice provided for API changes
|
||||
- Deprecation warnings for removed features
|
||||
- Club module (`src/club.rs`) has been removed
|
||||
- `scraper` dependency removed
|
||||
- `tracks.txt` removed (replaced by `track_names.json`)
|
||||
- Magic number 115 replaced with `const MAX_RQ: u8 = 113`
|
||||
- `translate_track_name()` now loads from `track_names.json` instead of hardcoded match
|
||||
|
||||
---
|
||||
|
||||
**API Version**: 0.2.0
|
||||
**Last Updated**: 2024-12-XX
|
||||
**Compatibility**: Rust 1.70.0+
|
||||
**Last Updated**: 2026-05-06
|
||||
**Compatibility**: Rust 1.70.0+
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -175,6 +175,48 @@ This document describes the JSON data structures used by the Top Drives Records
|
|||
- Used for track-specific analysis queries
|
||||
- Supports condition filtering
|
||||
|
||||
### 5. Track Names Configuration (`track_names.json`)
|
||||
|
||||
**Purpose**: Maps internal track codes to human-readable track names.
|
||||
|
||||
**Source**: Included in the repository, loaded at compile time via `include_str!`
|
||||
|
||||
**Structure**:
|
||||
```json
|
||||
{
|
||||
"laguna": "Laguna Seca",
|
||||
"monaco": "Monaco Streets",
|
||||
"testBowl": "Test Bowl",
|
||||
"dealsGap": "Deals Gap"
|
||||
}
|
||||
```
|
||||
|
||||
**Field Descriptions**:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| *key* | String | Internal track code (e.g., `"laguna"`) |
|
||||
| *value* | String | Human-readable track name (e.g., `"Laguna Seca"`) |
|
||||
|
||||
**Usage in Application**:
|
||||
- Loaded by `translate_track_name()` in `src/lib.rs`
|
||||
- Compiled into the binary at build time
|
||||
- Fallback: Returns original track code if no mapping found
|
||||
|
||||
## Application Constants
|
||||
|
||||
### `MAX_RQ`
|
||||
|
||||
```rust
|
||||
const MAX_RQ: u8 = 113;
|
||||
```
|
||||
|
||||
**Purpose**: Defines the maximum RQ level used in analysis range calculations.
|
||||
|
||||
**Usage**: When evaluating car performance, each car is tested from its base RQ up to `MAX_RQ`. This replaces the former magic number 115, providing a named constant for clarity and easy adjustment.
|
||||
|
||||
**Location**: Defined in `src/records.rs`
|
||||
|
||||
## Data Processing Pipeline
|
||||
|
||||
### 1. Data Acquisition Phase
|
||||
|
|
@ -226,6 +268,11 @@ if let Some(configurations) = json_data["data"].as_object() {
|
|||
- Process: Split on `"_a"` delimiter
|
||||
- Output: `("laguna", "00")` (track name, condition code)
|
||||
|
||||
**Track Name Translation:**
|
||||
- Track codes are looked up in `track_names.json` (compiled into binary)
|
||||
- Example: `"laguna"` → `"Laguna Seca"`
|
||||
- Fallback: Unknown codes returned as-is
|
||||
|
||||
**Condition Code Translation:**
|
||||
- `"00"` → `"Dry"`
|
||||
- `"01"` → `"Wet"`
|
||||
|
|
@ -255,7 +302,7 @@ BestCarRecord {
|
|||
```
|
||||
|
||||
**RQ Range Analysis:**
|
||||
- Test each car from its base RQ up to 115
|
||||
- Test each car from its base RQ up to `MAX_RQ` (113)
|
||||
- Compare against existing best times
|
||||
- Update records if better performance found
|
||||
|
||||
|
|
@ -303,7 +350,7 @@ BestCarRecord {
|
|||
- User collection: Small, loaded once
|
||||
|
||||
**Optimization Strategies**:
|
||||
- Concurrent processing of car files
|
||||
- Concurrent processing of car files with bounded concurrency (semaphore limit of 20)
|
||||
- Rate-limited downloads (20 concurrent max)
|
||||
- Streaming JSON parsing where possible
|
||||
|
||||
|
|
@ -315,7 +362,7 @@ BestCarRecord {
|
|||
3. **HashMap Operations**: Frequent insertions with RQ range checking
|
||||
|
||||
**Optimizations Applied**:
|
||||
1. **Concurrency**: One task per car file
|
||||
1. **Bounded Concurrency**: Semaphore limits concurrent operations to 20
|
||||
2. **Connection Pooling**: Reuse HTTP connections
|
||||
3. **Early Filtering**: Invalid times rejected quickly
|
||||
|
||||
|
|
@ -361,8 +408,8 @@ BestCarRecord {
|
|||
|
||||
**Track Name Issues**:
|
||||
- **Symptom**: Unknown track codes in output
|
||||
- **Cause**: Outdated track definitions
|
||||
- **Solution**: Update `tracks_repo.json`
|
||||
- **Cause**: Missing mapping in `track_names.json`
|
||||
- **Solution**: Add new mapping to `track_names.json`
|
||||
|
||||
### Data Validation Tools
|
||||
|
||||
|
|
@ -404,4 +451,4 @@ grep -r "No RID" times/
|
|||
- Historical performance trends
|
||||
- Meta change impact analysis
|
||||
- Community submission quality metrics
|
||||
- Regional performance differences
|
||||
- Regional performance differences
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ serde_json = "1.0.140"
|
|||
- Parse `my_cars.json` (user collection)
|
||||
- Parse individual car timing files
|
||||
- Parse `tracks_repo.json` (track definitions)
|
||||
- Parse `track_names.json` (track name mappings)
|
||||
|
||||
**Performance**: Zero-copy parsing where possible
|
||||
|
||||
|
|
@ -205,26 +206,9 @@ once_cell = "1.20.2"
|
|||
- Thread-safe access during concurrent parsing
|
||||
- Avoid expensive computation on every access
|
||||
|
||||
#### 6. `scraper` - HTML Parsing (Club Module Only)
|
||||
```toml
|
||||
scraper = "0.23.1"
|
||||
```
|
||||
|
||||
**Purpose**: HTML parsing and CSS selector engine for web scraping
|
||||
|
||||
**Features Used**:
|
||||
- CSS selector parsing with `Selector::parse()`
|
||||
- HTML document parsing with `Html::parse_document()`
|
||||
- Element selection and text extraction
|
||||
|
||||
**Why Needed**:
|
||||
- Alternative data source for club.rs module
|
||||
- Parse car data from topdrives.club HTML tables
|
||||
- Extract performance information from web pages
|
||||
|
||||
### Development Dependencies
|
||||
|
||||
#### 7. Build Tools and Quality
|
||||
#### 6. Build Tools and Quality
|
||||
|
||||
**Recommended additions for development:**
|
||||
```toml
|
||||
|
|
@ -289,9 +273,9 @@ name = "top_drives"
|
|||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "club"
|
||||
path = "src/club.rs"
|
||||
[lib]
|
||||
name = "top_drives"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "records"
|
||||
|
|
@ -300,7 +284,6 @@ path = "src/records.rs"
|
|||
|
||||
**Binary Configuration:**
|
||||
- **records**: Main analysis tool (JSON-based)
|
||||
- **club**: Alternative scraper (web-based)
|
||||
|
||||
**Dependency Versions:**
|
||||
- All dependencies pinned to specific versions for reproducibility
|
||||
|
|
@ -342,7 +325,7 @@ RUST_BACKTRACE=1 cargo run
|
|||
```
|
||||
|
||||
**Concurrency Tuning:**
|
||||
- Default: 20 concurrent downloads (configurable via Semaphore)
|
||||
- Default: 20 concurrent operations (bounded via Semaphore)
|
||||
- Thread count: Automatic based on CPU cores
|
||||
- Memory usage: ~100-200MB for full dataset
|
||||
|
||||
|
|
@ -471,7 +454,6 @@ cargo build --release
|
|||
**Binary Location:**
|
||||
```
|
||||
target/release/records # Main analyzer
|
||||
target/release/club # Club scraper
|
||||
```
|
||||
|
||||
**Cross-Platform Building:**
|
||||
|
|
@ -529,4 +511,4 @@ cargo profdata # Profile-guided optimization
|
|||
- **Build Tool**: Cargo (included with Rust)
|
||||
- **Editor**: VS Code with rust-analyzer (recommended)
|
||||
- **Testing**: Built-in Rust testing framework
|
||||
- **Debugging**: GDB/LLDB integration available
|
||||
- **Debugging**: GDB/LLDB integration available
|
||||
|
|
|
|||
|
|
@ -160,8 +160,8 @@ Best car for Track: laguna, Condition: Dry, RQ: 85 -> McLaren F1 (1993) with tim
|
|||
```
|
||||
|
||||
**Track name format:**
|
||||
- Use internal names: `laguna`, `monaco`, `testBowl`
|
||||
- Full mappings in `translate_track_name()` function
|
||||
- Use display names: `Laguna Seca`, `Monaco`, `Test Bowl`
|
||||
- Full mappings loaded from `track_names.json`
|
||||
|
||||
**Condition options:**
|
||||
- `Dry` - Standard asphalt
|
||||
|
|
@ -169,6 +169,43 @@ Best car for Track: laguna, Condition: Dry, RQ: 85 -> McLaren F1 (1993) with tim
|
|||
- `Dirt` - Off-road tracks
|
||||
- `Snow` - Winter conditions
|
||||
|
||||
## Daily Workflow
|
||||
|
||||
The recommended daily workflow uses the `full_script` command, which runs `fetch` followed by all analysis commands in a single pipeline:
|
||||
|
||||
```bash
|
||||
cargo run --bin records -- full_script
|
||||
```
|
||||
|
||||
### What `full_script` Does
|
||||
|
||||
1. **Fetches** fresh data from the community repository
|
||||
2. **Runs** all analysis commands sequentially
|
||||
3. **Generates** all output CSV files
|
||||
|
||||
### Output Files
|
||||
|
||||
| File | Purpose | Format |
|
||||
|------|---------|--------|
|
||||
| `best.csv` | Ranked list of best cars — evaluate if a car is worth keeping | `RQ,Brand,Model,Year,Count` |
|
||||
| `details.csv` | Detailed timing data for all tracks/conditions — find best config for a car | `RQ,TrackID,Condition,CarRQ,Brand,Model,Year,Config,Time` |
|
||||
| `add.csv` | Cars to add to wishlist — missing cars not yet wishlisted, prefixed with `+` | `+RQ,Brand,Model,Year` |
|
||||
| `remove.csv` | Cars to remove from wishlist — stale wishlist entries, prefixed with `-` | `-RQ,Brand,Model,Year` |
|
||||
| `trash.csv` | Owned cars that can be safely removed | `RQ,Brand,Model,Year` |
|
||||
| `sorted.csv` | Best cars sorted by count (descending) | `RQ,Brand,Model,Year,Count` |
|
||||
| `very_best.csv` | Best cars with count ≥ 10 | `RQ,Brand,Model,Year,Count` |
|
||||
| `wishlist.csv` | Missing cars already on wishlist | `RQ,Brand,Model,Year,Count` |
|
||||
|
||||
### Typical Daily Routine
|
||||
|
||||
1. **Run `full_script`** to get fresh data and full analysis
|
||||
2. **Review `best.csv`** to see how your cars rank overall
|
||||
3. **Check `add.csv`** for cars worth adding to your wishlist (prefixed with `+`)
|
||||
4. **Check `remove.csv`** for wishlist entries to remove (prefixed with `-`)
|
||||
5. **Review `trash.csv`** for owned cars you can safely sell
|
||||
6. **Use `details.csv`** to find the optimal tune config for a specific car
|
||||
7. **Check `very_best.csv`** for the absolute top performers (count ≥ 10)
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### RQ Range Analysis
|
||||
|
|
@ -241,16 +278,24 @@ The tool generates CSV files for external analysis:
|
|||
- Import into spreadsheet for sorting/filtering
|
||||
- Track performance across time
|
||||
|
||||
#### missing.csv
|
||||
- Cars to acquire
|
||||
- Prioritize by count and RQ relevance
|
||||
- Plan acquisition strategy
|
||||
|
||||
#### details.csv
|
||||
- Comprehensive timing database
|
||||
- Advanced analysis and research
|
||||
- Historical performance tracking
|
||||
|
||||
#### add.csv / remove.csv
|
||||
- Wishlist management
|
||||
- `+` prefix indicates cars to add
|
||||
- `-` prefix indicates cars to remove
|
||||
|
||||
#### trash.csv
|
||||
- Cars safe to sell from your collection
|
||||
- Cross-reference with your garage before selling
|
||||
|
||||
#### sorted.csv / very_best.csv
|
||||
- Sorted rankings by versatility
|
||||
- `very_best.csv` filters to count ≥ 10 for top-tier recommendations
|
||||
|
||||
### Spreadsheet Analysis Tips
|
||||
|
||||
**Excel/Google Sheets setup:**
|
||||
|
|
@ -265,7 +310,7 @@ The tool generates CSV files for external analysis:
|
|||
|
||||
#### "Please provide a command"
|
||||
**Problem:** No arguments provided
|
||||
**Solution:** Use `best`, `missing`, `trash`, `details`, or `track`
|
||||
**Solution:** Use `best`, `missing`, `trash`, `details`, `track`, or `full_script`
|
||||
|
||||
#### "Could not read my cars file"
|
||||
**Problem:** `my_cars.json` not found or not readable
|
||||
|
|
@ -303,9 +348,9 @@ The tool generates CSV files for external analysis:
|
|||
## Tips and Best Practices
|
||||
|
||||
### Regular Maintenance
|
||||
1. **Weekly**: Run `fetch` to get latest data
|
||||
2. **Monthly**: Review `missing` cars for new acquisitions
|
||||
3. **Quarterly**: Use `trash` to optimize collection
|
||||
1. **Daily**: Run `full_script` to get latest data and full analysis
|
||||
2. **Weekly**: Review `missing` cars for new acquisitions
|
||||
3. **Monthly**: Use `trash` to optimize collection
|
||||
|
||||
### Data-Driven Decisions
|
||||
1. **Trust the numbers**: Count-based recommendations are data-driven
|
||||
|
|
@ -331,4 +376,4 @@ The tool generates CSV files for external analysis:
|
|||
|
||||
---
|
||||
|
||||
**Remember**: This tool provides data-driven insights, but ultimately you're the best judge of which cars you enjoy driving. Use the analysis as a guide, not a strict rulebook.
|
||||
**Remember**: This tool provides data-driven insights, but ultimately you're the best judge of which cars you enjoy driving. Use the analysis as a guide, not a strict rulebook.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue