refactor: modular architecture with unified error handling and DashMap concurrency

- Break monolithic records.rs into commands/, output/, parsing/ modules
- Add lib.rs with shared types (AppError, Car, BestCarRecord, etc.)
- Replace Arc<Mutex<HashMap>> with DashMap for concurrent access
- Add unified AppError enum with thiserror
- Add configurable CLI paths (--my-cars, --cars-db, --times-dir, --tracks-db)
- Add serde deserialization for Car struct
- Add CSV escaping for fields with commas/quotes/newlines
- Fix track name splitting: split_once -> rsplit_once for _a handling
- Add integration tests, proptest property-based tests, and unit tests
- Add rustfmt.toml configuration
- Update documentation (API, ARCHITECTURE, DATA_STRUCTURES, README)
This commit is contained in:
Jan-Peter von Hunnius 2026-05-08 16:19:01 +02:00
parent 9d57b18a4e
commit a9a46ee551
24 changed files with 3541 additions and 1470 deletions

48
src/commands/fetch.rs Normal file
View file

@ -0,0 +1,48 @@
//! Fetch command implementation.
//!
//! Downloads car database, tracks database, and per-car timing data
//! from remote repositories.
use top_drives::{AppError, get_file};
use crate::parsing::fetch_times;
/// Base URL for the remote data repository on GitHub.
/// Single place to update if the repository path or branch changes.
const GITHUB_RAW_BASE: &str =
"https://github.com/TiagoXavi/topdrives-records/raw/refs/heads/main/src/database";
/// Returns the full URL for the car database file.
fn cars_db_url() -> String {
format!("{GITHUB_RAW_BASE}/cars_final.json")
}
/// Returns the full URL for the tracks database file.
fn tracks_db_url() -> String {
format!("{GITHUB_RAW_BASE}/tracks_repo.json")
}
/// Downloads the tracks database file from the remote repository.
async fn get_tracks_file(tracks_db_path: &str) -> Result<(), AppError> {
println!("Fetching tracks file...");
get_file(&tracks_db_url(), tracks_db_path).await
}
/// Downloads the cars database file from the remote repository.
pub async fn get_cars_file(cars_db_path: &str) -> Result<(), AppError> {
println!("Fetching cars file...");
get_file(&cars_db_url(), cars_db_path).await
}
/// Executes the fetch command: downloads all data files.
///
/// Downloads the car database, tracks database, and per-car timing files.
/// Fails fast on critical dependency failures — the cars DB must be available
/// before per-car timing files can be fetched.
pub async fn run_fetch(cars_db_path: &str, tracks_db_path: &str, times_dir: &str) -> Result<(), AppError> {
get_cars_file(cars_db_path).await?;
get_tracks_file(tracks_db_path).await?;
fetch_times(cars_db_path, times_dir).await?;
println!("Fetch complete — all files downloaded successfully.");
Ok(())
}