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!)));
}
}