Files
gridpilot.gg/core/racing/domain/value-objects/ParticipantCount.ts
2025-12-27 17:53:01 +01:00

121 lines
2.8 KiB
TypeScript

/**
* Domain Value Object: ParticipantCount
*
* Represents the number of participants in a league or race.
* Enforces constraints based on league visibility and other business rules.
*/
import type { IValueObject } from '@core/shared/domain';
import { RacingDomainValidationError } from '../errors/RacingDomainError';
export interface ParticipantCountProps {
value: number;
}
export class ParticipantCount implements IValueObject<ParticipantCountProps> {
readonly value: number;
private constructor(value: number) {
this.value = value;
}
static create(value: number): ParticipantCount {
if (!Number.isInteger(value) || value < 0) {
throw new RacingDomainValidationError('Participant count must be a non-negative integer');
}
return new ParticipantCount(value);
}
/**
* Validate against minimum requirements for ranked leagues
*/
validateForRankedLeague(): { valid: boolean; error?: string } {
if (this.value < 10) {
return {
valid: false,
error: 'Ranked leagues require at least 10 participants'
};
}
return { valid: true };
}
/**
* Validate against minimum requirements for unranked leagues
*/
validateForUnrankedLeague(): { valid: boolean; error?: string } {
if (this.value < 2) {
return {
valid: false,
error: 'Unranked leagues require at least 2 participants'
};
}
return { valid: true };
}
/**
* Validate against maximum capacity
*/
validateAgainstMax(maxParticipants: number): { valid: boolean; error?: string } {
if (this.value > maxParticipants) {
return {
valid: false,
error: `Participant count (${this.value}) exceeds maximum capacity (${maxParticipants})`
};
}
return { valid: true };
}
/**
* Check if count meets minimum for given visibility type
*/
meetsMinimumForVisibility(isRanked: boolean): boolean {
return isRanked ? this.value >= 10 : this.value >= 2;
}
/**
* Increment count by 1
*/
increment(): ParticipantCount {
return new ParticipantCount(this.value + 1);
}
/**
* Decrement count by 1 (if > 0)
*/
decrement(): ParticipantCount {
if (this.value === 0) {
throw new RacingDomainValidationError('Cannot decrement below zero');
}
return new ParticipantCount(this.value - 1);
}
/**
* Check if count is zero
*/
isZero(): boolean {
return this.value === 0;
}
/**
* Check if count is at least the given minimum
*/
isAtLeast(min: number): boolean {
return this.value >= min;
}
get props(): ParticipantCountProps {
return { value: this.value };
}
equals(other: IValueObject<ParticipantCountProps>): boolean {
return this.value === other.props.value;
}
toString(): string {
return this.value.toString();
}
toNumber(): number {
return this.value;
}
}