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

15 KiB
Raw Blame History

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 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:

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:

  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

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:

  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

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:

  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

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
  • -d flag 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

  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

#[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+