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+
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue