diff --git a/src/ValidatableExtensions/ValidatableExtensions.CollectionProperties.cs b/src/ValidatableExtensions/ValidatableExtensions.CollectionProperties.cs index 09551d3..d67cbe2 100644 --- a/src/ValidatableExtensions/ValidatableExtensions.CollectionProperties.cs +++ b/src/ValidatableExtensions/ValidatableExtensions.CollectionProperties.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Linq.Expressions; namespace Throw; @@ -231,4 +232,90 @@ public static ref readonly Validatable IfNotContains + /// Throws an exception if the collection returned from the given contains the specified element . + /// Important note: if the collection is a non-evaluated expression, the expression will be evaluated. + /// + /// + /// The default exception thrown is an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly Validatable IfAny(this in Validatable validatable, Func func, Expression> predicate, [CallerArgumentExpression("func")] string? funcName = null) + where TValue : notnull + where TCollectionType : IEnumerable + { + + Validator.ThrowIfContainsElement(func(validatable.Value), predicate,$"{validatable.ParamName}: {funcName}", validatable.ExceptionCustomizations); + + return ref validatable; + } + + /// + /// Throws an exception if the collection returned from the given contains exactly one occurence of the specified element . + /// Important note: if the collection is a non-evaluated expression, the expression will be evaluated. + /// + /// + /// The default exception thrown is an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly Validatable IfSingle(this in Validatable validatable, Func func, dynamic element, [CallerArgumentExpression("func")] string? funcName = null) + where TValue : notnull + where TCollectionType : IEnumerable + { + Validator.ThrowIfContainsSingleElement(func(validatable.Value), element, $"{validatable.ParamName}: {funcName}", validatable.ExceptionCustomizations); + + return ref validatable; + } + + /// + /// Throws an exception if the collection returned from the given contains multiple occurence of the specified element . + /// Important note: if the collection is a non-evaluated expression, the expression will be evaluated. + /// + /// + /// The default exception thrown is an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly Validatable IfNotSingle(this in Validatable validatable, Func func, dynamic element, [CallerArgumentExpression("func")] string? funcName = null) + where TValue : notnull + where TCollectionType : IEnumerable + { + Validator.ThrowIfContainsNoSingleElement(func(validatable.Value), element, $"{validatable.ParamName}: {funcName}", validatable.ExceptionCustomizations); + + return ref validatable; + } + + /// + /// Throws an exception if the collection returned from the given contains no occurence of the specified element . + /// Important note: if the collection is a non-evaluated expression, the expression will be evaluated. + /// + /// + /// The default exception thrown is an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly Validatable IfNone(this in Validatable validatable, Func func, dynamic element, [CallerArgumentExpression("func")] string? funcName = null) + where TValue : notnull + where TCollectionType : IEnumerable + { + Validator.ThrowIfContainsNone(func(validatable.Value), element, $"{validatable.ParamName}: {funcName}", validatable.ExceptionCustomizations); + + return ref validatable; + } + + /// + /// Throws an exception if the collection returned from the given has all items matching the specified element . + /// Important note: if the collection is a non-evaluated expression, the expression will be evaluated. + /// + /// + /// The default exception thrown is an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly Validatable IfAll(this in Validatable validatable, Func func, dynamic element, [CallerArgumentExpression("func")] string? funcName = null) + where TValue : notnull + where TCollectionType : IEnumerable + { + Validator.ThrowIfAll(func(validatable.Value), element, $"{validatable.ParamName}: {funcName}", validatable.ExceptionCustomizations); + + return ref validatable; + } } \ No newline at end of file diff --git a/src/Validators/Validator.Collections.cs b/src/Validators/Validator.Collections.cs index db909d6..203b19b 100644 --- a/src/Validators/Validator.Collections.cs +++ b/src/Validators/Validator.Collections.cs @@ -1,6 +1,7 @@ namespace Throw; using System.Collections; +using System.Linq.Expressions; internal static partial class Validator { @@ -152,4 +153,83 @@ static int GetEnumeratedCount(IEnumerable enumerable) _ => GetEnumeratedCount(value) }; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfContainsElement(TValue value, Expression> predicate, string paramName, ExceptionCustomizations? exceptionCustomizations) + where TValue : notnull, IEnumerable + { + foreach (var item in value) + { + if (item) + { + ExceptionThrower.Throw(paramName, exceptionCustomizations, $"Collection should not contain element {element}."); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfContainsSingleElement(TValue value, dynamic element, string paramName, ExceptionCustomizations? exceptionCustomizations) + where TValue : notnull, IEnumerable + { + var count = 0; + foreach (var item in value) + { + if (item == element) + { + count++; + if (count > 1) break; + } + } + if (count == 1) ExceptionThrower.Throw(paramName, exceptionCustomizations, $"Collection should not contain a single element having value of {element}."); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfContainsNoSingleElement(TValue value, dynamic element, string paramName, ExceptionCustomizations? exceptionCustomizations) + where TValue : notnull, IEnumerable + { + var count = 0; + foreach (var item in value) + { + if (item == element) + { + count++; + if (count > 1) break; + } + } + if (count > 1) ExceptionThrower.Throw(paramName, exceptionCustomizations, $"Collection should not contain multiple occurences of element having value of {element}."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfContainsNone(TValue value, dynamic element, string paramName, ExceptionCustomizations? exceptionCustomizations) + where TValue : notnull, IEnumerable + { + var count = 0; + foreach (var item in value) + { + if (item == element) + { + count++; + if (count > 0) break; + } + } + if (count > 0) ExceptionThrower.Throw(paramName, exceptionCustomizations, $"Collection should contain at least one occurences of element having value of {element}."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfAll(TValue value, dynamic element, string paramName, ExceptionCustomizations? exceptionCustomizations) + where TValue : notnull, IEnumerable + { + bool notAll = false; + foreach (var item in value) + { + if (item != element) + { + notAll = true; + if (notAll) break; + } + } + if (!notAll) ExceptionThrower.Throw(paramName, exceptionCustomizations, $"Collection should contain at least one occurences of an element not of the same value as the element {element}."); + } + } \ No newline at end of file