top_drives/docs/API.md
Jan-Peter von Hunnius 32c4d3c10e Added documentation
Added test cases
Added comments
2025-10-02 17:35:56 +02:00

654 lines
No EOL
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API Documentation
## Overview
This document provides comprehensive API documentation for the Top Drives Records Analyzer, including all public functions, data structures, and their usage patterns.
## Core Modules
### Records Module (`src/records.rs`)
Primary analysis engine for processing JSON-based timing data.
#### Public Functions
##### `main()`
**Entry point for the records analyzer**
```rust
#[tokio::main]
async fn main()
```
**Purpose**: Command-line interface and operation dispatcher
**Arguments**: Command line arguments
- `args[1]`: Command (`fetch`, `best`, `missing`, `trash`, `details`, `track`)
**Commands**:
- `fetch`: Download all data files
- `best`: Show best owned cars
- `missing`: Show best unowned cars
- `trash`: Show removable cars
- `details`: Export all timing data
- `track`: Track-specific analysis
**Example**:
```bash
cargo run --bin records -- best
```
##### `print_best_cars(filter_out_owned: bool)`
**Displays ranked list of best performing cars**
```rust
fn print_best_cars(filter_out_owned: bool)
```
**Purpose**: Aggregates performance data across all tracks and conditions
**Arguments**:
- `filter_out_owned`: If true, excludes owned cars from output
**Output Format**:
```csv
RQ,Brand,Model,Year,Count
85,McLaren,F1,1993,15
82,Ferrari,250 GTO,1962,12
```
**Algorithm**:
1. Count occurrences of each car across all track/RQ/condition combinations
2. Sort by RQ level, then by car identifier
3. Apply ownership filter if requested
##### `print_best_times()`
**Exports comprehensive timing data for all combinations**
```rust
fn print_best_times()
```
**Purpose**: Detailed performance export for external analysis
**Output Format**:
```csv
RQ,TrackID,Condition,CarRQ,Brand,Model,Year,Config,Time
85,laguna,Dry,85,McLaren,F1,1993,233,112.45
```
**Data Volume**: Large output file with all track/condition/RQ combinations
##### `print_best_car(track: &String, condition: &String, rq: &String)`
**Finds best car for specific track conditions**
```rust
fn print_best_car(track: &String, condition: &String, rq: &String)
```
**Purpose**: Targeted analysis for specific racing scenarios
**Arguments**:
- `track`: Track identifier (e.g., "laguna", "monaco")
- `condition`: Surface condition (e.g., "Dry", "Wet")
- `rq`: RQ level limit
**Example**:
```rust
print_best_car(&"laguna".to_string(), &"Dry".to_string(), &"85".to_string());
```
##### `print_trash()`
**Identifies cars safe for removal from collection**
```rust
fn print_trash()
```
**Purpose**: Collection optimization recommendations
**Safety Criteria**:
- Car not among best performers on any track
- RQ level below 80 (preserves high-end cars)
- Not a prize car (may have collection value)
**Output Format**:
```csv
RQ,Brand,Model,Year
45,Toyota,Corolla,1995
```
##### `parse_car_times(car: Car)`
**Processes timing data for a single car**
```rust
async fn parse_car_times(car: Car)
```
**Purpose**: Core parsing logic for individual car performance data
**Process Flow**:
1. Load car's JSON timing file
2. Parse each configuration (111, 233, 323, etc.)
3. Extract timing data for each track/condition
4. Apply quality filters (downvotes, time validation)
5. Update global best cars cache
**Quality Filters**:
- Minimum time: 1.2 seconds
- No downvotes from community
- Special handling for Test Bowl track
##### `parse_all_times()`
**Orchestrates concurrent processing of all cars**
```rust
async fn parse_all_times()
```
**Purpose**: Coordinates parallel processing of car timing files
**Concurrency**: One async task per car file
**Synchronization**: Waits for all tasks to complete
##### `update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCarRecord)`
**Updates global best cars database with new performance data**
```rust
fn update_best_car(track_id: &str, car_rq: u8, surface: &str, new_record: BestCarRecord)
```
**Purpose**: Implements core best-car selection algorithm
**RQ Range Analysis**:
- Tests car from base RQ up to maximum (115)
- Compares against existing best times
- Updates records for improved performance
**Logic**:
1. For each RQ level from car_rq to 115:
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
##### `get_car_list() -> Vec<Car>`
**Loads complete car database from repository file**
```rust
fn get_car_list() -> Vec<Car>
```
**Purpose**: Provides access to all known cars in the database
**Source**: `cars_repo.json` file
**Returns**: Vector of all cars with complete metadata
##### `get_my_car_list() -> Vec<String>`
**Loads user's car collection from game export**
```rust
fn get_my_car_list() -> Vec<String>
```
**Purpose**: Identifies which cars the user owns
**Source**: `my_cars.json` game export
**Filter**: Only `locked: true` cards
**Returns**: Vector of owned car GUIDs
##### `fetch_times()`
**Downloads timing files for all cars with rate limiting**
```rust
async fn fetch_times()
```
**Purpose**: Batch download of car timing data
**Rate Limiting**: 20 concurrent downloads maximum
**Progress**: Shows download progress for each file
##### `fetch_car(rid: String)`
**Downloads timing data for single car**
```rust
async fn fetch_car(rid: String)
```
**Purpose**: Individual car data fetching
**URL Format**: `https://api.topdrivesrecords.com/car/{rid}`
**Storage**: `times/{rid}.json`
##### `get_cars_file()`
**Downloads main car database**
```rust
async fn get_cars_file()
```
**Purpose**: Updates car metadata database
**Source**: GitHub repository cars database
**Destination**: `cars_repo.json`
##### `get_tracks_file()`
**Downloads track definitions**
```rust
async fn get_tracks_file()
```
**Purpose**: Updates track name mappings
**Source**: GitHub repository tracks database
**Destination**: `tracks_repo.json`
##### `get_file(file_url: &str, local_file_path: &str)`
**Generic file download utility**
```rust
async fn get_file(file_url: &str, local_file_path: &str)
```
**Purpose**: Downloads files from remote URLs
**Features**:
- Automatic existing file removal
- Error handling with graceful failure
- Progress indication through errors
##### `translate_track_name(track_code: &str) -> &str`
**Converts track codes to human-readable names**
```rust
fn translate_track_name(track_code: &str) -> &str
```
**Purpose**: User-friendly track name display
**Coverage**: 200+ track mappings
**Fallback**: Returns original code if not found
##### `translate_track_type(track_code: &str) -> &str`
**Converts condition codes to surface descriptions**
```rust
fn translate_track_type(track_code: &str) -> &str
```
**Purpose**: Human-readable surface condition names
**Mappings**:
- `"00"` → `"Dry"`
- `"01"` → `"Wet"`
- `"10"` → `"Dirt"`
- `"11"` → `"Dirt Wet"`
##### `split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a str, &'a str)>`
**String splitting utility for track ID parsing**
```rust
fn split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a str, &'a str)>
```
**Purpose**: Parse track IDs with condition suffixes
**Example**:
```rust
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
#### `Card`
**User's car collection entry**
```rust
struct Card {
cardId: String, // Unique card identifier
locked: bool, // Ownership status
}
```
#### `Car`
**Car metadata from database**
```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`
**Performance record for best cars**
```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 configuration
time: f64, // Track time
owned: bool, // User ownership
guid: String, // Game identifier
}
```
#### `TripletKey`
**Composite key for track conditions**
```rust
type TripletKey = (String, u8, String);
// Track ID RQ Level Surface Condition
```
### Club Module Data Types
#### `Car`
**Scraped car data from website**
```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
}
```
## Global State
### `BEST_CARS` Cache
**Thread-safe global performance database**
```rust
static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>
```
**Initialization**: One-time setup with `once_cell::Lazy`
**Thread Safety**: Protected by `Mutex` for concurrent access
**Structure**: HashMap with composite keys for efficient lookups
## Error Handling
### Network Errors
- **HTTP Failures**: Logged but don't crash application
- **Invalid JSON**: Graceful fallback to defaults
- **Missing Files**: Clear error messages for user
### Parse Errors
- **Malformed Data**: Skip invalid entries
- **Type Conversion**: Use safe defaults
- **Missing Fields**: Provide sensible fallbacks
### User Input Errors
- **Invalid Commands**: Clear usage instructions
- **Missing Arguments**: Descriptive error messages
- **Out of Range**: Validation with helpful feedback
## Usage Examples
### Basic Analysis Workflow
```rust
// 1. Load user's car collection
let my_cars = get_my_car_list();
// 2. Process all car timing data
parse_all_times().await;
// 3. Generate best cars report
print_best_cars(false);
// 4. Find collection optimization opportunities
print_trash();
```
### Track-Specific Analysis
```rust
// Analyze specific track conditions
let track = "laguna".to_string();
let condition = "Dry".to_string();
let rq = "85".to_string();
print_best_car(&track, &condition, &rq);
```
### Data Fetching
```rust
// Download fresh data
get_cars_file().await;
get_tracks_file().await;
fetch_times().await;
```
## Performance Characteristics
### Time Complexity
- **Parsing**: O(n) where n = number of cars × tracks
- **Best Car Selection**: O(1) HashMap operations
- **Sorting**: O(n log n) for output ordering
### Memory Usage
- **Peak**: ~200-300MB during full analysis
- **Baseline**: ~50MB application overhead
- **Per Car**: ~1-2KB metadata + timing data
### Concurrency
- **Downloads**: 20 concurrent connections max
- **Processing**: One task per car file
- **Thread Safety**: Mutex-protected shared state
## Extension Points
### Adding New Commands
1. Add command parsing in `main()`
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
### 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
- HTTPS-only external requests
- Certificate validation (with fallback)
- No sensitive data transmission
### File System Security
- Read-only access to game files
- No modification of user data
- Safe file path handling
### Input Validation
- Command injection prevention
- Path traversal protection
- Input sanitization
## Troubleshooting API Issues
### Common Problems
**"Function not found"**
- Check module imports
- Verify function signatures
- Ensure proper visibility
**"Type mismatch"**
- Review struct field types
- Check function signatures
- Verify generic parameters
**"Deadlock"**
- Avoid nested Mutex locks
- Check lock ordering
- Use timeout where possible
### Debug Strategies
**Logging**:
```rust
println!("Processing car: {}", car.rid);
eprintln!("Error: {}", error);
```
**Tracing**:
```rust
// Add at function entry
let _span = tracing::span!(Level::INFO, "function_name");
let _enter = _span.enter();
```
**Profiling**:
```bash
cargo flamegraph --bin records -- best
```
## Migration Guide
### Version Compatibility
- **Current**: 0.2.0 API stable
- **Previous**: 0.1.0 fully compatible
- **Future**: Semantic versioning maintained
### Breaking Changes
- None currently planned
- Advance notice provided for API changes
- Deprecation warnings for removed features
---
**API Version**: 0.2.0
**Last Updated**: 2024-12-XX
**Compatibility**: Rust 1.70.0+