feat: create request projects for basic CQRS

This commit is contained in:
Louis Seubert 2026-05-08 20:26:26 +02:00
commit d614788e06
Signed by: louis9902
GPG key ID: 4B9DB28F826553BD
190 changed files with 12236 additions and 0 deletions

View file

@ -0,0 +1,119 @@
// Copyright (c) The Geekeey Authors
// SPDX-License-Identifier: EUPL-1.2
using System.Linq.Expressions;
namespace Geekeey.Request.Validation;
internal abstract record Rule
{
protected Rule(Expression expression)
{
PropertyPath = PropertyPath.FromExpression(expression);
}
protected PropertyPath GetPropertyPath()
{
return PropertyPathTransform(PropertyPath);
}
public PropertyPath PropertyPath { get; }
public Severity Severity { get; init; } = Severity.Error;
public string? Code { get; init; }
public Func<PropertyPath, PropertyPath> PropertyPathTransform { get; init; } = static path => path;
public abstract IEnumerable<Problem> Validate(ValidationContext context);
}
internal abstract record Rule<T, TProperty> : Rule
{
protected Rule(Expression expression)
: base(expression)
{
}
public IReadOnlyList<IRuleStep<TProperty>> Steps { get; init; } = [];
public override IEnumerable<Problem> Validate(ValidationContext context)
{
if (context.Instance is T instance)
{
return Validate(instance, context);
}
if (context.Instance is null && default(T) is null)
{
return Validate((T)context.Instance!, context);
}
var actualType = context.Instance?.GetType().FullName ?? "null";
throw new InvalidOperationException(
$"Expected validation context instance of type '{typeof(T).FullName}', but got '{actualType}'.");
}
protected abstract IEnumerable<Problem> Validate(T instance, ValidationContext context);
}
internal sealed record PropertyRule<T, TProperty> : Rule<T, TProperty>
{
private readonly Func<T, TProperty> _accessor;
public PropertyRule(Expression<Func<T, TProperty>> expression) : base(expression)
{
_accessor = expression.Compile();
}
protected override IEnumerable<Problem> Validate(T instance, ValidationContext context)
{
if (Steps.Count is 0)
{
return [];
}
var value = _accessor(instance);
var propertyPath = GetPropertyPath();
return Steps.SelectMany(step => step.Validate(value, context, propertyPath, Code, Severity));
}
}
internal sealed record CollectionRule<T, TElement> : Rule<T, TElement>
{
private readonly Func<T, IEnumerable<TElement>> _accessor;
public CollectionRule(Expression<Func<T, IEnumerable<TElement>>> expression) : base(expression)
{
_accessor = expression.Compile();
}
protected override IEnumerable<Problem> Validate(T instance, ValidationContext context)
{
if (Steps.Count is 0)
{
yield break;
}
if (_accessor(instance) is not { } collection)
{
yield break;
}
var index = 0;
foreach (var element in collection)
{
var propertyPath = GetPropertyPath() + index;
foreach (var step in Steps)
{
foreach (var problem in step.Validate(element, context, propertyPath, Code, Severity))
{
yield return problem;
}
}
index++;
}
}
}