using Greencore.platform.rules; namespace Greencore.platform.validators; /// Contrato de una regla completa, independiente de su aparición en el árbol. public interface IValidationDefinition { RuleDefinition Definition { get; } RuleDiagnosticDefinition Diagnostic { get; } } /// Marca una regla que puede utilizarse con cualquier contexto de ejecución. public interface ILocalValidationDefinition : IValidationDefinition { } public class ValidationDefinition : IValidationDefinition { public RuleDefinition Definition { get; } public RuleDiagnosticDefinition Diagnostic { get; } public ValidationDefinition(RuleDefinition definition, RuleDiagnosticDefinition diagnostic) { ArgumentNullException.ThrowIfNull(definition); ArgumentNullException.ThrowIfNull(diagnostic); Definition = definition; Diagnostic = diagnostic; } } public sealed class LocalValidationDefinition : ValidationDefinition, ILocalValidationDefinition { public LocalValidationDefinition(RuleDefinition definition, RuleDiagnosticDefinition diagnostic) : base(definition, diagnostic) { } } /// Vincula condición y evidencia por tipo antes de construir el mensaje. public sealed record ConditionResult(bool IsSatisfied, TEvidence Evidence); public static class ValidationRule { public static LocalValidationDefinition ForAsync(string id, Func> condition, string code, string message) { ArgumentNullException.ThrowIfNull(condition); return new(RuleDefinition.ForAsync(id, (value, _, token) => condition(value, token)), new(code, message)); } /// Adapta directamente una regla asíncrona local a una definición diagnosticable. public static LocalValidationDefinition ForAsync(string id, IAsyncRule condition, string code, string message) { ArgumentNullException.ThrowIfNull(condition); return ForAsync(id, condition.IsSatisfiedByAsync, code, message); } public static ValidationDefinition WithEvidence(string id, Func> condition, string code, Func message) { ArgumentNullException.ThrowIfNull(condition); ArgumentNullException.ThrowIfNull(message); return new(new(id, (value, context) => { var result = condition(value, context) ?? throw new InvalidOperationException("The condition returned no result."); return new(result.IsSatisfied, result.Evidence); }), new(code, evaluation => message((E)evaluation.Evidence!))); } public static LocalValidationDefinition WithEvidenceAsync(string id, Func>> condition, string code, Func message) { ArgumentNullException.ThrowIfNull(condition); var definition = WithEvidenceAsync(id, (value, _, token) => condition(value, token), code, message); return new(definition.Definition, definition.Diagnostic); } public static LocalValidationDefinition For(string id, IRule condition, string code, string message) => new(RuleDefinition.For(id, condition), new(code, message)); public static LocalValidationDefinition For(string id, Func condition, string code, string message) => For(id, new PredicateRule(condition), code, message); public static ValidationDefinition For(string id, Func condition, string code, string message) => new(RuleDefinition.For(id, condition), new(code, message)); public static ValidationDefinition ForAsync(string id, Func> condition, string code, string message) => new(RuleDefinition.ForAsync(id, condition), new(code, message)); /// Adapta directamente una regla asíncrona que recibe contexto. public static ValidationDefinition ForAsync(string id, IContextRule condition, string code, string message) { ArgumentNullException.ThrowIfNull(condition); return ForAsync(id, condition.IsSatisfiedByAsync, code, message); } public static LocalValidationDefinition WithEvidence(string id, Func> condition, string code, Func message) { ArgumentNullException.ThrowIfNull(condition); ArgumentNullException.ThrowIfNull(message); return new(new(id, (value, _) => { var result = condition(value) ?? throw new InvalidOperationException("The condition returned no result."); return new(result.IsSatisfied, result.Evidence); }), new(code, evaluation => message((E)evaluation.Evidence!))); } public static ValidationDefinition WithEvidenceAsync(string id, Func>> condition, string code, Func message) { ArgumentNullException.ThrowIfNull(condition); ArgumentNullException.ThrowIfNull(message); return new(new(id, async (value, context, token) => { var result = await condition(value, context, token).ConfigureAwait(false) ?? throw new InvalidOperationException("The condition returned no result."); return new(result.IsSatisfied, result.Evidence); }), new(code, evaluation => message((E)evaluation.Evidence!))); } }