namespace Greencore.platform.rules;
/// Resultado explícito de una condición; la evidencia pertenece a esta ejecución.
public sealed record ConditionResult(bool IsSatisfied, object? Evidence = null);
public enum RuleNodeKind { Leaf, AllOf, AnyOf, Not, When, Prerequisite }
/// Definición identificable e inmutable, sin referencias a datos de una ejecución.
public sealed class RuleDefinition
{
public string Id { get; }
public bool IsAsync { get; }
public Func? Condition { get; }
public Func> AsyncCondition { get; }
public RuleDefinition(string id, Func condition)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentNullException.ThrowIfNull(condition);
Id = id;
Condition = condition;
AsyncCondition = (value, context, _) => ValueTask.FromResult(condition(value, context));
}
public RuleDefinition(string id, Func> condition)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentNullException.ThrowIfNull(condition);
Id = id;
IsAsync = true;
AsyncCondition = condition;
}
public RuleNode At(string nodeId, string? path = null) => new(nodeId, RuleNodeKind.Leaf, [], this, path);
public RuleDefinition Select(Func selector)
{
ArgumentNullException.ThrowIfNull(selector);
return Condition is { } sync
? new(Id, (parent, context) => sync(selector(parent), context))
: new(Id, (parent, context, token) => AsyncCondition(selector(parent), context, token));
}
}
public readonly record struct NoRuleContext;
public static class RuleDefinition
{
public static RuleDefinition WithContext(this RuleDefinition definition)
{
ArgumentNullException.ThrowIfNull(definition);
return definition.Condition is { } sync
? new(definition.Id, (value, _) => sync(value, default))
: new(definition.Id, (value, _, token) => definition.AsyncCondition(value, default, token));
}
public static RuleDefinition For(string id, IRule condition)
{
ArgumentNullException.ThrowIfNull(condition);
return new(id, (value, _) => new(condition.IsSatisfiedBy(value)));
}
public static RuleDefinition For(string id, Func condition)
{
ArgumentNullException.ThrowIfNull(condition);
return new(id, (value, context) => new(condition(value, context)));
}
public static RuleDefinition ForAsync(string id, Func> condition)
{
ArgumentNullException.ThrowIfNull(condition);
return new(id, async (value, context, token) => new(await condition(value, context, token).ConfigureAwait(false)));
}
}