export type GuardResponse = string; // This remains the type for the error message import { Result, ok, err } from './Result'; // Import ok and err factories export interface IGuardArgument { argument: any; argumentName: string; } export type GuardArgumentCollection = IGuardArgument[]; // Define the specific Result type used by Guard functions // Success holds `void` (or `undefined`), Error holds the message string type GuardResult = Result; export class Guard { // Combine function now expects GuardResult[] and returns GuardResult public static combine(guardResults: GuardResult[]): GuardResult { for (const result of guardResults) { // Use the isErr type guard if (result.isErr()) { // Return the first error encountered return result; // No need to cast, it's already Err } } // If all results are Ok, return a single Ok return ok(undefined); // Success case carries no value } public static greaterThan( minValue: number, actualValue: number, ): GuardResult { return actualValue > minValue ? ok(undefined) // Use ok factory : err(`Number given {${actualValue}} is not greater than {${minValue}}`); // Use err factory } public static againstAtLeast(numChars: number, text: string): GuardResult { return text.length >= numChars ? ok(undefined) // Use ok factory : err(`Text is not at least ${numChars} chars.`); // Use err factory } public static againstAtMost(numChars: number, text: string): GuardResult { return text.length <= numChars ? ok(undefined) // Use ok factory : err(`Text is greater than ${numChars} chars.`); // Use err factory } public static againstNullOrUndefined( argument: any, argumentName: string, ): GuardResult { if (argument === null || argument === undefined) { return err(`${argumentName} is null or undefined`); // Use err factory } else { return ok(undefined); // Use ok factory } } public static againstNullOrUndefinedBulk( args: GuardArgumentCollection, ): GuardResult { for (const arg of args) { const result = this.againstNullOrUndefined( arg.argument, arg.argumentName, ); // Use the isErr type guard if (result.isErr()) { return result; // Return the Err result directly } } // If loop completes, all arguments were valid return ok(undefined); // Use ok factory } public static isOneOf( value: any, validValues: any[], argumentName: string, ): GuardResult { let isValid = false; for (const validValue of validValues) { if (value === validValue) { isValid = true; } } if (isValid) { return ok(undefined); // Use ok factory } else { // Use err factory return err( `${argumentName} isn't oneOf the correct types in ${JSON.stringify( validValues, )}. Got "${value}".`, ); } } public static inRange( num: number, min: number, max: number, argumentName: string, ): GuardResult { const isInRange = num >= min && num <= max; if (!isInRange) { // Use err factory return err(`${argumentName} is not within range ${min} to ${max}.`); } else { return ok(undefined); // Use ok factory } } public static allInRange( numbers: number[], min: number, max: number, argumentName: string, ): GuardResult { for (const num of numbers) { const numIsInRangeResult = this.inRange(num, min, max, argumentName); // Use isErr type guard if (numIsInRangeResult.isErr()) { // Return the specific error from inRange return err( `${argumentName} is not within the range. Failed value: ${num}`, ); // Or return numIsInRangeResult directly } } // If loop completes, all numbers are in range return ok(undefined); // Use ok factory } }