using FluentValidation; using FluentValidation.Results; using FluentValidation.Validators; using Serilog.Events; namespace Recyclarr.Common.FluentValidation; public static class FluentValidationExtensions { // From: https://github.com/FluentValidation/FluentValidation/issues/1648 // ReSharper disable once UnusedMethodReturnValue.Global public static IRuleBuilderOptions SetNonNullableValidator( this IRuleBuilder ruleBuilder, IValidator validator, params string[] ruleSets) { var adapter = new ChildValidatorAdaptor(validator!, validator.GetType()) { RuleSets = ruleSets }; return ruleBuilder.SetAsyncValidator(adapter); } // ReSharper disable once UnusedMethodReturnValue.Global public static IRuleBuilderOptions SetNonNullableValidator( this IRuleBuilder ruleBuilder, Func validatorProvider, params string[] ruleSets) where TValidator : IValidator { var adapter = new ChildValidatorAdaptor( (context, _) => validatorProvider(context.InstanceToValidate), typeof(TValidator)) { RuleSets = ruleSets }; return ruleBuilder.SetAsyncValidator(adapter); } public static IEnumerable IsValid( this IEnumerable source, TValidator validator, Action, TSource>? handleInvalid = null) where TValidator : IValidator { foreach (var s in source) { var result = validator.Validate(s); if (result is {IsValid: true}) { yield return s; } else { handleInvalid?.Invoke(result.Errors, s); } } } public static LogEventLevel ToLogLevel(this Severity severity) { return severity switch { Severity.Error => LogEventLevel.Error, Severity.Warning => LogEventLevel.Warning, Severity.Info => LogEventLevel.Information, _ => LogEventLevel.Debug }; } public static int LogValidationErrors( this IReadOnlyCollection errors, ILogger log, string errorPrefix) { var numErrors = 0; foreach (var (error, level) in errors.Select(x => (x, x.Severity.ToLogLevel()))) { if (level == LogEventLevel.Error) { ++numErrors; } log.Write(level, "{ErrorPrefix}: {Msg}", errorPrefix, error.ErrorMessage); } return numErrors; } }