mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
add a base type for custom validators
This commit is contained in:
18
CliFx/ArgumentValueValidator.cs
Normal file
18
CliFx/ArgumentValueValidator.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace CliFx
|
||||
{
|
||||
/// <summary>
|
||||
/// A base type for custom validators.
|
||||
/// </summary>
|
||||
public abstract class ArgumentValueValidator<T> : IArgumentValueValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Your validation logic have to be implemented in this method.
|
||||
/// </summary>
|
||||
public abstract ValidationResult Validate(T value);
|
||||
|
||||
/// <summary>
|
||||
/// Non-generic method, will be called by the framework.
|
||||
/// </summary>
|
||||
public ValidationResult Validate(object value) => Validate((T) value);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,11 @@ namespace CliFx.Attributes
|
||||
/// </summary>
|
||||
public Type? Converter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of a converter to use for the option value evaluating.
|
||||
/// </summary>
|
||||
public Type[]? Validators { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace CliFx.Attributes
|
||||
/// </summary>
|
||||
public Type? Converter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of a converter to use for the option value evaluating.
|
||||
/// </summary>
|
||||
public Type[]? Validators { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -19,11 +19,15 @@ namespace CliFx.Domain
|
||||
|
||||
protected Type? Converter { get; set; }
|
||||
|
||||
protected CommandArgumentSchema(PropertyInfo? property, string? description, Type? converter = null)
|
||||
private readonly Type[]? _validators;
|
||||
|
||||
protected CommandArgumentSchema(PropertyInfo? property, string? description, Type? converter = null, Type[]? validators = null)
|
||||
{
|
||||
Property = property;
|
||||
Description = description;
|
||||
Converter = converter;
|
||||
|
||||
_validators = validators;
|
||||
}
|
||||
|
||||
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
||||
@@ -120,8 +124,15 @@ namespace CliFx.Domain
|
||||
}
|
||||
}
|
||||
|
||||
public void BindOn(ICommand command, IReadOnlyList<string> values) =>
|
||||
Property?.SetValue(command, Convert(values));
|
||||
public void BindOn(ICommand command, IReadOnlyList<string> values)
|
||||
{
|
||||
var value = Convert(values);
|
||||
|
||||
if (_validators.NotEmpty())
|
||||
Validate(value);
|
||||
|
||||
Property?.SetValue(command, value);
|
||||
}
|
||||
|
||||
public void BindOn(ICommand command, params string[] values) =>
|
||||
BindOn(command, (IReadOnlyList<string>) values);
|
||||
@@ -141,6 +152,25 @@ namespace CliFx.Domain
|
||||
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
private void Validate(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
return;
|
||||
|
||||
var failed = new List<ValidationResult>();
|
||||
foreach (var validator in _validators!)
|
||||
{
|
||||
var result = validator.InstanceOf<IArgumentValueValidator>().Validate(value!);
|
||||
if (result.IsValid)
|
||||
continue;
|
||||
|
||||
failed.Add(result);
|
||||
}
|
||||
|
||||
if (failed.NotEmpty())
|
||||
throw CliFxException.ValueValidationFailed(this, failed.Select(x => x.ErrorMessage!));
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class CommandArgumentSchema
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -24,8 +25,9 @@ namespace CliFx.Domain
|
||||
string? environmentVariableName,
|
||||
bool isRequired,
|
||||
string? description,
|
||||
Type? converter = null)
|
||||
: base(property, description, converter)
|
||||
Type? converter = null,
|
||||
Type[]? validators = null)
|
||||
: base(property, description, converter, validators)
|
||||
{
|
||||
Name = name;
|
||||
ShortName = shortName;
|
||||
@@ -99,7 +101,8 @@ namespace CliFx.Domain
|
||||
attribute.EnvironmentVariableName,
|
||||
attribute.IsRequired,
|
||||
attribute.Description,
|
||||
attribute.Converter
|
||||
attribute.Converter,
|
||||
attribute.Validators
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -12,8 +13,14 @@ namespace CliFx.Domain
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description, Type? converter = null)
|
||||
: base(property, description, converter)
|
||||
public CommandParameterSchema(
|
||||
PropertyInfo? property,
|
||||
int order,
|
||||
string name,
|
||||
string? description,
|
||||
Type? converter = null,
|
||||
Type[]? validators = null)
|
||||
: base(property, description, converter, validators)
|
||||
{
|
||||
Order = order;
|
||||
Name = name;
|
||||
@@ -52,7 +59,8 @@ namespace CliFx.Domain
|
||||
attribute.Order,
|
||||
name,
|
||||
attribute.Description,
|
||||
attribute.Converter
|
||||
attribute.Converter,
|
||||
attribute.Validators
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,5 +389,13 @@ Unrecognized options provided:
|
||||
|
||||
return new CliFxException(message.Trim());
|
||||
}
|
||||
|
||||
internal static CliFxException ValueValidationFailed(CommandArgumentSchema argument, IEnumerable<string> errors)
|
||||
{
|
||||
var message = $@"
|
||||
The validation of the provided value for {argument.Property!.Name} is failed because: {errors.JoinToString(Environment.NewLine)}";
|
||||
|
||||
return new CliFxException(message.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
7
CliFx/IArgumentValueValidator.cs
Normal file
7
CliFx/IArgumentValueValidator.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CliFx
|
||||
{
|
||||
internal interface IArgumentValueValidator
|
||||
{
|
||||
ValidationResult Validate(object value);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CliFx.Internal.Extensions
|
||||
{
|
||||
@@ -9,5 +10,11 @@ namespace CliFx.Internal.Extensions
|
||||
foreach (var item in items)
|
||||
source.Remove(item);
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? source) =>
|
||||
!source?.Any() ?? true;
|
||||
|
||||
public static bool NotEmpty<T>(this IEnumerable<T>? source) =>
|
||||
!source.IsNullOrEmpty();
|
||||
}
|
||||
}
|
||||
30
CliFx/ValidationResult.cs
Normal file
30
CliFx/ValidationResult.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace CliFx
|
||||
{
|
||||
/// <summary>
|
||||
/// A tiny object that represents a result of the validation.
|
||||
/// </summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// False if there is no error message, otherwise - true.
|
||||
/// </summary>
|
||||
public bool IsValid => ErrorMessage == null;
|
||||
|
||||
/// <summary>
|
||||
/// Contains an information about the reasons of failed validation.
|
||||
/// </summary>
|
||||
public string? ErrorMessage { get; private set; }
|
||||
|
||||
private ValidationResult() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates Ok result, means that the validation is passed.
|
||||
/// </summary>
|
||||
public static ValidationResult Ok() => new ValidationResult() { };
|
||||
|
||||
/// <summary>
|
||||
/// Creates Error result, means that the validation failed.
|
||||
/// </summary>
|
||||
public static ValidationResult Error(string message) => new ValidationResult() { ErrorMessage = message };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user