Files
gridpilot.gg/adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository.ts
2025-12-15 18:49:10 +01:00

178 lines
8.1 KiB
TypeScript

/**
* Infrastructure Adapter: InMemoryRaceRegistrationRepository
*
* In-memory implementation of IRaceRegistrationRepository.
* Stores race registrations in Maps keyed by raceId and driverId.
*/
import type { ILogger } from '@gridpilot/shared/logging/ILogger';
import type { RaceRegistration } from '@gridpilot/racing/domain/entities/RaceRegistration';
import type { IRaceRegistrationRepository } from '@gridpilot/racing/domain/repositories/IRaceRegistrationRepository';
type RaceRegistrationSeed = Pick<RaceRegistration, 'raceId' | 'driverId' | 'registeredAt'>;
export class InMemoryRaceRegistrationRepository implements IRaceRegistrationRepository {
private registrationsByRace: Map<string, Set<string>>;
private registrationsByDriver: Map<string, Set<string>>;
private readonly logger: ILogger;
constructor(logger: ILogger, seedRegistrations?: RaceRegistrationSeed[]) {
this.logger = logger;
this.logger.info('InMemoryRaceRegistrationRepository initialized.');
this.registrationsByRace = new Map();
this.registrationsByDriver = new Map();
if (seedRegistrations) {
this.logger.debug('Seeding with initial registrations', { count: seedRegistrations.length });
seedRegistrations.forEach((registration) => {
this.addToIndexes(registration.raceId, registration.driverId, registration.registeredAt);
});
}
}
private addToIndexes(raceId: string, driverId: string, _registeredAt: Date): void {
this.logger.debug('Attempting to add race registration to indexes', { raceId, driverId });
let raceSet = this.registrationsByRace.get(raceId);
if (!raceSet) {
raceSet = new Set();
this.registrationsByRace.set(raceId, raceSet);
this.logger.debug('Created new race set as none existed', { raceId });
}
raceSet.add(driverId);
this.logger.debug('Added driver to race set', { raceId, driverId });
let driverSet = this.registrationsByDriver.get(driverId);
if (!driverSet) {
driverSet = new Set();
this.registrationsByDriver.set(driverId, driverSet);
this.logger.debug('Created new driver set as none existed', { driverId });
}
driverSet.add(raceId);
this.logger.debug('Added race to driver set', { raceId, driverId });
this.logger.info('Successfully added race registration to indexes', { raceId, driverId });
}
private removeFromIndexes(raceId: string, driverId: string): void {
this.logger.debug('Attempting to remove race registration from indexes', { raceId, driverId });
const raceSet = this.registrationsByRace.get(raceId);
if (raceSet) {
raceSet.delete(driverId);
this.logger.debug('Removed driver from race set', { raceId, driverId });
if (raceSet.size === 0) {
this.registrationsByRace.delete(raceId);
this.logger.debug('Deleted race set as it is now empty', { raceId });
}
} else {
this.logger.warn('Race set not found during removal, potential inconsistency', { raceId });
}
const driverSet = this.registrationsByDriver.get(driverId);
if (driverSet) {
driverSet.delete(raceId);
this.logger.debug('Removed race from driver set', { raceId, driverId });
if (driverSet.size === 0) {
this.registrationsByDriver.delete(driverId);
this.logger.debug('Deleted driver set as it is now empty', { driverId });
}
} else {
this.logger.warn('Driver set not found during removal, potential inconsistency', { driverId });
}
this.logger.info('Successfully removed race registration from indexes', { raceId, driverId });
}
async isRegistered(raceId: string, driverId: string): Promise<boolean> {
this.logger.info('Checking if driver is registered for race', { raceId, driverId });
const raceSet = this.registrationsByRace.get(raceId);
if (!raceSet) {
this.logger.debug('Race set not found, driver not registered', { raceId, driverId });
return false;
}
const isRegistered = raceSet.has(driverId);
this.logger.debug('Registration status result', { raceId, driverId, isRegistered });
return isRegistered;
}
async getRegisteredDrivers(raceId: string): Promise<string[]> {
this.logger.info('Attempting to fetch registered drivers for race', { raceId });
const raceSet = this.registrationsByRace.get(raceId);
if (!raceSet) {
this.logger.debug('No registered drivers found for race', { raceId });
return [];
}
const drivers = Array.from(raceSet.values());
this.logger.debug('Found registered drivers for race', { raceId, count: drivers.length });
this.logger.info('Successfully fetched registered drivers for race', { raceId, count: drivers.length });
return drivers;
}
async getRegistrationCount(raceId: string): Promise<number> {
this.logger.info('Attempting to get registration count for race', { raceId });
const raceSet = this.registrationsByRace.get(raceId);
const count = raceSet ? raceSet.size : 0;
this.logger.debug('Registration count for race', { raceId, count });
this.logger.info('Returning registration count for race', { raceId, count });
return count;
}
async register(registration: RaceRegistration): Promise<void> {
this.logger.info('Attempting to register driver for race', { raceId: registration.raceId, driverId: registration.driverId });
const alreadyRegistered = await this.isRegistered(registration.raceId, registration.driverId);
if (alreadyRegistered) {
this.logger.warn('Driver already registered for race, registration aborted', { raceId: registration.raceId, driverId: registration.driverId });
throw new Error('Already registered for this race');
}
this.addToIndexes(registration.raceId, registration.driverId, registration.registeredAt);
this.logger.info('Driver successfully registered for race', { raceId: registration.raceId, driverId: registration.driverId });
}
async withdraw(raceId: string, driverId: string): Promise<void> {
this.logger.info('Attempting to withdraw driver from race', { raceId, driverId });
const alreadyRegistered = await this.isRegistered(raceId, driverId);
if (!alreadyRegistered) {
this.logger.warn('Driver not registered for race, withdrawal aborted', { raceId, driverId });
throw new Error('Not registered for this race');
}
this.removeFromIndexes(raceId, driverId);
this.logger.info('Driver successfully withdrew from race', { raceId, driverId });
}
async getDriverRegistrations(driverId: string): Promise<string[]> {
this.logger.info('Attempting to fetch registrations for driver', { driverId });
const driverSet = this.registrationsByDriver.get(driverId);
if (!driverSet) {
this.logger.debug('No registrations found for driver', { driverId });
return [];
}
const registrations = Array.from(driverSet.values());
this.logger.debug('Found registrations for driver', { driverId, count: registrations.length });
this.logger.info('Successfully fetched registrations for driver', { driverId, count: registrations.length });
return registrations;
}
async clearRaceRegistrations(raceId: string): Promise<void> {
this.logger.info('Attempting to clear all registrations for race', { raceId });
const raceSet = this.registrationsByRace.get(raceId);
if (!raceSet) {
this.logger.debug('No registrations to clear for race (race set not found)', { raceId });
return;
}
this.logger.debug('Found registrations to clear', { raceId, count: raceSet.size });
for (const driverId of raceSet.values()) {
const driverSet = this.registrationsByDriver.get(driverId);
if (driverSet) {
driverSet.delete(raceId);
if (driverSet.size === 0) {
this.registrationsByDriver.delete(driverId);
this.logger.debug('Deleted driver set as it is now empty during race clear', { raceId, driverId });
}
} else {
this.logger.warn('Driver set not found during race clear, potential inconsistency', { raceId, driverId });
}
this.logger.debug('Removed race from driver set during race clear', { raceId, driverId });
}
this.registrationsByRace.delete(raceId);
this.logger.info('Successfully cleared all registrations for race', { raceId });
}
}