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)
This commit is contained in:
Jan-Peter von Hunnius 2026-05-08 20:21:46 +02:00
parent d2c1418e5e
commit 67a79f190a
27 changed files with 1648 additions and 789 deletions

View file

@ -58,6 +58,9 @@ 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
// ---------------------------------------------------------------------------
@ -82,6 +85,10 @@ struct Cli {
#[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,
}
@ -154,32 +161,37 @@ async fn main() {
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, min_count).await?;
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).await?;
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, min_count, max_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, min_count, max_wishlist,
&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).await?;
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, &track, &condition, rq,
&cli.my_cars, &cli.cars_db, &cli.times_dir, &cli.output_dir,
&track, &condition, rq,
).await?;
}
}