namespace FinalFactory.Rendezvous.Capacity; internal sealed record CapacityOptions { public required string Profile { get; init; } public required int Listings { get; init; } public required int Attempts { get; init; } public required int Samples { get; init; } public required int SoakCycles { get; init; } public required int SoakSeconds { get; init; } public string? OutputPath { get; init; } public static CapacityOptions Parse(string[] args) { Dictionary values = ParseArguments(args); string profile = values.GetValueOrDefault("--profile") ?? "quick"; (int listings, int attempts, int samples, int soakCycles, int soakSeconds) = profile switch { "quick" => (1_000, 500, 100, 20, 0), "candidate" => (25_000, 10_000, 1_000, 1_000, 300), _ => throw new ArgumentException("--profile must be 'quick' or 'candidate'."), }; return new() { Profile = profile, Listings = ParsePositive(values.GetValueOrDefault("--listings"), listings, "--listings"), Attempts = ParsePositive(values.GetValueOrDefault("--attempts"), attempts, "--attempts"), Samples = ParsePositive(values.GetValueOrDefault("--samples"), samples, "--samples"), SoakCycles = ParsePositive( values.GetValueOrDefault("--soak-cycles"), soakCycles, "--soak-cycles"), SoakSeconds = ParseNonNegative( values.GetValueOrDefault("--soak-seconds"), soakSeconds, "--soak-seconds"), OutputPath = values.GetValueOrDefault("--output"), }; } private static Dictionary ParseArguments(string[] args) { HashSet allowed = [ "--profile", "--listings", "--attempts", "--samples", "--soak-cycles", "--soak-seconds", "--output", ]; Dictionary values = new(StringComparer.Ordinal); for (int index = 0; index < args.Length; index += 2) { string option = args[index]; if (!allowed.Contains(option)) { throw new ArgumentException($"Unknown option: {option}."); } if (index == args.Length - 1 || args[index + 1].StartsWith("--", StringComparison.Ordinal)) { throw new ArgumentException($"{option} requires a value."); } if (!values.TryAdd(option, args[index + 1])) { throw new ArgumentException($"{option} may be supplied only once."); } } return values; } private static int ParsePositive(string? value, int fallback, string option) => value is null ? fallback : int.TryParse(value, out int parsed) && parsed > 0 ? parsed : throw new ArgumentException($"{option} must be a positive integer."); private static int ParseNonNegative(string? value, int fallback, string option) => value is null ? fallback : int.TryParse(value, out int parsed) && parsed >= 0 ? parsed : throw new ArgumentException($"{option} must be a non-negative integer."); }