namespace GeradoresService.Core; public sealed record GeneratorOption( string Key, string Label, IReadOnlyList Choices, string DefaultValue); public sealed record OptionChoice(string Value, string Label); /// /// Valor gerado. é usado por geradores compostos (ex.: pessoa fictícia). /// public sealed record GeneratedItem(string Value, IReadOnlyDictionary? Fields = null); /// O valor é válido. /// O valor tem o formato deste tipo (mesmo que o dígito de controlo falhe). /// Resumo legível. /// Valor normalizado/formatado, quando válido. /// Passos do cálculo / informação extra. public sealed record ValidationResult( bool IsValid, bool FormatMatch, string Message, string? Normalized = null, IReadOnlyList? Details = null) { public static ValidationResult Valid(string normalized, string message, IReadOnlyList? details = null) => new(true, true, message, normalized, details); public static ValidationResult WrongFormat(string message) => new(false, false, message); public static ValidationResult BadChecksum(string message, IReadOnlyList? details = null) => new(false, true, message, null, details); } /// /// Contexto de geração: fonte de aleatoriedade e opções escolhidas pelo utilizador. /// public sealed class GeneratorContext(Random random, IReadOnlyDictionary options) { public Random Random { get; } = random; public static GeneratorContext Default(Random? random = null) => new(random ?? Random.Shared, new Dictionary()); /// /// Devolve o valor da opção ou o default. O valor tem de ser uma das escolhas válidas. /// public string Option(GeneratorOption option) { if (options.TryGetValue(option.Key, out var value) && !string.IsNullOrWhiteSpace(value)) { var match = option.Choices.FirstOrDefault(c => string.Equals(c.Value, value, StringComparison.OrdinalIgnoreCase)); if (match is null) throw new ArgumentException($"Valor '{value}' inválido para a opção '{option.Key}'."); return match.Value; } return option.DefaultValue; } public string Digits(int length) { Span buffer = stackalloc char[length]; for (var i = 0; i < length; i++) buffer[i] = (char)('0' + Random.Next(10)); return new string(buffer); } public T Pick(IReadOnlyList items) => items[Random.Next(items.Count)]; }