top_drives/docs/ARCHITECTURE.md
Jan-Peter von Hunnius 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

7.1 KiB

Architecture Documentation

System Overview

The Top Drives Records Analyzer consists of a main analysis binary and a shared library working together to provide comprehensive car performance analysis for the Top Drives racing game.

Component Architecture

1. Records Module (src/records.rs)

Primary analysis engine that processes pre-downloaded JSON timing data.

Key Responsibilities

  • Data Fetching: Downloads car and track databases from GitHub repository
  • File Processing: Parses JSON timing files for each car configuration
  • Performance Analysis: Compares timing data across RQ ranges and track conditions
  • Collection Management: Identifies owned cars and optimization opportunities

Key Constants

  • MAX_RQ: u8 = 113 — Maximum RQ level used for range analysis (replaces former magic number 115)

Data Flow

GitHub Repository
       ↓
cars_repo.json + tracks_repo.json
       ↓
times/*.json (1000+ car files)
       ↓
JSON Parsing → BestCarRecord Analysis
       ↓
User Collection (my_cars.json)
       ↓
Output: CSV/Terminal Results

2. Shared Library (src/lib.rs)

Common utilities shared across the project.

Key Responsibilities

  • Track Name Translation: Converts track codes to human-readable names via translate_track_name()
  • Track Type Translation: Converts condition codes to surface descriptions via translate_track_type()

Configuration

  • Track name mappings are loaded from track_names.json at compile time using include_str!
  • This external config file maps track codes (e.g., "laguna") to human-readable names (e.g., "Laguna Seca")

Data Structures

Core Types

TripletKey

type TripletKey = (String, u8, String);
//   ↓           ↓        ↓
// Track ID    RQ Level  Surface Condition

Purpose: Composite key for uniquely identifying track conditions Usage: HashMap key for storing best car records

Car

struct Car {
    rid: String,        // Repository ID
    brand: String,      // Manufacturer
    only_name: String,  // Model name
    year: u16,         // Model year
    rq: u8,           // Rarity level
    prize: bool,       // Prize car status
    guid: String,      // Game GUID
}

BestCarRecord

struct BestCarRecord {
    car_rid: String,   // Car identifier
    brand: String,     // Manufacturer
    only_name: String, // Model name
    year: u16,        // Year
    rq: u8,           // RQ level
    config: String,    // Tune config (111, 233, etc.)
    time: f64,        // Track time (seconds)
    owned: bool,      // User ownership status
    guid: String,     // Game identifier
}

Global State Management

BEST_CARS Cache

static BEST_CARS: Lazy<Mutex<HashMap<TripletKey, Vec<BestCarRecord>>>>

Thread Safety: Protected by Mutex for concurrent access during parsing Initialization: Uses once_cell::Lazy for one-time setup Structure: HashMap with composite keys for efficient lookups

Algorithm Details

RQ Range Analysis

The system analyzes car performance across multiple RQ brackets:

  1. Base RQ: Car's natural rarity level
  2. RQ Range: Tests from car's RQ up to MAX_RQ (113)
  3. Cross-RQ Competition: Allows cars to compete above their base level

Example: A RQ 85 car might perform well at RQ 90-95 levels against higher-rated cars

Time Validation

Multiple validation layers ensure data quality:

  1. Minimum Time: Times below 1.2 seconds are discarded
  2. Downvote Filter: Community-downvoted times are excluded
  3. Special Cases: Test Bowl track uses inverted time calculation (2000.0 / time)

Concurrent Processing

Bounded Concurrency Pattern

  • Task Spawning: One async task per car file
  • Semaphore: Limits concurrent operations to 20 (bounded concurrency)
  • Error Isolation: Individual car failures don't stop batch processing

Synchronization

  • Join Handles: Waits for all parsing tasks to complete
  • Mutex Protection: Safe concurrent access to shared BEST_CARS cache

File Organization

Input Files

├── cars_repo.json          # Car database from repository
├── tracks_repo.json        # Track definitions
├── track_names.json        # Track code → name mappings (compiled in)
├── my_cars.json           # User's car collection (game export)
└── times/                 # Individual car timing files
    ├── Ferrari_F40_1987.json
    ├── McLaren_F1_1993.json
    └── ...

Output Files

├── best.csv              # Best cars analysis results
├── details.csv           # Detailed timing data
├── add.csv               # Cars to add to wishlist
├── remove.csv            # Cars to remove from wishlist
├── trash.csv             # Owned cars safe to remove
├── sorted.csv            # Best cars sorted by count
├── very_best.csv         # Best cars with count ≥ 10
└── wishlist.csv          # Missing cars already on wishlist

Performance Characteristics

Records Module

  • Startup Time: Fast (uses cached JSON files)
  • Memory Usage: Moderate (loads all timing data into memory)
  • Accuracy: High (comprehensive dataset)
  • Update Frequency: Manual refresh required
  • Concurrency: Bounded at 20 simultaneous operations via semaphore

Error Handling Strategy

Graceful Degradation

  • Missing Files: Continue with available data
  • Network Errors: Log errors but don't crash
  • Parse Failures: Use default values or skip invalid entries

User Feedback

  • Progress Indication: Shows fetching/download progress
  • Error Messages: Clear descriptions of what went wrong

Extension Points

Adding New Tracks

  1. Update tracks_repo.json with new track definitions
  2. Add track name mapping in track_names.json
  3. Add condition mappings in translate_track_type()

Adding New Analysis Types

  1. Create new output functions (similar to print_best_cars())
  2. Add command parsing in main function
  3. Update help text and documentation

Security Considerations

Network Access

  • HTTPS Only: All external requests use HTTPS
  • Certificate Validation: Accepts invalid certificates for compatibility
  • Rate Limiting: Respects server limits with controlled concurrency (semaphore limit of 20)

Local File Access

  • Read Only: No modification of game files
  • User Data: Only reads exported collection data
  • No Persistence: Analysis results are output-only

Future Enhancements

Potential Improvements

  1. Caching Layer: Cache parsed results between runs
  2. Database Backend: Replace in-memory HashMap with persistent storage
  3. Configuration Files: External configuration for analysis parameters
  4. GUI Interface: Web-based interface for non-technical users
  5. Export Formats: Multiple output formats (JSON, XML, etc.)

Scalability Considerations

  1. Pagination: Handle large datasets with pagination
  2. Incremental Updates: Only fetch changed data
  3. Compression: Compress large timing files
  4. Streaming: Process data without loading everything into memory