top_drives/src/commands/mod.rs
Jan-Peter von Hunnius 67a79f190a Refactor: extract lib.rs into focused sub-modules, add --output-dir CLI option
- Split monolithic src/lib.rs (~640 lines) into 7 sub-modules:
  types.rs, context.rs, user_data.rs, translate.rs, http.rs,
  csv_util.rs, test_utils.rs
- Add configurable --output-dir CLI option for CSV output files
- Implement atomic file writes (write-to-temp + rename) for all CSVs
- Change compute_car_counts() to return CarCounts directly (not Result)
- Add test-utils feature gate for shared test helpers
- Add HTTP mock tests with mockito (6 tests)
- Add proptest tests for compute_car_counts (3 property tests)
- Update integration tests to use shared test utilities
- Update documentation (API.md, ARCHITECTURE.md, DATA_STRUCTURES.md)
2026-05-08 20:21:46 +02:00

63 lines
2.2 KiB
Rust

//! Command dispatch and shared helpers.
//!
//! Provides the `run_analysis` orchestration function and `load_my_cars_or_warn`
//! helper used by all command handlers.
pub mod all;
pub mod best;
pub mod details;
pub mod fetch;
pub mod track;
pub mod trash;
pub mod wishlist;
use std::collections::HashSet;
use std::sync::Arc;
use top_drives::{
AppError, AnalysisContext, CarCounts, MyCarsData,
compute_car_counts, load_my_cars_data,
};
use crate::parsing::parse_all_times;
// ---------------------------------------------------------------------------
// Helper: load my_cars.json with graceful degradation
// ---------------------------------------------------------------------------
/// Loads user car data, printing a warning and returning empty data if the file is missing.
///
/// Only `MissingFile` errors are gracefully degraded — parse errors and other
/// failures are propagated to the caller, because running analysis with
/// silently-corrupted ownership data would produce misleading results.
pub fn load_my_cars_or_warn(path: &str) -> Result<MyCarsData, AppError> {
match load_my_cars_data(path) {
Ok(data) => Ok(data),
Err(AppError::MissingFile(msg)) => {
eprintln!("Warning: {msg}");
eprintln!(" Analysis will run without ownership data.");
Ok(MyCarsData::default())
}
Err(e) => Err(e),
}
}
// ---------------------------------------------------------------------------
// Analysis orchestration
// ---------------------------------------------------------------------------
/// Common analysis setup: loads user data, parses all timing files, and computes car counts.
///
/// Returns the analysis context (for stdout commands) and the aggregated car counts
/// (for CSV output commands). The car list is stored inside `CarCounts`.
pub async fn run_analysis(
owned: &HashSet<String>,
cars_db_path: &str,
times_dir: &str,
) -> Result<(AnalysisContext, CarCounts), AppError> {
let ctx = AnalysisContext::new();
let owned = Arc::new(owned.clone());
let car_list = parse_all_times(owned, &ctx, cars_db_path, times_dir).await?;
let counts = compute_car_counts(&ctx, car_list);
Ok((ctx, counts))
}