# 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 ### Shared Library (`src/lib.rs`) Common types and utilities shared across the project. #### Public Types ##### `Car` **Core car information structure** ```rust #[derive(Debug, Clone, Deserialize)] pub struct Car { pub rid: String, // Repository identifier pub brand: String, // Manufacturer/brand name pub only_name: String, // Model name (without brand) pub year: u16, // Model year pub rq: u8, // Rarity level (10-113) pub prize: bool, // Whether car is prize-only pub guid: String, // Game-specific card identifier } ``` **Purpose**: Represents a car entity from the records database. **Deserialization**: Uses `serde::Deserialize` with `#[serde(rename = "onlyName")]` for the `only_name` field and `#[serde(default)]` for `prize`. ##### `BestCarRecord` **Best performing car record for a specific track/RQ/condition** ```rust #[derive(Debug, Clone)] pub struct BestCarRecord { pub car_rid: String, // Car identifier pub brand: String, // Manufacturer pub only_name: String, // Model name pub year: u16, // Year pub rq: u8, // RQ level pub config: String, // Tune config (111, 233, etc.) pub time: f64, // Track time in seconds pub owned: bool, // User ownership status pub prize: bool, // Prize car status pub guid: String, // Game identifier } ``` ##### `TripletKey` **Composite key for unique track conditions** ```rust pub type TripletKey = (String, u8, String); // Track ID RQ Level Surface Condition ``` ##### `AnalysisContext` **Mutable analysis state** ```rust pub struct AnalysisContext { pub best_cars: Arc>>>, } ``` **Methods**: - `new()` — Create a new, empty analysis context - `lock_best_cars()` — Acquire a lock on the best_cars map (returns `Result`, converts poison errors to `AppError`) ##### `CarCounts` **Aggregated per-car win counts** ```rust pub struct CarCounts { pub by_rid: HashMap, pub guid_to_rid: HashMap, pub car_map: HashMap, pub car_list: Vec, } ``` ##### `MyCarsData` **Parsed user data from `my_cars.json`** ```rust #[derive(Debug)] pub struct MyCarsData { pub owned: HashSet, // Owned car GUIDs pub wishlist: HashSet, // Wishlist car GUIDs } ``` ##### `AppError` **Unified error type** ```rust #[derive(Debug, thiserror::Error)] pub enum AppError { #[error("I/O error: {0}")] Io(#[from] std::io::Error), #[error("Network error: {0}")] Network(String), #[error("Parse error in {file}: {reason}")] Parse { file: String, reason: String }, #[error("Missing data file: {0}")] MissingFile(String), #[error("Internal error: mutex poisoned — {0}")] Poisoned(String), } ``` #### Public Functions ##### `translate_track_name(track_code: &str) -> String` **Translates internal track identifiers to human-readable names** - **Input**: Track code (e.g., `"laguna"`) - **Output**: Track name (e.g., `"Laguna Seca"`) - **Fallback**: Returns original code if no mapping found - **Data source**: `track_names.json` (compiled into binary via `include_str!`) ##### `translate_track_type(track_code: &str) -> &'static str` **Translates condition codes to surface names** - **Input**: Condition code (e.g., `"00"`) - **Output**: Surface name (e.g., `"Dry"`) - **Fallback**: Returns `"Unknown"` for unrecognized codes ##### `get_file(file_url: &str, local_file_path: &str) -> Result<(), AppError>` **Downloads a file from a URL and saves it locally** - Removes existing file before downloading - Returns `AppError::Network` on HTTP failures - Returns `AppError::Io` on file write failures ##### `load_my_cars_data(path: &str) -> Result` **Loads and parses user car collection data** - **Input**: Path to `my_cars.json` - **Returns**: `MyCarsData` with owned and wishlist GUIDs - **Errors**: `AppError::MissingFile` if file not found, `AppError::Parse` if JSON invalid ##### `compare_cars(a: &Car, b: &Car) -> std::cmp::Ordering` **Sorting comparator for cars** - Sorts by: RQ → Brand → Model name ##### `compute_car_counts(ctx: &AnalysisContext, car_list: Vec) -> Result` **Aggregates per-car win counts from analysis context** - Returns `CarCounts` with by_rid, guid_to_rid, car_map, and car_list ##### `escape_csv_field(field: &str) -> String` **Escapes a field for safe CSV inclusion** - Wraps in quotes if field contains comma, quote, newline, or carriage return - Doubles internal quotes per CSV spec --- ### Records Binary (`src/records.rs`) Primary analysis engine for processing JSON-based timing data. #### Constants | Constant | Value | Description | |----------|-------|-------------| | `MAX_RQ` | 113 | Maximum RQ level for range analysis | | `MIN_VALID_TIME` | 1.2 | Minimum valid track time (seconds) | | `TIME_TOLERANCE` | 0.005 | Tolerance for tie detection (seconds) | | `TEST_BOWL_FACTOR` | 2000.0 | Multiplier for Test Bowl time inversion | | `CONCURRENCY_LIMIT` | 20 | Maximum concurrent tasks | | `DEFAULT_MY_CARS_PATH` | `"my_cars.json"` | Default user data file path | | `CARS_DB_PATH` | `"cars_repo.json"` | Car database file path | | `TIMES_DIR` | `"times"` | Timing data directory | | `CARS_DB_URL` | GitHub URL | Remote car database URL | | `TRACKS_DB_URL` | GitHub URL | Remote tracks database URL | | `CAR_TIMING_API_BASE` | API URL | Base URL for per-car timing data | #### CLI Commands | Command | Description | Flags | |---------|-------------|-------| | `fetch` | Download latest car and track data | None | | `best` | Generate `best.csv` and `very_best.csv` | `--min-count N` (default: 10) | | `trash` | Generate `trash.csv` | None | | `wishlist` | Generate `wishlist.csv`, `add.csv`, `remove.csv` | `--min-count N`, `--max-wishlist M` | | `all` | Generate all output files in a single pass | `--min-count N`, `--max-wishlist M` | | `details` | Generate `details.csv` with all timing data | None | | `track` | Show best car for specific track/condition/RQ | ` ` | #### Key Internal Functions ##### `run_analysis(owned: &HashSet) -> Result<(AnalysisContext, CarCounts), AppError>` **Common analysis setup** Loads user data, parses all timing files, and computes car counts. ##### `parse_car_times(car: Car, my_cars: &HashSet, ctx: &AnalysisContext)` **Processes timing data for a single car** - Runs inside `tokio::task::spawn_blocking` to avoid blocking async runtime - Reads car JSON file from `times/` directory - Processes each configuration and track - Applies quality filters (downvotes, minimum time) - Handles Test Bowl time inversion ##### `update_best_car(best_cars, track_id, car_rq, surface, new_record)` **Updates best car records with a new performance entry** - Iterates RQ range from car's RQ to `MAX_RQ` - Replaces existing record if new time is faster - Adds to tie list if within `TIME_TOLERANCE` - Prevents duplicate `car_rid` entries in tie lists ##### `parse_all_times(my_cars, ctx) -> Vec` **Orchestrates concurrent parsing of all car timing files** - Uses `Semaphore(CONCURRENCY_LIMIT)` for bounded concurrency - Spawns blocking tasks via `tokio::task::spawn_blocking` - Returns the full car list for downstream use ##### `compute_ideal_wishlist(counts, owned, min_count, max_wishlist) -> Vec<(BestCarRecord, usize)>` **Computes the ideal wishlist** - Filters to cars with count >= `min_count` - Excludes owned cars - Sorts by RQ - Caps at `max_wishlist` #### Output Functions ##### `write_sorted_csv(path, header, entries, cmp, format_row)` **Generic sorted CSV writer** - Sorts by index (avoids cloning entries) - Writes header row - Formats each row via closure ##### `write_best_csv(counts)`, `write_very_best_csv(counts, min_count)` **Generate best car CSV files** - Format: `RQ,Brand,Model,Year,Count` - Fields are CSV-escaped ##### `write_trash_csv(counts, owned)` **Generate trash car CSV file** - Filters: not best on any track, RQ < 80, not prize car - Format: `RQ,Brand,Model,Year` ##### `write_wishlist_files(counts, owned, wishlist, min_count, max_wishlist)` **Generate wishlist, add, and remove CSV files** - Computes ideal wishlist once - Derives all three files from it - Format: `RQ,Brand,Model,Year,Count` ##### `write_details_csv(ctx)` **Generate comprehensive timing data CSV** - Format: `RQ,TrackID,Condition,CarRQ,Brand,Model,Year,Config,Time` - Sorted by RQ then track ID ##### `print_best_car(ctx, track, condition, rq)` **Print best car for a specific track/condition/RQ to stdout**