Commit graph

51 commits

Author SHA1 Message Date
ead4c721d4 Add update.ps1 and full_script.ps1: PS counterparts of the server scripts, run in the repo folder; pull my_cars.json from moria before analysis (moria stays the single source of truth; -NoSync skips)
All checks were successful
build / build-test (push) Successful in 1m31s
2026-08-31 07:24:17 +02:00
7026a0d556 CI: standard build/test pipeline (wave-1 rollout; node-free checkouts, job timeout)
All checks were successful
build / build-test (push) Successful in 1m16s
2026-08-30 13:28:14 +02:00
cc73fe62fc Add progress counter for car time downloads
Show a persistent single-line progress counter ("Downloading track times
for car 1234/7589") while fetch_times downloads timing data. A ProgressBar
from indicatif 0.17 is sized to the car list, cloned into each spawned
download task, and incremented after fetch_car completes so it tracks
completions rather than starts; Arc internals make this safe under the
concurrent semaphore. The bar is cleared via finish_and_clear() before
the error/warning logging loop so retry messages don't garble the line,
and indicatif renders nothing when stderr is not a TTY.

Also apply pending rustfmt reformatting to src/capture/windows.rs
(formatting only, no logic changes).
2026-08-22 10:53:17 +02:00
f829ad52a4 Shut down game, adb and BlueStacks after capture 2026-08-22 10:35:36 +02:00
3de8bf1f80 Silence CONNECT tunnel logging in capture output 2026-08-22 10:34:06 +02:00
ead31529a4 Add desktop launcher script for one-click garage capture 2026-08-22 10:26:38 +02:00
b85672559b Add Windows-only garage capture via MITM proxy and ADB automation 2026-08-22 10:23:56 +02:00
7ac889cbff Handle no-record cars and per-copy trash listing
Empty API responses (200 + empty body) mean "known car, no times
submitted yet" on the records site — typically newly released prize
cars. Previously each such car burned 3 retries with backoff and then
aborted the entire fetch; 27 cars in the current DB hit this.

- Add AppError::EmptyResponse { url } as a distinct, deterministic
  failure class; get_file fails immediately instead of retrying
- fetch_car downgrades EmptyResponse to a skip with an info log, so
  fetch completes; the next fetch picks the cars up automatically
  once records exist
- parse_car_times treats empty (or whitespace-only) timing files
  like missing files, eliminating "EOF while parsing a value at line
  1 column 0" parse errors from stale empty files

trash.csv Config column now lists only the copies to delete: for a
car whose one tune is a final build not best on any track (e.g.
owned 332 next to a best-on-213-tracks 323), only the dead tune is
shown instead of joining all owned configs. Categories 1 and 3 still
list all configs since the whole car goes.

Tests: empty response makes exactly 1 request and yields the
EmptyResponse variant; empty file parses Ok; partial-copy trash
shows only the deletable config.
2026-08-22 07:24:22 +02:00
293a8d7707 Add protect/storage lists, dynamic trash thresholds, tie resolution
Protect & storage lists:
- protect.csv: unlocked garage cars (state != 0, locked == false) worth
  locking more copies of. Needed copies = best configs surviving the
  RQ-band thresholds (prize cars and RQ >= 80 always count); already
  locked copies (copy_count) are subtracted, surplus drops off the list.
- storage.csv: Keep/Trash recommendation for storage cars (state == 0)
  using the same copy-count logic; surplus unlocked copies are fusion
  material.
- user_data.rs now parses card state (garage vs storage), garage slots
  (user.garageSlots/garageSlotsUsed), and unlocked card maps.

Dynamic trash thresholds:
- TrashThresholds derive RQ 20-29/30-39 as half/a quarter of a base
  value (--trash-base-count, default 4); RQ 40+ never covered.
- plan_trash_rules escalates the base automatically while projected free
  garage slots (capacity - used + unlocked non-best copies + new trash
  candidates, copies counted per card) stay below --garage-min-free
  (default 20), stopping when no further cars qualify. Effective
  thresholds and projection are printed.
- Improve-based low-value cars (best target count at or below the band
  threshold) land in trash.csv; collect_improve_entries is shared with
  improve.csv and computed once per run.

Same-car tie resolution:
- resolve_tied_configs collapses tie-equivalent configs of the same car
  per track (compute_car_counts, write_details_csv), preferring the
  player's owned final-build config. Fixes phantom rows like the
  Mercedes 280 SL 332 entry tied at 61.31 by its owned 323, inflated
  per-config track counts, and pointless second-copy recommendations.

Wishlist:
- Prize cars are excluded from the ideal wishlist and surface in
  remove.csv via the ideal-set difference.

CLI: new protect command; --trash-base-count/--garage-min-free on
trash/protect/all; details command reuses counts for tie resolution.
README updated for all new files, flags, and criteria.
2026-08-21 18:45:37 +02:00
ec8be9d138 Enforce feature isolation and expand copy_count test coverage
- 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
2026-08-15 21:30:38 +02:00
1a844e7aa8 Address fourth review round: observability, docs accuracy, dep hygiene, tests
- 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)
2026-08-15 20:14:53 +02:00
58b8d23e68 Address full-codebase review: logging boundary, clone elimination, robustness
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
2026-08-15 19:29:51 +02:00
39d1e1d71b Address review findings: const guards, qualifying-count regression test, owned keys
- 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
2026-08-15 19:07:03 +02:00
2ab66a249b Fix review findings: wishlist qualifying count, improve docs, and lint cleanup
- 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
2026-08-15 18:58:12 +02:00
62688926c3 Fix multi-copy car handling: track all configs per guid and add copy count
- 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
2026-06-06 21:13:47 +02:00
b24b274d25 Fix trash list to detect final builds not best on any track 2026-06-06 18:53:31 +02:00
4ecfc2d14d Fix remove.csv to include wishlist cars not best on any track
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.
2026-05-26 08:52:42 +02:00
fc9f637c60 Generalize config prefix stripping and refactor improve CSV output
- 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
2026-05-11 20:16:09 +02:00
2b0602d564 docs: update documentation for normalized config and improve command
- 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
2026-05-11 16:58:01 +02:00
bf21d3d1f6 feat: add normalized config tracking and improve command
- 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
2026-05-11 16:23:04 +02:00
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
d2c1418e5e docs: update documentation to match refactored codebase
- Delete obsolete plans/code-review.md (all suggestions implemented)
- Rewrite docs/API.md to reflect modular architecture, DashMap, corrected
  AppError variants, accurate function signatures and module locations
