- Gate env_logger init behind #[cfg(feature = "bin-logging")] in
records.rs main() so the binary respects the feature flag
- Set default-features = false on the dev-dependency self-reference
in Cargo.toml; previously default features were silently re-enabled
during tests, making feature isolation accidental rather than real
- Bind guid.to_string() once per card in user_data.rs deck loop and
reuse it via clone, replacing three allocations per locked card
- Add per-field help text to the Track subcommand args clarifying
human-readable names ("Laguna Seca", "Dry") vs raw codes
- Document write_details_csv DashMap snapshot memory tradeoff
(full clone, roughly doubled peak memory during the write)
- Extend my_cars fixture test: duplicate guids count as 2 copies
with per-copy configs, and incomplete-tune cards still count in
copy_count
- Warn when no timing data was found at all (empty --times-dir previously
produced empty CSVs without any hint) — best_cars empty + non-empty car
list now logs a warning in parse_all_times
- Remove the inaccurate "known gap" doc from write_trash_csv: category 1
(not best on any track) already covers non-final cars, so the
documented gap never existed
- Gate env_logger behind the bin-logging feature (default-on) and mark
the records binary as requiring it, so downstream lib consumers do not
pull env_logger transitively; lib builds clean with --no-default-features
- Switch fetch_car progress output from println! to log::info! so
RUST_LOG controls it
- atomic_write takes &Path instead of &PathBuf
- Add direct unit tests for is_final_build (sum-8 finals, maxed builds,
sub-8 non-finals, malformed inputs) and a parsing test covering
missing/null/non-numeric "t" fields (warn + skip, valid entries still
parsed)
Library no longer prints to stderr:
- Replace every eprintln! with log::warn! (lib: user_data, http; bin:
parsing, output, commands); initialize env_logger in main with a
warn-level default so output is unchanged unless RUST_LOG is set
- Warn on missing/non-numeric "t" time fields instead of silently
coercing them to 0.0; suppress the per-car NotFound warning (most
cars legitimately have no timing file), keep warnings for real I/O
errors
Ownership and clone cleanup:
- CarCounts no longer duplicates the car database: drop the car_list
field and build car_map by consuming car_list (each Car stored once);
iterate car_map.values() where the list was previously used
- run_analysis takes MyCarsData by value: owned moves into the parse
Arc, owned_configs/copy_count move into CarCounts; only the three
commands that need owned/wishlist afterwards retain their own copy
- is_final_build checks the digits directly (no per-call String
allocation) and treats malformed configs as non-final so they
surface in improve.csv; owned_final_configs becomes a borrowed
rid-to-configs map so the target-exclusion check allocates nothing
- Normalize sum arithmetic to u8 in normalize_to_final_build
Docs and structure:
- Document the first-seen-wins tie-breaking in compute_ideal_wishlist
and the non-final/non-best coverage gap in write_trash_csv
- Extract compare_by_rq_config_brand_name shared by compare_records
and the remove.csv sort; drop the unused _output_dir parameter from
run_track; add a retry_delay helper de-duplicating the three HTTP
backoff sites; rename the shadowed `entry` binding to config_data
- Add compile-time const assertions (const _: () block, stable on 1.95)
for MAX_RQ, MIN_VALID_TIME, TIME_TOLERANCE, TEST_BOWL_FACTOR, and
CONCURRENCY_LIMIT so bad constant edits fail the build; remove the
stale comment that incorrectly claimed compile-time validation existed
- Add regression test for the qualifying-count fix: car with 3 configs
where only 2 meet min_count and 2 copies suffice to cover the
qualifying ones (previously phantom-wishlisted via overcounting)
- Use owned String keys in qualifying_per_guid to decouple the map from
guid_to_rid's allocations
- Clarify write_improve_csv doc (exactly one row per car), restore the
remove.csv test section header, add trailing newline to .gitignore
- Count only configs meeting min_count as qualifying in the wishlist
copy-coverage check (previously ALL guid_to_rid entries inflated the
denominator, producing phantom wishlist entries), pre-computed once
per guid instead of per record
- Update stale write_improve_csv doc to match single-best-target
behavior including cross-copy exclusion
- Borrow owned configs for the trash CSV join instead of cloning the Vec
- Build owned_final_configs via filter_map + flat_map without the
throwaway intermediate Vec
- Remove runtime assertions on literal constants (clippy
assertions_on_constants) and replace a redundant closure with the
function reference (clippy redundant_closure)
- Apply rustfmt across the crate and drop a duplicated test-section header
- Change owned_configs from HashMap<String, String> to HashMap<String, Vec<String>>
to capture all configs across copies (previously last-write-wins lost data)
- Add copy_count field to MyCarsData and CarCounts for wishlist copy-coverage logic
- Fix improve.csv to exclude already-owned final configs as targets (e.g., Fiat
Campagnola 1974 now targets 332 instead of already-owned 323)
- Fix wishlist to check all owned configs for exact-match and final-build detection
- Fix trash to check all configs for final-build-not-best detection
- Update all tests for new Vec type and add multi-copy improve test
Previously, wishlist cars that weren't best on any track were silently
dropped from remove.csv. Now uses car_map as fallback to include all
non-ideal wishlist cars with available metadata, using an intermediate
RemoveEntry struct. Adds test coverage for this scenario.
- Replace hardcoded v2c_/v1c_ prefix stripping with generic rsplit_once('_')
approach that handles any prefix (v2b_, v3a_, etc.) while validating
the suffix is exactly 3 ASCII digits
- Refactor write_improve_csv to emit one row per normalized config per car
instead of a single aggregated row, giving visibility into all viable
upgrade paths
- Add 4 new tests covering prefix stripping and improve CSV behavior
- Add improve command to all command tables and usage sections
- Add normalized_config field to BestCarRecord struct documentation
- Update CarCounts.by_rid type from String to (String, String)
- Update CarCounts.guid_to_rid type to HashMap<String, HashSet<(String, String)>>
- Add owned_configs field to MyCarsData and CarCounts docs
- Add normalize_to_final_build() to API docs
- Update CSV format examples to include FinalBuild column
- Update my_cars.json structure with engineMajor/weightMajor/chassisMajor
- Update timing file example with v2c_ version prefix
- Add version prefix stripping to data transformation docs
- Update CHANGELOG.md with Unreleased section
- Fix compare_records description to include normalized config sorting
- Add normalize_to_final_build() to map tune configs to final builds (323/233/332)
- Add normalized_config field to BestCarRecord
- Change CarCounts.by_rid key from String to (String, String) for per-config tracking
- Change CarCounts.guid_to_rid to HashMap<String, HashSet<(String, String)>>
- Extract owned tune configs from my_cars.json (engineMajor/weightMajor/chassisMajor)
- Add improve command: owned cars not yet at final build, sorted by track count
- Strip version prefixes (v2c_/v1c_) from config keys at parse time
- Update dedup check in update_best_car to consider normalized_config
- Add atomic_write helper for safe CSV output
- Delete completed plans/code-review-v2.md
- Apply rustfmt formatting across codebase
- Replace stdout output with structured CSV file generation
- Add wishlist, all commands with --min-count and --max-wishlist flags
- Introduce CarCounts struct for shared analysis computation
- Improve error handling: return Result instead of unwrap/panic
- Add prize field to BestCarRecord
- Remove TODO.md
- Add clap dependency with derive feature for structured CLI argument parsing
- Replace env::args() manual matching with clap subcommands (fetch, best, missing, wishlist, trash, details, track)
- Change print_best_car() signature from (&String, &String, &String) to (&str, &str, u8) for idiomatic Rust
- Add TODO.md tracking known tech debt and planned features
- Update CHANGELOG.md with clap migration details and set v0.2.0 release date
- Extract shared types (Car, BestCarRecord, AnalysisContext) into src/lib.rs
- Replace global static BEST_CARS with injected AnalysisContext for testability
- Add semaphore-bounded concurrency (limit 20) in parse_all_times()
- Replace hardcoded track name match with track_names.json loaded via include_str!
- Replace magic number 115 with const MAX_RQ: u8 = 113
- Improve floating-point tie comparison with TIME_TOLERANCE (5ms) constant
- Remove src/club.rs binary (topdrives.club no longer maintained)
- Remove scraper dependency from Cargo.toml
- Remove tracks.txt (replaced by track_names.json)
- Remove empty/placeholder test bodies
- Improve error handling in get_file() for response body read
- Add load_my_cars_data() combining owned + wishlist extraction in one I/O
- Update CHANGELOG, README, and all docs to reflect changes
- Remove once_cell and serde dependencies; use std::sync::LazyLock
and manual serde_json::Value parsing instead
- Add wishlist support: load wishlist from my_cars.json, prefix
missing cars with '+' in missing mode, add 'wishlist' command
to show stale wishlist entries (prefixed with '-')
- Replace unwrap()/expect() with graceful eprintln warnings and
early returns throughout (get_car_list, parse_car_times, etc.)
- Scope all Mutex locks tightly to avoid holding during I/O
(print_best_times, print_best_cars, print_trash, print_wishlist_stale)
- Replace custom split_prefix_postfix with str::split_once
- Pass owned-car set into parse_all_times/parse_car_times via
Arc<HashSet> instead of reading file per task
- Consolidate my_cars.json loading into single load_my_cars_data()
returning both owned and wishlist sets
- Add .gitignore and Cargo.lock to version control
- Report task failures in parse_all_times join loop