15 KiB
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
#[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 filesbest: Show best owned carsmissing: Show best unowned carstrash: Show removable carsdetails: Export all timing datatrack: Track-specific analysis
Example:
cargo run --bin records -- best
print_best_cars(filter_out_owned: bool)
Displays ranked list of best performing cars
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:
RQ,Brand,Model,Year,Count
85,McLaren,F1,1993,15
82,Ferrari,250 GTO,1962,12
Algorithm:
- Count occurrences of each car across all track/RQ/condition combinations
- Sort by RQ level, then by car identifier
- Apply ownership filter if requested
print_best_times()
Exports comprehensive timing data for all combinations
fn print_best_times()
Purpose: Detailed performance export for external analysis
Output Format:
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
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:
print_best_car(&"laguna".to_string(), &"Dry".to_string(), &"85".to_string());
print_trash()
Identifies cars safe for removal from collection
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:
RQ,Brand,Model,Year
45,Toyota,Corolla,1995
parse_car_times(car: Car)
Processes timing data for a single car
async fn parse_car_times(car: Car)
Purpose: Core parsing logic for individual car performance data
Process Flow:
- Load car's JSON timing file
- Parse each configuration (111, 233, 323, etc.)
- Extract timing data for each track/condition
- Apply quality filters (downvotes, time validation)
- 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
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
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:
- For each RQ level from car_rq to 115:
- If new time is better → replace record
- If new time ties → add to list (if not duplicate)
- If new time is worse → ignore
get_car_list() -> Vec<Car>
Loads complete car database from repository file
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
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
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
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
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
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
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
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
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
fn split_prefix_postfix<'a>(input: &'a str, delimiter: &'a str) -> Option<(&'a str, &'a str)>
Purpose: Parse track IDs with condition suffixes
Example:
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
#[tokio::main]
async fn main()
Purpose: Web scraping interface for live data
Arguments:
- Command line arguments for RQ and track filtering
-dflag for detailed output
get_page(url: &str) -> String
Fetches HTML content from URLs
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
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
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
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
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
struct Card {
cardId: String, // Unique card identifier
locked: bool, // Ownership status
}
Car
Car metadata from database
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
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
type TripletKey = (String, u8, String);
// Track ID RQ Level Surface Condition
Club Module Data Types
Car
Scraped car data from website
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
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
// 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
// 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
// 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
- Add command parsing in
main() - Implement analysis function
- Update help documentation
Adding New Data Sources
- Implement new module (similar to
club.rs) - Add data source selection logic
- Update configuration system
Custom Analysis Types
- Create new output formatting functions
- Add aggregation algorithms
- Extend data structures as needed
Testing
Unit Tests
#[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:
println!("Processing car: {}", car.rid);
eprintln!("Error: {}", error);
Tracing:
// Add at function entry
let _span = tracing::span!(Level::INFO, "function_name");
let _enter = _span.enter();
Profiling:
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+