- Rewrite docs/ARCHITECTURE.md to reflect DashMap concurrency, remove
  completed future enhancements, fix AnalysisContext description
- Update docs/USER_GUIDE.md with global CLI path options (--my-cars,
  --cars-db, --times-dir, --tracks-db) and updated troubleshooting
2026-05-08 16:36:14 +02:00
a9a46ee551 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)
2026-05-08 16:19:01 +02:00
9d57b18a4e Release v0.3.0: CSV output, wishlist commands, improved error handling
- 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
2026-05-07 20:13:30 +02:00
08c96d9981 Replace manual CLI parsing with clap derive
- 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
2026-05-07 12:27:11 +02:00
89fa69e3c8 refactor: extract lib.rs, remove club module, add bounded concurrency
- 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
2026-05-06 20:20:15 +02:00
6a68469239 Modernize dependencies, add wishlist features, improve error handling
- 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
2026-05-06 00:30:56 +02:00
32c4d3c10e Added documentation
Added test cases
Added comments
2025-10-02 17:35:56 +02:00
7a8d9beedc Exclude RQ 80+ from trash 2025-06-17 13:10:16 +02:00
a109940c5d Fixed warning
Exclude prize cars from trash
2025-06-16 16:07:11 +02:00
b8433909fd Added trash feature 2025-06-16 11:42:39 +02:00
48e623a1a3 Added feature for missing cars 2025-06-16 11:07:14 +02:00
4e299db3c0 Corrected handling of tied times 2025-05-29 10:47:05 +02:00
ad60ab1b11 Sort by RQ and car name rather than RQ and occurences 2025-04-25 09:17:54 +02:00
4a21a552d7 Fixed a bug that was miscounting the best time occurences for cars 2025-04-24 17:30:48 +02:00
5dd278b8f1 Fix: For the best cars the highest RQ of any given track where they were best was printed, instead of the car's RQ. The list was also sorted by that wrong RQ 2025-04-02 14:53:35 +02:00
1a6ea07ed9 Get rid of DB, parse into memory 2025-03-31 12:15:01 +02:00
12a3868a46 Fixes and preparation for in-memory solution 2025-03-30 17:07:36 +02:00
02ff6eddb2 Multi-threaded fetch 2025-03-30 16:33:04 +02:00
431ea2814b Implemented search for best car for track 2025-03-28 16:21:07 +01:00
21e53eb9e2 Perform parsing in-memory 2025-03-28 15:47:09 +01:00
137ebde68b Caching SQL INSERTS 2025-03-23 10:35:55 +01:00
2fa2966aaa Update to TD Records and new cars 2025-02-13 08:18:05 +01:00
5783bb4826 Bugfix: Overflow in the counter 2024-11-21 14:08:31 +01:00
e565866ba8 Added counter for tracks the cars are best for in the non-detailed output 2024-11-21 10:44:43 +01:00
195a306b79 Corrected executable name
Incremented version
Removed outdated imports
2024-10-20 14:07:07 +02:00
a495f4d4d7 Introduced switch for detailed track output 2024-10-19 15:46:32 +02:00
e2efe052b5 Fix: if no rq10 car finishes, the track's best car is not in the list 2024-10-18 15:49:46 +02:00
5c3a05d2e6 Fixed an error with unknown track ids
Fixed track list
2024-10-18 08:11:01 +02:00
Jan-Peter von Hunnius
abda9a3313 Added the tracks a car ist best for to the output plus the car's configuration 2024-10-16 16:24:39 +02:00