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>
|
/// </summary>
|
||||||
public Type? Converter { get; set; }
|
public Type? Converter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of a converter to use for the option value evaluating.
|
||||||
|
/// </summary>
|
||||||
|
public Type[]? Validators { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ namespace CliFx.Attributes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Type? Converter { get; set; }
|
public Type? Converter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of a converter to use for the option value evaluating.
|
||||||
|
/// </summary>
|
||||||
|
public Type[]? Validators { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
protected Type? Converter { get; set; }
|
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;
|
Property = property;
|
||||||
Description = description;
|
Description = description;
|
||||||
Converter = converter;
|
Converter = converter;
|
||||||
|
|
||||||
|
_validators = validators;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
||||||
@@ -120,8 +124,15 @@ namespace CliFx.Domain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BindOn(ICommand command, IReadOnlyList<string> values) =>
|
public void BindOn(ICommand command, IReadOnlyList<string> values)
|
||||||
Property?.SetValue(command, Convert(values));
|
{
|
||||||
|
var value = Convert(values);
|
||||||
|
|
||||||
|
if (_validators.NotEmpty())
|
||||||
|
Validate(value);
|
||||||
|
|
||||||
|
Property?.SetValue(command, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void BindOn(ICommand command, params string[] values) =>
|
public void BindOn(ICommand command, params string[] values) =>
|
||||||
BindOn(command, (IReadOnlyList<string>) values);
|
BindOn(command, (IReadOnlyList<string>) values);
|
||||||
@@ -141,6 +152,25 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
return Array.Empty<string>();
|
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
|
internal partial class CommandArgumentSchema
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -24,8 +25,9 @@ namespace CliFx.Domain
|
|||||||
string? environmentVariableName,
|
string? environmentVariableName,
|
||||||
bool isRequired,
|
bool isRequired,
|
||||||
string? description,
|
string? description,
|
||||||
Type? converter = null)
|
Type? converter = null,
|
||||||
: base(property, description, converter)
|
Type[]? validators = null)
|
||||||
|
: base(property, description, converter, validators)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
ShortName = shortName;
|
ShortName = shortName;
|
||||||
@@ -99,7 +101,8 @@ namespace CliFx.Domain
|
|||||||
attribute.EnvironmentVariableName,
|
attribute.EnvironmentVariableName,
|
||||||
attribute.IsRequired,
|
attribute.IsRequired,
|
||||||
attribute.Description,
|
attribute.Description,
|
||||||
attribute.Converter
|
attribute.Converter,
|
||||||
|
attribute.Validators
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -12,8 +13,14 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description, Type? converter = null)
|
public CommandParameterSchema(
|
||||||
: base(property, description, converter)
|
PropertyInfo? property,
|
||||||
|
int order,
|
||||||
|
string name,
|
||||||
|
string? description,
|
||||||
|
Type? converter = null,
|
||||||
|
Type[]? validators = null)
|
||||||
|
: base(property, description, converter, validators)
|
||||||
{
|
{
|
||||||
Order = order;
|
Order = order;
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -52,7 +59,8 @@ namespace CliFx.Domain
|
|||||||
attribute.Order,
|
attribute.Order,
|
||||||
name,
|
name,
|
||||||
attribute.Description,
|
attribute.Description,
|
||||||
attribute.Converter
|
attribute.Converter,
|
||||||
|
attribute.Validators
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,5 +389,13 @@ Unrecognized options provided:
|
|||||||
|
|
||||||
return new CliFxException(message.Trim());
|
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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace CliFx.Internal.Extensions
|
namespace CliFx.Internal.Extensions
|
||||||
{
|
{
|
||||||
@@ -9,5 +10,11 @@ namespace CliFx.Internal.Extensions
|
|||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
source.Remove(item);
|
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