< Summary

Information
Class: NexusLabs.Needlr.FluentValidation.FluentValidationOptionsAdapter<T>
Assembly: NexusLabs.Needlr.FluentValidation
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.FluentValidation/FluentValidationOptionsAdapter.cs
Line coverage
95%
Covered lines: 20
Uncovered lines: 1
Coverable lines: 21
Total lines: 106
Line coverage: 95.2%
Branch coverage
88%
Covered branches: 16
Total branches: 18
Branch coverage: 88.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
Validate(...)100%1414100%
FormatError(...)50%2266.66%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.FluentValidation/FluentValidationOptionsAdapter.cs

#LineLine coverage
 1using FluentValidation;
 2using FluentValidation.Results;
 3
 4using Microsoft.Extensions.Options;
 5
 6using NexusLabs.Needlr.Generators;
 7
 8namespace NexusLabs.Needlr.FluentValidation;
 9
 10/// <summary>
 11/// Adapts a FluentValidation <see cref="IValidator{T}"/> to work as an
 12/// <see cref="IValidateOptions{TOptions}"/> for Microsoft.Extensions.Options.
 13/// </summary>
 14/// <typeparam name="TOptions">The options type being validated.</typeparam>
 15/// <remarks>
 16/// <para>
 17/// This adapter allows FluentValidation validators to be used seamlessly with
 18/// Needlr's <c>[Options(ValidateOnStart = true)]</c> attribute. The adapter
 19/// translates FluentValidation's <see cref="ValidationResult"/> into the
 20/// <see cref="ValidateOptionsResult"/> expected by the options framework.
 21/// </para>
 22/// <para>
 23/// Usage with Needlr source generation:
 24/// <code>
 25/// [Options("Database", ValidateOnStart = true, Validator = typeof(DatabaseOptionsValidator))]
 26/// public class DatabaseOptions { ... }
 27///
 28/// public class DatabaseOptionsValidator : AbstractValidator&lt;DatabaseOptions&gt;
 29/// {
 30///     public DatabaseOptionsValidator()
 31///     {
 32///         RuleFor(x => x.ConnectionString).NotEmpty();
 33///     }
 34/// }
 35/// </code>
 36/// </para>
 37/// <para>
 38/// Register the adapter in your DI container:
 39/// <code>
 40/// services.AddFluentValidationOptionsAdapter&lt;DatabaseOptions, DatabaseOptionsValidator&gt;();
 41/// </code>
 42/// </para>
 43/// </remarks>
 44public sealed class FluentValidationOptionsAdapter<TOptions> : IValidateOptions<TOptions>
 45    where TOptions : class
 46{
 47    private readonly IValidator<TOptions> _validator;
 48    private readonly string? _name;
 49
 50    /// <summary>
 51    /// Creates a new adapter for the specified FluentValidation validator.
 52    /// </summary>
 53    /// <param name="validator">The FluentValidation validator to adapt.</param>
 54    /// <param name="name">Optional name for named options validation.</param>
 955    public FluentValidationOptionsAdapter(IValidator<TOptions> validator, string? name = null)
 56    {
 957        _validator = validator ?? throw new ArgumentNullException(nameof(validator));
 958        _name = name;
 959    }
 60
 61    /// <summary>
 62    /// Validates the specified options instance.
 63    /// </summary>
 64    /// <param name="name">The name of the options instance being validated.</param>
 65    /// <param name="options">The options instance to validate.</param>
 66    /// <returns>A <see cref="ValidateOptionsResult"/> indicating success or failure.</returns>
 67    public ValidateOptionsResult Validate(string? name, TOptions options)
 68    {
 69        // If this adapter is for a specific named options, skip validation for other names
 870        if (_name != null && _name != name)
 71        {
 172            return ValidateOptionsResult.Skip;
 73        }
 74
 775        if (options == null)
 76        {
 177            return ValidateOptionsResult.Fail($"Options of type {typeof(TOptions).Name} cannot be null.");
 78        }
 79
 680        var result = _validator.Validate(options);
 81
 682        if (result.IsValid)
 83        {
 284            return ValidateOptionsResult.Success;
 85        }
 86
 487        var errors = result.Errors
 488            .Where(f => f.Severity == Severity.Error)
 489            .Select(FormatError)
 490            .ToList();
 91
 492        return errors.Count > 0
 493            ? ValidateOptionsResult.Fail(errors)
 494            : ValidateOptionsResult.Success;
 95    }
 96
 97    private static string FormatError(ValidationFailure failure)
 98    {
 399        if (string.IsNullOrEmpty(failure.PropertyName))
 100        {
 0101            return failure.ErrorMessage;
 102        }
 103
 3104        return $"{failure.PropertyName}: {failure.ErrorMessage}";
 105    }
 106}