//! # Top Drives Records Analyzer //! //! A comprehensive Rust-based tool for analyzing car performance data in the Top Drives racing game. //! This module provides the CLI entry point and delegates to submodules for parsing, output, and //! command handling. //! //! ## Module Structure //! //! - [`commands`] — Command handlers (fetch, best, trash, wishlist, details, track) //! - [`parsing`] — Core timing data parsing and car database loading //! - [`output`] — CSV output utilities and sorting helpers //! //! ## Key Features //! //! - Fetches car and track data from GitHub repository //! - Parses JSON timing files for performance analysis //! - Identifies best performing cars across all tracks and conditions //! - Compares user collection against optimal car lists //! - Provides track-specific performance recommendations //! - Supports multiple track conditions (Dry, Wet, Dirt, Snow, etc.) //! //! ## Data Flow //! //! 1. **Fetch Phase**: Downloads car database and track definitions //! 2. **Parse Phase**: Processes JSON timing files for each car //! 3. **Analysis Phase**: Compares times across RQ ranges and conditions //! 4. **Output Phase**: Writes results to CSV files //! //! ## Dependencies //! //! - `reqwest`: HTTP client for fetching data from GitHub //! - `serde_json`: JSON parsing for car and track data //! - `tokio`: Asynchronous runtime for concurrent processing //! - `futures`: Future utilities for handling async operations //! - `clap`: Command-line argument parsing mod commands; mod output; mod parsing; use clap::{Parser, Subcommand}; use top_drives::AppError; // --------------------------------------------------------------------------- // Constants (CLI defaults) // --------------------------------------------------------------------------- /// Default path for the user's car collection export. const DEFAULT_MY_CARS_PATH: &str = "my_cars.json"; /// Default path for the car database file. const DEFAULT_CARS_DB_PATH: &str = "cars_repo.json"; /// Default directory for per-car timing JSON files. const DEFAULT_TIMES_DIR: &str = "times"; /// Default path for the tracks database file. const DEFAULT_TRACKS_DB_PATH: &str = "tracks_repo.json"; /// Default directory for output CSV files. const DEFAULT_OUTPUT_DIR: &str = "."; // --------------------------------------------------------------------------- // CLI definition // --------------------------------------------------------------------------- /// Command-line interface for the Top Drives Records Analyzer. #[derive(Parser)] #[command(name = "records", about = "Top Drives Records Analyzer", version)] struct Cli { /// Path to the user's car collection export file #[arg(long, default_value = DEFAULT_MY_CARS_PATH)] my_cars: String, /// Path to the car database file #[arg(long, default_value = DEFAULT_CARS_DB_PATH)] cars_db: String, /// Directory containing per-car timing JSON files #[arg(long, default_value = DEFAULT_TIMES_DIR)] times_dir: String, /// Path to the tracks database file #[arg(long, default_value = DEFAULT_TRACKS_DB_PATH)] tracks_db: String, /// Directory for output CSV files #[arg(long, default_value = DEFAULT_OUTPUT_DIR)] output_dir: String, #[command(subcommand)] command: Commands, } /// Available subcommands for car performance analysis. #[derive(Subcommand)] enum Commands { /// Download latest car and track data from repository Fetch, /// Generate best.csv and very_best.csv Best { /// Minimum number of tracks a car must be best on to appear in very_best.csv (default: 10) #[arg(long, default_value_t = 10)] min_count: usize, }, /// Generate trash.csv (owned cars not best on any track) Trash, /// Generate wishlist.csv, add.csv, and remove.csv Wishlist { /// Minimum number of tracks a car must be best on to qualify (default: 10) #[arg(long, default_value_t = 10)] min_count: usize, /// Maximum number of cars on the wishlist (default: 300) #[arg(long, default_value_t = 300)] max_wishlist: usize, }, /// Generate all output files in a single pass All { /// Minimum number of tracks a car must be best on to qualify for very_best/wishlist (default: 10) #[arg(long, default_value_t = 10)] min_count: usize, /// Maximum number of cars on the wishlist (default: 300) #[arg(long, default_value_t = 300)] max_wishlist: usize, }, /// Generate details.csv with all timing data Details, /// Show best car for a specific track/condition/RQ combination Track { track: String, condition: String, rq: u8 }, /// Generate improve.csv (owned cars not in final config, sorted by best tracks) Improve, } // --------------------------------------------------------------------------- // Main entry point // --------------------------------------------------------------------------- /// Main entry point for the Top Drives Records Analyzer /// /// Parses command line arguments using `clap` and executes the requested analysis operation. /// Supports multiple subcommands for different types of car performance analysis. /// /// # Example Usage /// /// ```bash /// cargo run --bin records -- best /// cargo run --bin records -- track "Laguna Seca" "Dry" "85" /// cargo run --bin records -- --cars-db /data/cars.json --times-dir /data/times best /// ``` #[tokio::main] async fn main() { if let Err(e) = run().await { eprintln!("Error: {e}"); std::process::exit(1); } } async fn run() -> Result<(), AppError> { let cli = Cli::parse(); // Ensure the output directory exists before dispatching any command. // create_dir_all(".") is a no-op, so no special-casing needed. std::fs::create_dir_all(&cli.output_dir)?; match cli.command { Commands::Fetch => { commands::fetch::run_fetch(&cli.cars_db, &cli.tracks_db, &cli.times_dir).await?; } Commands::Best { min_count } => { commands::best::run_best( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, min_count, ) .await?; } Commands::Trash => { commands::trash::run_trash(&cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir) .await?; } Commands::Wishlist { min_count, max_wishlist } => { commands::wishlist::run_wishlist( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, min_count, max_wishlist, ) .await?; } Commands::All { min_count, max_wishlist } => { commands::all::run_all( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, min_count, max_wishlist, ) .await?; } Commands::Details => { commands::details::run_details( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, ) .await?; } Commands::Track { track, condition, rq } => { commands::track::run_track( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, &track, &condition, rq, ) .await?; } Commands::Improve => { commands::improve::run_improve( &cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir, ) .await?; } } Ok(()) }