From f484c96c7ab90759fc7d2de4680eba9d7d18323f Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 27 Oct 2024 19:39:12 +0100 Subject: [PATCH 001/123] feat: update single-source file for v12.0 Signed-off-by: Kenny Pflug --- Light.GuardClauses.SingleFile.cs | 4166 +++++++++++++++++------------- 1 file changed, 2342 insertions(+), 1824 deletions(-) diff --git a/Light.GuardClauses.SingleFile.cs b/Light.GuardClauses.SingleFile.cs index 95c3eae..55dea4f 100644 --- a/Light.GuardClauses.SingleFile.cs +++ b/Light.GuardClauses.SingleFile.cs @@ -1,11 +1,11 @@ /* ------------------------------ - Light.GuardClauses 10.3.0 + Light.GuardClauses 12.0.0 ------------------------------ License information for Light.GuardClauses The MIT License (MIT) -Copyright (c) 2016, 2023 Kenny Pflug mailto:kenny.pflug@live.de +Copyright (c) 2016, 2024 Kenny Pflug mailto:kenny.pflug@live.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -40,6 +40,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using JetBrains.Annotations; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; #nullable enable annotations namespace Light.GuardClauses @@ -58,7 +59,7 @@ internal static class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class { if (parameter is null) @@ -74,7 +75,7 @@ public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? param /// Your custom exception thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : class { if (parameter is null) @@ -93,7 +94,7 @@ public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? param /// Thrown when is a value type and the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (default(T)is null) { @@ -104,7 +105,11 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [Caller if (EqualityComparer.Default.Equals(parameter, default !)) Throw.ArgumentDefault(parameterName, message); + // If we end up here, we have a value type which cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } /// @@ -115,7 +120,7 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [Caller /// Your custom exception thrown when is the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeDefault([ValidatedNotNull] this T parameter, Func exceptionFactory) + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) { if (default(T)is null) { @@ -126,7 +131,11 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, Func.Default.Equals(parameter, default !)) Throw.CustomException(exceptionFactory); + // If we end up here, we have a value type which cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } /// @@ -140,10 +149,18 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, FuncThrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + + } + if (parameter is null) Throw.ArgumentNull(parameterName, message); return parameter; @@ -159,10 +176,18 @@ public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this /// Your custom exception thrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) { if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + + } + if (parameter is null) Throw.CustomException(exceptionFactory); return parameter; @@ -178,7 +203,7 @@ public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message)is T castValue) return castValue; @@ -194,7 +219,7 @@ public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? p /// Your custom exception thrown when cannot be cast to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) { if (parameter is T castValue) return castValue; @@ -211,7 +236,7 @@ public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? p /// The enum value to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidEnumValue(this T parameter) - where T : Enum => EnumInfo.IsValidEnumValue(parameter); + where T : struct, Enum => EnumInfo.IsValidEnumValue(parameter); /// /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type @@ -224,7 +249,7 @@ public static bool IsValidEnumValue(this T parameter) /// Thrown when is no valid enum value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : Enum + where T : struct, Enum { if (!EnumInfo.IsValidEnumValue(parameter)) Throw.EnumValueNotDefined(parameter, parameterName, message); @@ -243,7 +268,7 @@ public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpress [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) - where T : Enum + where T : struct, Enum { if (!EnumInfo.IsValidEnumValue(parameter)) Throw.CustomException(exceptionFactory, parameter); @@ -363,7 +388,7 @@ public static void InvalidArgument(bool condition, T parameter, FuncThe message that will be passed to the resulting exception (optional). /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustHaveValue([NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct { if (!parameter.HasValue) @@ -379,7 +404,7 @@ public static T MustHaveValue([NoEnumeration] this T? parameter, [CallerArgum /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NoEnumeration] this T? parameter, Func exceptionFactory) + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct { if (!parameter.HasValue) @@ -705,1170 +730,1015 @@ public static T MustNotBe(this T parameter, T other, IEqualityComparer equ /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); - /* - * ------------------------------------- - * Must Not Be Less Than - * Must Be Greater Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustNotBeLessThan(parameter, other, parameterName, message); + if (parameter.Kind != DateTimeKind.Utc) + Throw.MustBeUtcDateTime(parameter, parameterName, message); return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter.Kind != DateTimeKind.Utc) + Throw.CustomException(exceptionFactory, parameter); return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + if (parameter.Kind != DateTimeKind.Local) + Throw.MustBeLocalDateTime(parameter, parameterName, message); return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter.Kind != DateTimeKind.Local) + Throw.CustomException(exceptionFactory, parameter); return parameter; } - /* - * ------------------------------------- - * Must Be Less Than - * Must Not Be Greater Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustBeLessThan(parameter, other, parameterName, message); + if (parameter.Kind != DateTimeKind.Unspecified) + Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter.Kind != DateTimeKind.Unspecified) + Throw.CustomException(exceptionFactory, parameter); return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Ensures that the collection has the specified number of items, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . + /// The collection to be checked. + /// The number of items the collection must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . + /// Thrown when does not have the specified number of items. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + if (parameter!.Count(parameterName, message) != count) + Throw.InvalidCollectionCount(parameter, count, parameterName, message); return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The collection to be checked. + /// The number of items the collection must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is null || parameter.Count() != count) + Throw.CustomException(exceptionFactory, parameter, count); return parameter; } - /* - * ------------------------------------- - * Must Be Greater Than - * Must Not Be Less Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// Checks if the specified collection is null or empty. /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// The collection to be checked. + /// True if the collection is null or empty, else false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; + /// + /// Ensures that the collection is not null or empty, or otherwise throws an . + /// + /// The collection to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . + /// Thrown when has no items. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustBeGreaterThan(parameter, other, parameterName, message); + if (parameter.Count(parameterName, message) == 0) + Throw.EmptyCollection(parameterName, message); return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The collection to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is null || parameter.Count() == 0) + Throw.CustomException(exceptionFactory, parameter); return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// Ensures that the collection contains the specified item, or otherwise throws a . /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// The collection to be checked. + /// The item that must be part of the collection. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . + /// Thrown when does not contain . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + Throw.MissingItem(parameter, item, parameterName, message); + return parameter; + } + + if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) + Throw.MissingItem(parameter, item, parameterName, message); return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The collection to be checked. + /// The item that must be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + Throw.CustomException(exceptionFactory, parameter, item); + return parameter; + } + + if (parameter is null || !parameter.Contains(item)) + Throw.CustomException(exceptionFactory, parameter, item); return parameter; } - /* - * ------------------------------------- - * Must Not Be Greater Than - * Must Be Less Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Ensures that the collection does not contain the specified item, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . + /// The collection to be checked. + /// The item that must not be part of the collection. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . + /// Thrown when contains . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + if (parameter is ICollection collection) + { + if (collection.Contains(item)) + Throw.ExistingItem(parameter, item, parameterName, message); + return parameter; + } + + if (parameter.MustNotBeNull(parameterName, message).Contains(item)) + Throw.ExistingItem(parameter, item, parameterName, message); return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// The collection to be checked. + /// The item that must not be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is ICollection collection) + { + if (collection.Contains(item)) + Throw.CustomException(exceptionFactory, parameter, item); + return parameter; + } + + if (parameter is null || parameter.Contains(item)) + Throw.CustomException(exceptionFactory, parameter, item); return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Checks if the given is one of the specified . /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. + /// The item to be checked. + /// The collection that might contain the . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static bool IsOneOf(this TItem item, [NotNull, ValidatedNotNull] IEnumerable items) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); - return parameter; + if (items is ICollection collection) + return collection.Contains(item); + if (items is string @string && item is char character) + return @string.IndexOf(character) != -1; + return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the value is one of the specified items, or otherwise throws a . /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// The value to be checked. + /// The items that should contain the value. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to one of the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); + // ReSharper disable PossibleMultipleEnumeration + if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + Throw.ValueNotOneOf(parameter, items, parameterName, message); return parameter; + // ReSharper restore PossibleMultipleEnumeration } - /* - * ------------------------------------- - * Ranges - * ------------------------------------- - */ - /// - /// Checks if the value is within the specified range. - /// - /// The comparable to be checked. - /// The range where must be in-between. - /// True if the parameter is within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsIn([ValidatedNotNull] this T parameter, Range range) - where T : IComparable => range.IsValueWithinRange(parameter); /// - /// Checks if the value is not within the specified range. + /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The range where must not be in-between. - /// True if the parameter is not within the specified range, else false. - /// Thrown when is null. + /// The value to be checked. + /// The items that should contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNotIn([ValidatedNotNull] this T parameter, Range range) - where T : IComparable => !range.IsValueWithinRange(parameter); + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || !parameter.IsOneOf(items)) + Throw.CustomException(exceptionFactory, parameter, items!); + return parameter; + } + /// - /// Ensures that is within the specified range, or otherwise throws an . + /// Ensures that the value is not one of the specified items, or otherwise throws a . /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must be in-between. + /// The value to be checked. + /// The items that must not contain the value. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not within . - /// Thrown when is null. + /// Thrown when is equal to one of the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustBeInRange(parameter, range, parameterName, message); + // ReSharper disable PossibleMultipleEnumeration + if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + Throw.ValueIsOneOf(parameter, items, parameterName, message); return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that is within the specified range, or otherwise throws your custom exception. + /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. /// - /// The parameter to be checked. - /// The range where must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not within , or when is null. + /// The value to be checked. + /// The items that must not contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) - where T : IComparable + [ContractAnnotation("items:null => halt")] + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || !range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || parameter.IsOneOf(items)) + Throw.CustomException(exceptionFactory, parameter, items!); return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws an . + /// Ensures that the collection has at least the specified number of items, or otherwise throws an . /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must not be in-between. + /// The collection to be checked. + /// The number of items the collection should have at least. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is within . + /// Thrown when does not contain at least the specified number of items. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeIn([ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustNotBeInRange(parameter, range, parameterName, message); + if (parameter.Count(parameterName, message) < count) + Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. /// - /// The parameter to be checked. - /// The range where must not be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is within , or when is null. + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeIn([ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (parameter is null || range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); + if (parameter is null || parameter.Count() < count) + Throw.CustomException(exceptionFactory, parameter, count); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the collection has at most the specified number of items, or otherwise throws an . /// - /// The date time to be checked. + /// The collection to be checked. + /// The number of items the collection should have at most. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when does not contain at most the specified number of items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.Kind != DateTimeKind.Utc) - Throw.MustBeUtcDateTime(parameter, parameterName, message); + if (parameter.Count(parameterName, message) > count) + Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The collection to be checked. + /// The number of items the collection should have at most. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (parameter.Kind != DateTimeKind.Utc) - Throw.CustomException(exceptionFactory, parameter); + if (parameter is null || parameter.Count() > count) + Throw.CustomException(exceptionFactory, parameter, count); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws an . /// - /// The date time to be checked. + /// The span to be checked. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Local) - Throw.MustBeLocalDateTime(parameter, parameterName, message); + if (parameter.Length != length) + Throw.InvalidSpanLength(parameter, length, parameterName, message); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) + public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter.Kind != DateTimeKind.Local) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length != length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The date time to be checked. + /// The span to be checked. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + if (parameter.Length != length) + Throw.InvalidSpanLength(parameter, length, parameterName, message); return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length != length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the collection has the specified number of items, or otherwise throws an . + /// Ensures that the span is longer than the specified length, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection must have. + /// The span to be checked. + /// The value that the span must be longer than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified number of items. - /// Thrown when is null. + /// Thrown when is shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter!.Count(parameterName, message) != count) - Throw.InvalidCollectionCount(parameter!, count, parameterName, message); - return parameter!; + if (parameter.Length <= length) + Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + return parameter; } /// - /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. + /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified number of items, or when is null. + /// The span to be checked. + /// The length value that the span must be longer than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.Count() != count) - Throw.CustomException(exceptionFactory, parameter, count); + if (parameter.Length <= length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Checks if the specified collection is null or empty. - /// - /// The collection to be checked. - /// True if the collection is null or empty, else false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; - /// - /// Ensures that the collection is not null or empty, or otherwise throws an . + /// Ensures that the span is longer than the specified length, or otherwise throws an . /// - /// The collection to be checked. + /// The span to be checked. + /// The value that the span must be longer than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no items. - /// Thrown when is null. + /// Thrown when is shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) == 0) - Throw.EmptyCollection(parameterName, message); - return parameter!; + if (parameter.Length <= length) + Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. + /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no items, or when is null. + /// The span to be checked. + /// The length value that the span must be longer than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.Count() == 0) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length <= length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws a . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// - /// The collection to be checked. - /// The item that must be part of the collection. + /// The span to be checked. + /// The value that the span must be longer than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when is null. + /// Thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - Throw.MissingItem(parameter, item, parameterName, message); - return parameter; - } - - if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.MissingItem(parameter!, item, parameterName, message); - return parameter!; + if (parameter.Length < length) + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain , or when is null. + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - - if (parameter is null || !parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); + if (parameter.Length < length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws an . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// - /// The collection to be checked. - /// The item that must not be part of the collection. + /// The span to be checked. + /// The value that the span must be longer than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable - { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - Throw.ExistingItem(parameter, item, parameterName, message); - return parameter; - } - - if (parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.ExistingItem(parameter!, item, parameterName, message); - return parameter!; - } - - /// - /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The item that must not be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . + /// Thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - - if (parameter is null || parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); + if (parameter.Length < length) + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); return parameter; } /// - /// Checks if the given is one of the specified . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The item to be checked. - /// The collection that might contain the . - /// Thrown when is null. + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static bool IsOneOf(this TItem item, [ValidatedNotNull] IEnumerable items) + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (items is ICollection collection) - return collection.Contains(item); - if (items is string @string && item is char character) - return @string.IndexOf(character) != -1; - return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); + if (parameter.Length < length) + Throw.CustomSpanException(exceptionFactory, parameter, length); + return parameter; } /// - /// Ensures that the value is one of the specified items, or otherwise throws a . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The value to be checked. - /// The items that should contain the value. + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to one of the specified . - /// Thrown when is null. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueNotOneOf(parameter, items, parameterName, message); + if (parameter.Length >= length) + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The items that should contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to one of the specified , or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (items is null || !parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); + if (parameter.Length >= length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The value to be checked. - /// The items that must not contain the value. + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to one of the specified . - /// Thrown when is null. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueIsOneOf(parameter, items, parameterName, message); + if (parameter.Length >= length) + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The items that must not contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is equal to one of the specified , or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off - if (items is null || parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); + if (parameter.Length >= length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection should have at least. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at least the specified number of items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable - { - if (parameter.Count(parameterName, message) < count) - Throw.InvalidMinimumCollectionCount(parameter!, count, parameterName, message); - return parameter!; - } - - /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection should have at least. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || parameter.Count() < count) - Throw.CustomException(exceptionFactory, parameter, count); + if (parameter.Length > length) + Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); return parameter; } /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws an . - /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at most the specified number of items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable - { - if (parameter.Count(parameterName, message) > count) - Throw.InvalidMaximumCollectionCount(parameter!, count, parameterName, message); - return parameter!; - } - - /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.Count() > count) - Throw.CustomException(exceptionFactory, parameter, count); + if (parameter.Length > length) + Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// /// The span to be checked. - /// The length that the span must have. + /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified length. + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); + if (parameter.Length > length) + Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.Length != length) + if (parameter.Length > length) Throw.CustomSpanException(exceptionFactory, parameter, length); return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Checks if the two specified types are equivalent. This is true when both types are equal or + /// when one type is a constructed generic type and the other type is the corresponding generic type definition. /// - /// The span to be checked. - /// The length that the span must have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Your custom exception thrown when does not have the specified length. + /// The first type to be checked. + /// The other type to be checked. + /// + /// True if both types are null, or if both are equal, or if one type + /// is a constructed generic type and the other one is the corresponding generic type definition, else false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || !(type is null) && !(other is null) && (type == other || type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)); + private static bool CheckTypeEquivalency(Type type, Type other) { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); - return parameter; + if (type.IsConstructedGenericType) + return type.GetGenericTypeDefinition() == other; + return other.GetGenericTypeDefinition() == type; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Checks if the type implements the specified interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + /// The type to be checked. + /// The interface type that should implement. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type interfaceType// ReSharper restore RedundantNullableFlowAttribute + ) { - if (parameter.Length != length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) + { + if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) + return true; + } + + return false; } /// - /// Ensures that the span is longer than the specified length, or otherwise throws an . + /// Checks if the type implements the specified interface type. This overload uses the specified + /// to compare the interface types. /// - /// The span to be checked. - /// The value that the span must be longer than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length <= length) - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); - return parameter; + /// The type to be checked. + /// The interface type that should implement. + /// The equality comparer used to compare the interface types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type interfaceType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer// ReSharper restore RedundantNullableFlowAttribute + ) + { + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + typeComparer.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) + { + if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) + return true; + } + + return false; } /// - /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. + /// Checks if the given is equal to the specified or if it implements it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length value that the span must be longer than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than or equal to . + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length <= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); /// - /// Ensures that the span is longer than the specified length, or otherwise throws an . + /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified + /// to compare the types. /// - /// The span to be checked. - /// The value that the span must be longer than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than or equal to . + /// , + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// The equality comparer used to compare the interface types. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); + /// + /// Checks if the specified type derives from the other type. Internally, this method uses + /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type info to be checked. + /// The base class that should derive from. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass) + // ReSharper restore RedundantNullableFlowAttribute { - if (parameter.Length <= length) - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); - return parameter; + baseClass.MustNotBeNull(nameof(baseClass)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (currentBaseType.IsEquivalentTypeTo(baseClass)) + return true; + currentBaseType = currentBaseType.BaseType; + } + + return false; } /// - /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. + /// Checks if the specified type derives from the other type. This overload uses the specified + /// to compare the types. /// - /// The span to be checked. - /// The length value that the span must be longer than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + /// The type info to be checked. + /// The base class that should derive from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) + // ReSharper restore RedundantNullableFlowAttribute { - if (parameter.Length <= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; + baseClass.MustNotBeNull(nameof(baseClass)); + typeComparer.MustNotBeNull(nameof(typeComparer)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (typeComparer.Equals(currentBaseType, baseClass)) + return true; + currentBaseType = currentBaseType.BaseType; + } + + return false; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Checks if the given is equal to the specified or if it derives from it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified + /// to compare the types. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType) => // ReSharper restore RedundantNullableFlowAttribute + baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified + /// to compare the types. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length > length) - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute + baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions + /// are regarded as equal. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length > length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// This overload uses the specified to compare the types. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length > length) - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the given is a generic type that has open generic parameters, + /// but is no generic type definition. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The type to be checked. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length > length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("type:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOpenConstructedGenericType([NotNull, ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; /// /// Checks if the specified string is null or empty. /// @@ -1886,7 +1756,7 @@ public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter is null) Throw.ArgumentNull(parameterName, message); @@ -1903,15 +1773,11 @@ public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parame /// Your custom exception thrown when is an empty string or null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (string.IsNullOrEmpty(parameter)) + if (parameter.IsNullOrEmpty()) Throw.CustomException(exceptionFactory, parameter); -#if NETSTANDARD2_0 - return parameter!; -#else return parameter; -#endif } /// @@ -1932,10 +1798,10 @@ public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parame /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { parameter.MustNotBeNullOrEmpty(parameterName, message); - foreach (var character in parameter!) + foreach (var character in parameter) { if (!character.IsWhiteSpace()) return parameter; @@ -1953,7 +1819,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Your custom exception thrown when is null, empty, or contains only white space. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] - public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (parameter.IsNullOrWhiteSpace()) Throw.CustomException(exceptionFactory, parameter); @@ -2008,10 +1874,10 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Your custom exception thrown when is not equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) { if (!string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -2024,7 +1890,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { @@ -2041,11 +1907,11 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The enum value specifying how the two strings should be compared. /// The delegate that creates your custom exception. and are passed to this delegate. /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + /// Thrown when is not a valid value from the enum. + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { if (!parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -2073,7 +1939,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// Your custom exception thrown when is equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2093,7 +1959,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { @@ -2108,14 +1974,14 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { if (parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -2130,11 +1996,11 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] - public static string MustMatch([ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) - Throw.StringDoesNotMatch(parameter!, regex, parameterName, message); - return parameter!; + Throw.StringDoesNotMatch(parameter, regex, parameterName, message); + return parameter; } /// @@ -2149,7 +2015,7 @@ public static string MustMatch([ValidatedNotNull] this string? parameter, Regex /// or when is null. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string MustMatch([ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) + public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || regex is null || !regex.IsMatch(parameter)) @@ -2189,11 +2055,14 @@ public static bool Equals(this string? @string, string? value, StringComparisonT /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - Throw.StringDoesNotContain(parameter!, value!, parameterName, message); - return parameter!; + { + Throw.StringDoesNotContain(parameter, value, parameterName, message); + } + + return parameter; } /// @@ -2209,11 +2078,14 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || !parameter.Contains(value)) + { Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; } @@ -2230,11 +2102,14 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) - Throw.StringDoesNotContain(parameter!, value, comparisonType, parameterName, message); - return parameter!; + { + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + } + + return parameter; } /// @@ -2247,16 +2122,19 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// /// Your custom exception thrown when does not contain , /// or when is null, - /// or when is null, - /// or when is not a valid value from the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || parameter.IndexOf(value, comparisonType) < 0) + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + { Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } @@ -2271,11 +2149,14 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - Throw.StringContains(parameter!, value, parameterName, message); - return parameter!; + { + Throw.StringContains(parameter, value, parameterName, message); + } + + return parameter; } /// @@ -2283,7 +2164,7 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// /// The string to be checked. /// The string that must not be part of . - /// The delegate that creates your custom exception (optional). and are passed to this + /// The delegate that creates your custom exception (optional). and are passed to this delegate. /// /// Your custom exception thrown when contains , /// or when is null, @@ -2291,7 +2172,7 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || parameter.Contains(value)) @@ -2312,11 +2193,11 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) - Throw.StringContains(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.StringContains(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -2329,15 +2210,15 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// /// Your custom exception thrown when contains , /// or when is null, - /// or when is null, - /// or when is not a valid value of the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || parameter.IndexOf(value, comparisonType) >= 0) + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); return parameter; } @@ -2353,7 +2234,8 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("string:null => halt; value:null => halt")] - public static bool Contains([ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; + // ReSharper disable once RedundantNullableFlowAttribute + public static bool Contains([NotNull, ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; /// /// Checks if the string is a substring of the other string. /// @@ -2363,7 +2245,9 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("value:null => halt; other:null => halt")] - public static bool IsSubstringOf([ValidatedNotNull] this string value, [ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper restore RedundantNullableFlowAttribute /// /// Checks if the string is a substring of the other string. /// @@ -2375,7 +2259,9 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("value:null => halt; other:null => halt")] - public static bool IsSubstringOf(this string value, string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + // ReSharper disable RedundantNullableFlowAttribute /// /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// @@ -2387,11 +2273,11 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.NotSubstring(parameter!, value, parameterName, message); - return parameter!; + Throw.NotSubstring(parameter, value, parameterName, message); + return parameter; } /// @@ -2407,7 +2293,7 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || !value.Contains(parameter)) @@ -2428,11 +2314,11 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) == -1) - Throw.NotSubstring(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -2445,15 +2331,15 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// /// Your custom exception thrown when does not contain , /// or when is null, - /// or when is null, - /// or when is not a valid value of the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || value.IndexOf(parameter, comparisonType) == -1) + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); return parameter; } @@ -2469,11 +2355,11 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.Substring(parameter!, value, parameterName, message); - return parameter!; + Throw.Substring(parameter, value, parameterName, message); + return parameter; } /// @@ -2490,7 +2376,7 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || value.Contains(parameter)) @@ -2511,11 +2397,11 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) != -1) - Throw.Substring(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.Substring(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -2524,7 +2410,7 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// The string to be checked. /// The other string that must not contain . /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// /// Your custom exception thrown when contains , /// or when is null, @@ -2533,11 +2419,294 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || value.IndexOf(parameter, comparisonType) != -1) + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not start with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when starts with . + /// Thrown when or is null. + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not end with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when ends with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + { Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } @@ -2569,11 +2738,11 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) - Throw.InvalidEmailAddress(parameter!, parameterName, message); - return parameter!; + Throw.InvalidEmailAddress(parameter, parameterName, message); + return parameter; } /// @@ -2585,7 +2754,7 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (!parameter.IsEmailAddress()) Throw.CustomException(exceptionFactory, parameter); @@ -2604,11 +2773,11 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) - Throw.InvalidEmailAddress(parameter!, parameterName, message); - return parameter!; + Throw.InvalidEmailAddress(parameter, parameterName, message); + return parameter; } /// @@ -2621,7 +2790,7 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) @@ -2640,11 +2809,11 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length >= length) - Throw.StringNotShorterThan(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotShorterThan(parameter, length, parameterName, message); + return parameter; } /// @@ -2656,7 +2825,7 @@ public static string MustBeShorterThan([ValidatedNotNull] this string? parameter /// Your custom exception thrown when is null or when it has a length greater than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length >= length) Throw.CustomException(exceptionFactory, parameter, length); @@ -2674,11 +2843,11 @@ public static string MustBeShorterThan([ValidatedNotNull] this string? parameter /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length > length) - Throw.StringNotShorterThanOrEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -2690,7 +2859,7 @@ public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? /// Your custom exception thrown when is null or when it has a length greater than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length > length) Throw.CustomException(exceptionFactory, parameter, length); @@ -2704,15 +2873,15 @@ public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? /// The asserted length of the string. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length different than . + /// Thrown when has a length other than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length != length) - Throw.StringLengthNotEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -2721,10 +2890,10 @@ public static string MustHaveLength([ValidatedNotNull] this string? parameter, i /// The string to be checked. /// The asserted length of the string. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length different than . + /// Your custom exception thrown when is null or when it has a length other than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length != length) Throw.CustomException(exceptionFactory, parameter, length); @@ -2742,11 +2911,11 @@ public static string MustHaveLength([ValidatedNotNull] this string? parameter, i /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length <= length) - Throw.StringNotLongerThan(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotLongerThan(parameter, length, parameterName, message); + return parameter; } /// @@ -2758,7 +2927,7 @@ public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, /// Your custom exception thrown when is null or when it has a length shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length <= length) Throw.CustomException(exceptionFactory, parameter, length); @@ -2776,11 +2945,11 @@ public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length < length) - Throw.StringNotLongerThanOrEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -2792,7 +2961,7 @@ public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? p /// Your custom exception thrown when is null or when it has a length shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length < length) Throw.CustomException(exceptionFactory, parameter, length); @@ -2810,11 +2979,11 @@ public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? p /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) - Throw.StringLengthNotInRange(parameter!, range, parameterName, message); - return parameter!; + Throw.StringLengthNotInRange(parameter, range, parameterName, message); + return parameter; } /// @@ -2826,7 +2995,7 @@ public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, /// Your custom exception thrown when is null or its length is not within the specified range. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) + public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) { if (parameter is null || !range.IsValueWithinRange(parameter.Length)) Throw.CustomException(exceptionFactory, parameter, range); @@ -2850,11 +3019,11 @@ public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) - Throw.NotNewLine(parameter!, parameterName, message); - return parameter!; + Throw.NotNewLine(parameter, parameterName, message); + return parameter; } /// @@ -2865,7 +3034,7 @@ public static string MustBeNewLine([ValidatedNotNull] this string? parameter, [C /// Your custom exception thrown when is not equal to "\n" or "\r\n". [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (!parameter.IsNewLine()) Throw.CustomException(exceptionFactory, parameter); @@ -2912,11 +3081,11 @@ public static string MustBeNewLine([ValidatedNotNull] this string? parameter, Fu /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) Throw.NotTrimmed(parameter, parameterName, message); - return parameter!; + return parameter; } /// @@ -2928,11 +3097,11 @@ public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, [C /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmed(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmed()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -2975,11 +3144,11 @@ public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, Fu /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) Throw.NotTrimmedAtStart(parameter, parameterName, message); - return parameter!; + return parameter; } /// @@ -2991,11 +3160,11 @@ public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parame /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmedAtStart(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -3038,11 +3207,11 @@ public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parame /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) Throw.NotTrimmedAtEnd(parameter, parameterName, message); - return parameter!; + return parameter; } /// @@ -3054,243 +3223,39 @@ public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmedAtEnd(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// - /// Checks if the two specified types are equivalent. This is true when both types are equal or - /// when one type is a constructed generic type and the other type is the corresponding generic type definition. + /// Ensures that the specified URI is an absolute one, or otherwise throws a . /// - /// The first type to be checked. - /// The other type to be checked. - /// - /// True if both types are null, or if both are equal, or if one type - /// is a constructed generic type and the other one is the corresponding generic type definition, else false. - /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not an absolute URI. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || !(type is null) && !(other is null) && (type == other || type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)); - private static bool CheckTypeEquivalency(Type type, Type other) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (type.IsConstructedGenericType) - return type.GetGenericTypeDefinition() == other; - return other.GetGenericTypeDefinition() == type; + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) + Throw.MustBeAbsoluteUri(parameter, parameterName, message); + return parameter; } /// - /// Checks if the type implements the specified interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. /// - /// The type to be checked. - /// The interface type that should implement. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt")] - public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type interfaceType) - { - interfaceType.MustNotBeNull(nameof(interfaceType)); - var implementedInterfaces = type.MustNotBeNull(nameof(type)).GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) - return true; - } - - return false; - } - - /// - /// Checks if the type implements the specified interface type. This overload uses the specified - /// to compare the interface types. - /// - /// The type to be checked. - /// The interface type that should implement. - /// The equality comparer used to compare the interface types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] - public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type interfaceType, [ValidatedNotNull] IEqualityComparer typeComparer) - { - interfaceType.MustNotBeNull(nameof(interfaceType)); - typeComparer.MustNotBeNull(nameof(typeComparer)); - var implementedInterfaces = type.MustNotBeNull(nameof(type)).GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) - return true; - } - - return false; - } - - /// - /// Checks if the given is equal to the specified or if it implements it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); - /// - /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified - /// to compare the types. - /// - /// , - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// The equality comparer used to compare the interface types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); - /// - /// Checks if the specified type derives from the other type. Internally, this method uses - /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type info to be checked. - /// The base class that should derive from. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt")] - public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClass) - { - baseClass.MustNotBeNull(nameof(baseClass)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) - { - if (currentBaseType.IsEquivalentTypeTo(baseClass)) - return true; - currentBaseType = currentBaseType.BaseType; - } - - return false; - } - - /// - /// Checks if the specified type derives from the other type. This overload uses the specified - /// to compare the types. - /// - /// The type info to be checked. - /// The base class that should derive from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] - public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClass, [ValidatedNotNull] IEqualityComparer typeComparer) - { - baseClass.MustNotBeNull(nameof(baseClass)); - typeComparer.MustNotBeNull(nameof(typeComparer)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) - { - if (typeComparer.Equals(currentBaseType, baseClass)) - return true; - currentBaseType = currentBaseType.BaseType; - } - - return false; - } - - /// - /// Checks if the given is equal to the specified or if it derives from it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); - /// - /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); - /// - /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] - public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClassOrInterfaceType) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); - /// - /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] - public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClassOrInterfaceType, [ValidatedNotNull] IEqualityComparer typeComparer) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); - /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions - /// are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrInheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); - /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// This overload uses the specified to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrInheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); - /// - /// Checks if the given is a generic type that has open generic parameters, - /// but is no generic type definition. - /// - /// The type to be checked. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt")] - public static bool IsOpenConstructedGenericType([ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; - /// - /// Ensures that the specified URI is an absolute one, or otherwise throws a . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not an absolute URI. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) - Throw.MustBeAbsoluteUri(parameter!, parameterName, message); - return parameter!; - } - - /// - /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when is not an absolute URI, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when is not an absolute URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { if (parameter is null || parameter.IsAbsoluteUri == false) Throw.CustomException(exceptionFactory, parameter); @@ -3307,11 +3272,11 @@ public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, Func /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) - Throw.MustBeRelativeUri(parameter!, parameterName, message); - return parameter!; + Throw.MustBeRelativeUri(parameter, parameterName, message); + return parameter; } /// @@ -3322,7 +3287,7 @@ public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, [Cal /// Your custom exception thrown when is an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { if (parameter is null || parameter.IsAbsoluteUri) Throw.CustomException(exceptionFactory, parameter); @@ -3341,11 +3306,11 @@ public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, Func /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) - Throw.UriMustHaveScheme(parameter!, scheme, parameterName, message); - return parameter!; + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + return parameter; } /// @@ -3361,11 +3326,11 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -3381,7 +3346,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) Throw.CustomException(exceptionFactory, parameter, scheme); @@ -3399,7 +3364,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); + public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); /// /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. /// @@ -3412,7 +3377,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); + public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); /// /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . /// @@ -3424,7 +3389,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); + public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); /// /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. /// @@ -3437,7 +3402,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); + public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); /// /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . /// @@ -3449,11 +3414,11 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter!.Scheme.Equals("http") == false) + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) Throw.UriMustHaveOneSchemeOf(parameter, new[] { "https", "http" }, parameterName, message); - return parameter!; + return parameter; } /// @@ -3468,11 +3433,11 @@ public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, [ /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter!.Scheme.Equals("http") == false) + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -3487,18 +3452,18 @@ public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, F /// Throw when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] - public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { // ReSharper disable PossibleMultipleEnumeration parameter.MustBeAbsoluteUri(parameterName, message); if (schemes is ICollection collection) { - if (!collection.Contains(parameter!.Scheme)) + if (!collection.Contains(parameter.Scheme)) Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); return parameter; } - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter!.Scheme)) + if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); return parameter; // ReSharper restore PossibleMultipleEnumeration @@ -3518,7 +3483,7 @@ public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, IE /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) + public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || !parameter.IsAbsoluteUri) @@ -3530,102 +3495,431 @@ public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? return parameter; } - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (schemes is null || !schemes.Contains(parameter.Scheme)) Throw.CustomException(exceptionFactory, parameter, schemes!); return parameter; } - } - /// - /// Provides meta-information about enum values and the flag bitmask if the enum is marked with the . - /// Can be used to validate that an enum value is valid. - /// - /// The type of the enum. - internal static class EnumInfo - where T : Enum - { - // ReSharper disable StaticMemberInGenericType - /// - /// Gets the value indicating whether the enum type is marked with the flags attribute. - /// - public static readonly bool IsFlagsEnum = typeof(T).GetCustomAttribute(Types.FlagsAttributeType) != null; - /// - /// Gets the flags pattern when is true. If the enum is not a flags enum, then 0UL is returned. - /// - public static readonly ulong FlagsPattern; - private static readonly int EnumSize = Unsafe.SizeOf(); - private static readonly T[] EnumConstantsArray; - /// - /// Gets the underlying type that is used for the enum ( for default enums). - /// - public static readonly Type UnderlyingType; + /* + * ------------------------------------- + * Must Not Be Less Than + * Must Be Greater Than or Equal To + * ------------------------------------- + */ /// - /// Gets the values of the enum as a read-only collection. + /// Ensures that the specified is not less than the given value, or otherwise throws an . /// - public static ReadOnlyMemory EnumConstants { get; } - - static EnumInfo() + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); - var fields = typeof(T).GetFields(); - UnderlyingType = fields[0].FieldType; - EnumConstants = new ReadOnlyMemory(EnumConstantsArray); - if (!IsFlagsEnum) - return; - for (var i = 0; i < EnumConstantsArray.Length; ++i) - { - var convertedValue = ConvertToUInt64(EnumConstantsArray[i]); - FlagsPattern |= convertedValue; - } + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + Throw.MustNotBeLessThan(parameter, other, parameterName, message); + return parameter; } + /// + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsValidFlagsValue(T enumValue) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - var convertedValue = ConvertToUInt64(enumValue); - return (FlagsPattern & convertedValue) == convertedValue; + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs off + if (parameter is null || parameter.CompareTo(other) < 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; } - private static bool IsValidValue(T parameter) - { - var comparer = EqualityComparer.Default; - for (var i = 0; i < EnumConstantsArray.Length; ++i) - if (comparer.Equals(EnumConstantsArray[i], parameter)) - return true; - return false; + /// + /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + return parameter; } /// - /// Checks if the specified enum value is valid. This is true if either the enum is a standard enum and the enum value corresponds - /// to one of the enum constant values or if the enum type is marked with the and the given value - /// is a valid combination of bits for this type. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// The enum value to be checked. - /// True if either the enum value is + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidEnumValue(T enumValue) => IsFlagsEnum ? IsValidFlagsValue(enumValue) : IsValidValue(enumValue); - private static ulong ConvertToUInt64(T value) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - switch (EnumSize) - { - case 1: - return Unsafe.As(ref value); - case 2: - return Unsafe.As(ref value); - case 4: - return Unsafe.As(ref value); - case 8: - return Unsafe.As(ref value); - default: - ThrowUnknownEnumSize(); - return default; - } + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; } - private static void ThrowUnknownEnumSize() + /* + * ------------------------------------- + * Must Be Less Than + * Must Not Be Greater Than or Equal To + * ------------------------------------- + */ + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + Throw.MustBeLessThan(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /* + * ------------------------------------- + * Must Be Greater Than + * Must Not Be Less Than or Equal To + * ------------------------------------- + */ + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + Throw.MustBeGreaterThan(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /* + * ------------------------------------- + * Must Not Be Greater Than + * Must Be Less Than or Equal To + * ------------------------------------- + */ + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /* + * ------------------------------------- + * Ranges + * ------------------------------------- + */ + /// + /// Checks if the value is within the specified range. + /// + /// The comparable to be checked. + /// The range where must be in-between. + /// True if the parameter is within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsIn([NotNull, ValidatedNotNull] this T parameter, Range range) + where T : IComparable => range.IsValueWithinRange(parameter); + /// + /// Checks if the value is not within the specified range. + /// + /// The comparable to be checked. + /// The range where must not be in-between. + /// True if the parameter is not within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNotIn([NotNull, ValidatedNotNull] this T parameter, Range range) + where T : IComparable => !range.IsValueWithinRange(parameter); + /// + /// Ensures that is within the specified range, or otherwise throws an . + /// + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not within . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + Throw.MustBeInRange(parameter, range, parameterName, message); + return parameter; + } + + /// + /// Ensures that is within the specified range, or otherwise throws your custom exception. + /// + /// The parameter to be checked. + /// The range where must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not within , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || !range.IsValueWithinRange(parameter)) + Throw.CustomException(exceptionFactory, parameter!, range); + return parameter; + } + + /// + /// Ensures that is not within the specified range, or otherwise throws an . + /// + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is within . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + Throw.MustNotBeInRange(parameter, range, parameterName, message); + return parameter; + } + + /// + /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// + /// The parameter to be checked. + /// The range where must not be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is within , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + where T : IComparable { - throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || range.IsValueWithinRange(parameter)) + Throw.CustomException(exceptionFactory, parameter!, range); + return parameter; } } @@ -3661,79 +3955,6 @@ public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlways type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); } - /// - /// Represents an that compares strings using the - /// ordinal sort rules, ignoring the case and the white space characters. - /// - internal sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityComparer - { - /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the case and - /// the white space of the provided strings. - /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) - { - x.MustNotBeNull(nameof(x)); - y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(y); - } - - /// - /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters - /// which are interpreted as case-insensitive. - /// - /// Thrown when is null. - public int GetHashCode(string @string) - { - @string.MustNotBeNull(nameof(@string)); - var hashBuilder = MultiplyAddHashBuilder.Create(); - foreach (var character in @string) - { - if (!character.IsWhiteSpace()) - hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); - } - - return hashBuilder.BuildHash(); - } - } - - /// - /// Represents an that compares strings using the - /// ordinal sort rules and ignoring the white space characters. - /// - internal sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer - { - /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space - /// of the provided strings. - /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) - { - x.MustNotBeNull(nameof(x)); - y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreWhiteSpace(y); - } - - /// - /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. - /// - /// Thrown when is null. - public int GetHashCode(string @string) - { - @string.MustNotBeNull(nameof(@string)); - var hashCodeBuilder = MultiplyAddHashBuilder.Create(); - foreach (var character in @string) - { - if (!character.IsWhiteSpace()) - hashCodeBuilder.CombineIntoHash(character); - } - - return hashCodeBuilder.BuildHash(); - } - } - /// /// Defines a range that can be used to check if a specified is in between it or not. /// @@ -3846,12 +4067,12 @@ public RangeFromInfo(T from, bool isFromInclusive) /// public override string ToString() => $"Range from {CreateRangeDescriptionText()}"; /// - /// Returns either "inclusive" or "exclusive", depending whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string LowerBoundaryText {[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetBoundaryText(IsFromInclusive); } /// - /// Returns either "inclusive" or "exclusive", depending whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string UpperBoundaryText {[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetBoundaryText(IsToInclusive); } @@ -3917,6 +4138,26 @@ public static Range.RangeFromInfo FromInclusive(T value) public static Range.RangeFromInfo FromExclusive(T value) where T : IComparable => new(value, false); /// + /// Creates a range with both boundaries inclusive. + /// + /// The lower boundary of the range. + /// The upper boundary of the range. + /// A new range with both boundaries inclusive. + /// Thrown when is less than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range InclusiveBetween(T from, T to) + where T : IComparable => new(from, to); + /// + /// Creates a range with both boundaries exclusive. + /// + /// The lower boundary of the range. + /// The upper boundary of the range. + /// A new range with both boundaries exclusive. + /// Thrown when is less than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range ExclusiveBetween(T from, T to) + where T : IComparable => new(from, to, false, false); + /// /// Creates a range for the specified enumerable that encompasses all valid indexes. /// /// @@ -3966,28 +4207,252 @@ public static Range.RangeFromInfo FromExclusive(T value) /// The length of the memory is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(ReadOnlyMemory memory) => new(0, memory.Length, isFromInclusive: true, isToInclusive: false); + public static Range For(ReadOnlyMemory memory) => new(0, memory.Length, isFromInclusive: true, isToInclusive: false); + /// + /// Creates a range for the specified memory that encompasses all valid indexes. + /// + /// + /// The count of the segment is used to create a valid index range. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range For(ArraySegment segment) => new(0, segment.Count, isFromInclusive: true, isToInclusive: false); + } + + /// + /// Provides regular expressions that are used in string assertions. + /// + internal static class RegularExpressions + { + /// + /// Gets the default regular expression for email validation. + /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and + /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. + /// + public static readonly Regex EmailRegex = new Regex(@"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$", RegexOptions.CultureInvariant | RegexOptions.ECMAScript); + } + + /// + /// This class caches instances to avoid use of the typeof operator. + /// + internal abstract class Types + { + /// + /// Gets the type. + /// + public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); + } + + /// + /// Specifies the culture, case , and sort rules when comparing strings. + /// + /// + /// This enum is en extension of , adding + /// capabilities to ignore white space when making string equality comparisons. + /// See the when + /// you want to compare in such a way. + /// + internal enum StringComparisonType + { + /// + /// Compare strings using culture-sensitive sort rules and the current culture. + /// + CurrentCulture = 0, + /// + /// Compare strings using culture-sensitive sort rules, the current culture, and + /// ignoring the case of the strings being compared. + /// + CurrentCultureIgnoreCase = 1, + /// + /// Compare strings using culture-sensitive sort rules and the invariant culture. + /// + InvariantCulture = 2, + /// + /// Compare strings using culture-sensitive sort rules, the invariant culture, and + /// ignoring the case of the strings being compared. + /// + InvariantCultureIgnoreCase = 3, + /// + /// Compare strings using ordinal sort rules. + /// + Ordinal = 4, + /// + /// Compare strings using ordinal sort rules and ignoring the case of the strings + /// being compared. + /// + OrdinalIgnoreCase = 5, + /// + /// Compare strings using ordinal sort rules and ignoring the white space characters + /// of the strings being compared. + /// + OrdinalIgnoreWhiteSpace = 6, + /// + /// Compare strings using ordinal sort rules, ignoring the case and ignoring the + /// white space characters of the strings being compared. + /// + OrdinalIgnoreCaseIgnoreWhiteSpace = 7 + } + + /// + /// Represents an that compares strings using the + /// ordinal sort rules, ignoring the case and the white space characters. + /// + internal sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityComparer + { + /// + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the case and + /// the white space of the provided strings. + /// + /// Thrown when or are null. + public bool Equals(string? x, string? y) + { + x.MustNotBeNull(nameof(x)); + y.MustNotBeNull(nameof(y)); + return x.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(y); + } + + /// + /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters + /// which are interpreted as case-insensitive. + /// + /// Thrown when is null. + public int GetHashCode(string @string) + { + @string.MustNotBeNull(nameof(@string)); + var hashBuilder = MultiplyAddHashBuilder.Create(); + foreach (var character in @string) + { + if (!character.IsWhiteSpace()) + hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); + } + + return hashBuilder.BuildHash(); + } + } + + /// + /// Provides meta-information about enum values and the flag bitmask if the enum is marked with the . + /// Can be used to validate that an enum value is valid. + /// + /// The type of the enum. + internal static class EnumInfo + where T : struct, Enum + { + // ReSharper disable StaticMemberInGenericType + /// + /// Gets the value indicating whether the enum type is marked with the flags attribute. + /// + public static readonly bool IsFlagsEnum = typeof(T).GetCustomAttribute(Types.FlagsAttributeType) != null; + /// + /// Gets the flags pattern when is true. If the enum is not a flags enum, then 0UL is returned. + /// + public static readonly ulong FlagsPattern; + private static readonly int EnumSize = Unsafe.SizeOf(); + private static readonly T[] EnumConstantsArray; + /// + /// Gets the values of the enum as a read-only collection. + /// + public static ReadOnlyMemory EnumConstants { get; } + + static EnumInfo() + { +#if NET8_0 + EnumConstantsArray = Enum.GetValues(); +#else + EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); +#endif + EnumConstants = new(EnumConstantsArray); + if (!IsFlagsEnum) + return; + for (var i = 0; i < EnumConstantsArray.Length; ++i) + { + var convertedValue = ConvertToUInt64(EnumConstantsArray[i]); + FlagsPattern |= convertedValue; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValidFlagsValue(T enumValue) + { + var convertedValue = ConvertToUInt64(enumValue); + return (FlagsPattern & convertedValue) == convertedValue; + } + + private static bool IsValidValue(T parameter) + { + var comparer = EqualityComparer.Default; + for (var i = 0; i < EnumConstantsArray.Length; ++i) + if (comparer.Equals(EnumConstantsArray[i], parameter)) + return true; + return false; + } + /// - /// Creates a range for the specified memory that encompasses all valid indexes. + /// Checks if the specified enum value is valid. This is true if either the enum is a standard enum and the enum value corresponds + /// to one of the enum constant values or if the enum type is marked with the and the given value + /// is a valid combination of bits for this type. /// - /// - /// The count of the segment is used to create a valid index range. - /// + /// The enum value to be checked. + /// True if either the enum value is [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(ArraySegment segment) => new(0, segment.Count, isFromInclusive: true, isToInclusive: false); + public static bool IsValidEnumValue(T enumValue) => IsFlagsEnum ? IsValidFlagsValue(enumValue) : IsValidValue(enumValue); + private static ulong ConvertToUInt64(T value) + { + switch (EnumSize) + { + case 1: + return Unsafe.As(ref value); + case 2: + return Unsafe.As(ref value); + case 4: + return Unsafe.As(ref value); + case 8: + return Unsafe.As(ref value); + default: + ThrowUnknownEnumSize(); + return default; + } + } + + private static void ThrowUnknownEnumSize() + { + throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); + } } /// - /// Provides regular expressions that are used in string assertions. + /// Represents an that compares strings using the + /// ordinal sort rules and ignoring the white space characters. /// - internal static class RegularExpressions + internal sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer { /// - /// Gets the default regular expression for email validation. - /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and - /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space + /// of the provided strings. /// - public static readonly Regex EmailRegex = new Regex(@"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$", RegexOptions.CultureInvariant | RegexOptions.ECMAScript); + /// Thrown when or are null. + public bool Equals(string? x, string? y) + { + x.MustNotBeNull(nameof(x)); + y.MustNotBeNull(nameof(y)); + return x.EqualsOrdinalIgnoreWhiteSpace(y); + } + + /// + /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. + /// + /// Thrown when is null. + public int GetHashCode(string @string) + { + @string.MustNotBeNull(nameof(@string)); + var hashCodeBuilder = MultiplyAddHashBuilder.Create(); + foreach (var character in @string) + { + if (!character.IsWhiteSpace()) + hashCodeBuilder.CombineIntoHash(character); + } + + return hashCodeBuilder.BuildHash(); + } } /// @@ -3998,241 +4463,265 @@ internal static class RegularExpressions /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. /// internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class ValidatedNotNullAttribute : Attribute + { + } +} + +namespace Light.GuardClauses.Exceptions +{ /// - /// Specifies the culture, case , and sort rules when comparing strings. + /// This exception indicates that a value is not defined in the corresponding enum type. /// - /// - /// This enum is en extension of , adding - /// capabilities to ignore white space when making string equality comparisons. - /// See the when - /// you want to compare in such a way. - /// - internal enum StringComparisonType + [Serializable] + internal class EnumValueNotDefinedException : ArgumentException { /// - /// Compare strings using culture-sensitive sort rules and the current culture. - /// - CurrentCulture = 0, - /// - /// Compare strings using culture-sensitive sort rules, the current culture, and - /// ignoring the case of the strings being compared. - /// - CurrentCultureIgnoreCase = 1, - /// - /// Compare strings using culture-sensitive sort rules and the invariant culture. - /// - InvariantCulture = 2, - /// - /// Compare strings using culture-sensitive sort rules, the invariant culture, and - /// ignoring the case of the strings being compared. - /// - InvariantCultureIgnoreCase = 3, - /// - /// Compare strings using ordinal sort rules. - /// - Ordinal = 4, - /// - /// Compare strings using ordinal sort rules and ignoring the case of the strings - /// being compared. - /// - OrdinalIgnoreCase = 5, - /// - /// Compare strings using ordinal sort rules and ignoring the white space characters - /// of the strings being compared. - /// - OrdinalIgnoreWhiteSpace = 6, - /// - /// Compare strings using ordinal sort rules, ignoring the case and ignoring the - /// white space characters of the strings being compared. + /// Creates a new instance of . /// - OrdinalIgnoreCaseIgnoreWhiteSpace = 7 + /// The name of the parameter. + /// The message of the exception. + public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) + { + } + +#if !NET8_0 + /// + protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif } /// - /// This class caches instances to avoid use of the typeof operator. + /// This exception indicates that configuration data is invalid. /// - internal abstract class Types + [Serializable] + internal class InvalidConfigurationException : Exception { /// - /// Gets the type. + /// Initializes a new instance of . /// - public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); + /// The message of the exception (optional). + /// The exception that is the cause of this one (optional). + public InvalidConfigurationException(string? message = null, Exception? innerException = null) : base(message, innerException) + { + } + +#if !NET8_0 + /// + protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif } - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class ValidatedNotNullAttribute : Attribute + /// + /// This exception indicates that an URI is relative instead of absolute. + /// + [Serializable] + internal class RelativeUriException : UriException { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + { + } + +#if !NET8_0 + /// + protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif } -} -namespace Light.GuardClauses.Exceptions -{ /// - /// This exception indicates that an URI is absolute instead of relative. + /// This exception indicates that a collection contains an item that must not be part of it. /// [Serializable] - internal class AbsoluteUriException : UriException + internal class ExistingItemException : CollectionException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a value of a value type is the default value. + /// This exception indicates that an item is not present in a collection. /// [Serializable] - internal class ArgumentDefaultException : ArgumentException + internal class MissingItemException : CollectionException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) + public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) + protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that the state of a collection is invalid. + /// This exception indicates that an Email address is invalid. /// [Serializable] - internal class CollectionException : ArgumentException + internal class InvalidEmailAddressException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) + public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a collection has no items. + /// This exception indicates that two values are equal. /// [Serializable] - internal class EmptyCollectionException : InvalidCollectionCountException + internal class ValuesEqualException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) + public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that that a GUID is empty. + /// This exception indicates that a string has an invalid length. /// [Serializable] - internal class EmptyGuidException : ArgumentException + internal class StringLengthException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) + public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) + protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string is empty. + /// This exception indicates that a string is in an invalid state. /// [Serializable] - internal class EmptyStringException : StringException + internal class SubstringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a value is not defined in the corresponding enum type. + /// This exception indicates that a collection has no items. /// [Serializable] - internal class EnumValueNotDefinedException : ArgumentException + internal class EmptyCollectionException : InvalidCollectionCountException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - /// The name of the parameter. - /// The message of the exception. - public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The name of the parameter (optional). + /// The message of the exception (optional). + public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a collection contains an item that must not be part of it. + /// This exception indicates that a value of a value type is the default value. /// [Serializable] - internal class ExistingItemException : CollectionException + internal class ArgumentDefaultException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// @@ -4250,94 +4739,104 @@ public InvalidCollectionCountException(string? parameterName = null, string? mes { } +#if !NET8_0 /// protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that configuration data is invalid. + /// This exception indicates that the state of a collection is invalid. /// [Serializable] - internal class InvalidConfigurationException : Exception + internal class CollectionException : ArgumentException { /// - /// Initializes a new instance of . + /// Creates a new instance of . /// + /// The name of the parameter (optional). /// The message of the exception (optional). - /// The exception that is the cause of this one (optional). - public InvalidConfigurationException(string? message = null, Exception? innerException = null) : base(message, innerException) + public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) + protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a value is invalid. + /// This exception indicates that a string is in an invalid state. /// [Serializable] - internal class InvalidDateTimeException : ArgumentException + internal class StringException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) + public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) + protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that an Email address is invalid. + /// This exception indicates that a has no value. /// [Serializable] - internal class InvalidEmailAddressException : StringException + internal class NullableHasNoValueException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) + public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) + protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that the data is in invalid state. + /// This exception indicates that two references point to the same object. /// [Serializable] - internal class InvalidStateException : Exception + internal class SameObjectReferenceException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// + /// The name of the parameter (optional). /// The message of the exception (optional). - /// The exception that is the cause of this one (optional). - public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) + public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) + protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// @@ -4355,178 +4854,196 @@ public InvalidUriSchemeException(string? parameterName = null, string? message = { } +#if !NET8_0 /// protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that an item is not present in a collection. + /// This exception indicates that an URI is absolute instead of relative. /// [Serializable] - internal class MissingItemException : CollectionException + internal class AbsoluteUriException : UriException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a has no value. + /// This exception indicates that that a GUID is empty. /// [Serializable] - internal class NullableHasNoValueException : ArgumentException + internal class EmptyGuidException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) + public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that an URI is relative instead of absolute. + /// This exception indicates that an URI is invalid. /// [Serializable] - internal class RelativeUriException : UriException + internal class UriException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) + protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that two references point to the same object. + /// This exception indicates that two values are not equal. /// [Serializable] - internal class SameObjectReferenceException : ArgumentException + internal class ValuesNotEqualException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string is not matching a regular expression. + /// This exception indicates that a string contains only white space. /// [Serializable] - internal class StringDoesNotMatchException : StringException + internal class WhiteSpaceStringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public StringDoesNotMatchException(string? parameterName = null, string? message = null) : base(parameterName, message) + public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) + protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string is in an invalid state. + /// This exception indicates that an item is part of a collection. /// [Serializable] - internal class StringException : ArgumentException + internal class ValueIsOneOfException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string has an invalid length. + /// This exception indicates that a string is not matching a regular expression. /// [Serializable] - internal class StringLengthException : StringException + internal class StringDoesNotMatchException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) + public StringDoesNotMatchException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) + protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string is in an invalid state. + /// This exception indicates that a value is invalid. /// [Serializable] - internal class SubstringException : StringException + internal class InvalidDateTimeException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) + public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// @@ -4739,37 +5256,61 @@ public static void SameObjectReference(T? parameter, [CallerArgumentExpressio [DoesNotReturn] public static void Substring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name an message. + /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void StringNotShorterThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void StringNotShorterThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name an message. + /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void StringLengthNotEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void StringNotLongerThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void StringNotLongerThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name an message. + /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -4990,395 +5531,141 @@ public static void SameObjectReference(T? parameter, [CallerArgumentExpressio [ContractAnnotation("=> halt")] [DoesNotReturn] public static void CustomSpanException(SpanExceptionFactory exceptionFactory, in Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); - } - - /// - /// This exception indicates that a value cannot be cast to another type. - /// - [Serializable] - internal class TypeCastException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - - /// - protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } - - /// - /// This exception indicates that an URI is invalid. - /// - [Serializable] - internal class UriException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - - /// - protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } - - /// - /// This exception indicates that an item is not part of a collection. - /// - [Serializable] - internal class ValueIsNotOneOfException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - - /// - protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Throws the exception that is returned by . and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); } /// - /// This exception indicates that an item is part of a collection. + /// This exception indicates that a string is empty. /// [Serializable] - internal class ValueIsOneOfException : ArgumentException + internal class EmptyStringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } +#if !NET8_0 /// - protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that two values are equal. + /// This exception indicates that an item is not part of a collection. /// [Serializable] - internal class ValuesEqualException : ArgumentException + internal class ValueIsNotOneOfException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that two values are not equal. + /// This exception indicates that a value cannot be cast to another type. /// [Serializable] - internal class ValuesNotEqualException : ArgumentException + internal class TypeCastException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) { } +#if !NET8_0 /// - protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } /// - /// This exception indicates that a string contains only white space. + /// This exception indicates that the data is in invalid state. /// [Serializable] - internal class WhiteSpaceStringException : StringException + internal class InvalidStateException : Exception { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - /// The name of the parameter (optional). /// The message of the exception (optional). - public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The exception that is the cause of this one (optional). + public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) { } +#if !NET8_0 /// - protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } namespace Light.GuardClauses.FrameworkExtensions { /// - /// Provides extension methods for the interface. + /// Represents a builder for the algorithm that does not allocate. + /// Should only be used in cases where the overload for sixteen values is not enough or a dedicated + /// initial hash must be provided (e.g. for test reasons). + /// Instantiate the builder with the method. You have to instantiate a new builder + /// for each hash code that you want to calculate. /// - internal static class EnumerableExtensions + internal struct MultiplyAddHashBuilder { - /// - /// Tries to cast the specified enumerable to an , or - /// creates a new containing the enumerable items. - /// - /// The item type of the enumerable. - /// The enumerable to be transformed. - /// The list containing the items of the enumerable. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static IList AsList([ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); - /// - /// Tries to cast the specified enumerable to an , or - /// creates a new collection containing the enumerable items by calling the specified delegate. - /// - /// The item type of the collection. - /// The enumerable that will be converted to . - /// The delegate that creates the collection containing the specified items. - /// The cast enumerable, or a new collection containing the enumerable items. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - public static IList AsList([ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); - /// - /// Tries to downcast the specified enumerable to an array, or creates a new array with the specified items. - /// - /// The item type of the collection. - /// The enumerable that will be converted to an array. - /// The cast array, or a new array containing the enumerable items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static T[] AsArray([ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); - /// - /// Performs the action on each item of the specified enumerable. If the enumerable contains items that are null, this - /// method can either throw an exception or ignore the value (your delegate will not be called in this case). - /// - /// The item type of the enumerable. - /// The collection containing the items that will be passed to the action. - /// The action that executes for each item of the collection. - /// The value indicating whether this method should throw a when any of the items is null (optional). Defaults to true. - /// Thrown when or is null. - /// Thrown when contains a value that is null and is set to true. - public static IEnumerable ForEach([ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) - { - // ReSharper disable PossibleMultipleEnumeration - action.MustNotBeNull(nameof(action)); - var i = 0; - if (enumerable is IList list) - { - for (; i < list.Count; i++) - { - var item = list[i]; - if (item is null) - { - if (throwWhenItemIsNull) - throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); - continue; - } - - action(item); - } - } - else - { - foreach (var item in enumerable.MustNotBeNull(nameof(enumerable))) - { - if (item is null) - { - if (throwWhenItemIsNull) - throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); - ++i; - continue; - } - - action(item); - ++i; - } - } - - return enumerable; - // ReSharper restore PossibleMultipleEnumeration - } - - /// - /// Tries to cast the specified enumerable as an , or - /// creates a new containing the enumerable items. - /// - /// The item type of the enumerable. - /// The enumerable to be transformed. - /// The list containing the items of the enumerable. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); - /// - /// Tries to cast the specified enumerable as an , or - /// creates a new collection containing the enumerable items by calling the specified delegate. - /// - /// The item type of the collection. - /// The enumerable that will be converted to . - /// The delegate that creates the collection containing the specified items. - /// The cast enumerable, or a new collection containing the enumerable items. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumerable source, Func, IReadOnlyList> createCollection) => source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); - /// - /// Gets the count of the specified enumerable. - /// - /// The enumerable whose count should be determined. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("enumerable:null => halt")] - public static int Count([ValidatedNotNull] this IEnumerable enumerable) - { - if (enumerable is ICollection collection) - return collection.Count; - if (enumerable is string @string) - return @string.Length; - return DetermineCountViaEnumerating(enumerable); - } - - /// - /// Gets the count of the specified enumerable. - /// - /// The enumerable whose count should be determined. - /// The name of the parameter that is passed to the (optional). - /// The message that is passed to the (optional). - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("enumerable:null => halt")] - public static int Count([ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) - { - if (enumerable is ICollection collection) - return collection.Count; - if (enumerable is string @string) - return @string.Length; - return DetermineCountViaEnumerating(enumerable, parameterName, message); - } - - /// - /// Gets the count of the specified enumerable. - /// - /// The enumerable whose count should be determined. - /// Thrown when is null. + private int _hash; [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("enumerable:null => halt")] - public static int GetCount(this IEnumerable enumerable) - { - if (enumerable is ICollection collection) - return collection.Count; - if (enumerable is string @string) - return @string.Length; - if (TryGetCollectionOfTCount(enumerable, out var count)) - return count; - return DetermineCountViaEnumerating(enumerable); - } - + private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; /// - /// Gets the count of the specified enumerable. + /// Combines the given value into the hash using the method. /// - /// The enumerable whose count should be determined. - /// The name of the parameter that is passed to the (optional). - /// The message that is passed to the (optional). - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("enumerable:null => halt")] - public static int GetCount(this IEnumerable enumerable, string? parameterName, string? message = null) - { - if (enumerable is ICollection collection) - return collection.Count; - if (enumerable is string @string) - return @string.Length; - if (TryGetCollectionOfTCount(enumerable, out var count)) - return count; - return DetermineCountViaEnumerating(enumerable, parameterName, message); - } - - private static bool TryGetCollectionOfTCount([NoEnumeration] this IEnumerable enumerable, out int count) - { - if (enumerable is ICollection collectionOfT) - { - count = collectionOfT.Count; - return true; - } - - if (enumerable is IReadOnlyCollection readOnlyCollection) - { - count = readOnlyCollection.Count; - return true; - } - - count = 0; - return false; - } - - private static int DetermineCountViaEnumerating(IEnumerable? enumerable) - { - var count = 0; - var enumerator = enumerable.MustNotBeNull(nameof(enumerable)).GetEnumerator(); - while (enumerator.MoveNext()) - count++; - return count; - } - - private static int DetermineCountViaEnumerating(IEnumerable? enumerable, string? parameterName, string? message) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiplyAddHashBuilder CombineIntoHash(T value) { - var count = 0; - var enumerator = enumerable.MustNotBeNull(parameterName, message).GetEnumerator(); - while (enumerator.MoveNext()) - count++; - return count; + MultiplyAddHash.CombineIntoHash(ref _hash, value); + return this; } - internal static bool ContainsViaForeach(this IEnumerable items, TItem item) - { - var equalityComparer = EqualityComparer.Default; - foreach (var i in items) - { - if (equalityComparer.Equals(i, item)) - return true; - } - - return false; - } + /// + /// Returns the calculated hash code. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int BuildHash() => _hash; + /// + /// Initializes a new instance of with the specified initial hash. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new MultiplyAddHashBuilder(initialHash); } /// @@ -5387,7 +5674,7 @@ internal static bool ContainsViaForeach(this IEnumerable items, TI internal static class ExpressionExtensions { /// - /// Extracts the from a expression of the shape "object => object.Property". + /// Extracts the from an expression of the shape "object => object.Property". /// /// The object type. /// The type of the property. @@ -5396,7 +5683,8 @@ internal static class ExpressionExtensions /// /// Throw when the is not of the shape "object => object.Property". /// - public static PropertyInfo ExtractProperty([ValidatedNotNull] this Expression> expression) + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + public static PropertyInfo ExtractProperty([NotNull, ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; @@ -5406,7 +5694,7 @@ public static PropertyInfo ExtractProperty([ValidatedNotNull] this } /// - /// Extracts the from a expression of the shape "object => object.Field". + /// Extracts the from an expression of the shape "object => object.Field". /// /// The object type. /// The type of the field. @@ -5415,7 +5703,8 @@ public static PropertyInfo ExtractProperty([ValidatedNotNull] this /// /// Throw when the is not of the shape "object => object.Field". /// - public static FieldInfo ExtractField([ValidatedNotNull] this Expression> expression) + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + public static FieldInfo ExtractField([NotNull, ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; @@ -5740,37 +6029,255 @@ public static int CreateHashCode - /// Represents a builder for the algorithm that does not allocate. - /// Should only be used in cases where the overload for sixteen values is not enough or a dedicated - /// initial hash must be provided (e.g. for test reasons). - /// Instantiate the builder with the method. You have to instantiate a new builder - /// for each hash code that you want to calculate. + /// Provides extension methods for the interface. /// - internal struct MultiplyAddHashBuilder + internal static class EnumerableExtensions { - private int _hash; + /// + /// Tries to cast the specified enumerable to an , or + /// creates a new containing the enumerable items. + /// + /// The item type of the enumerable. + /// The enumerable to be transformed. + /// The list containing the items of the enumerable. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; + [ContractAnnotation("source:null => halt; source:notnull => notnull")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); /// - /// Combines the given value into the hash using the method. + /// Tries to cast the specified enumerable to an , or + /// creates a new collection containing the enumerable items by calling the specified delegate. /// + /// The item type of the collection. + /// The enumerable that will be converted to . + /// The delegate that creates the collection containing the specified items. + /// The cast enumerable, or a new collection containing the enumerable items. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiplyAddHashBuilder CombineIntoHash(T value) + [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + /// + /// Tries to downcast the specified enumerable to an array, or creates a new array with the specified items. + /// + /// The item type of the collection. + /// The enumerable that will be converted to an array. + /// The cast array, or a new array containing the enumerable items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("source:null => halt; source:notnull => notnull")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static T[] AsArray([NotNull, ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); + /// + /// Performs the action on each item of the specified enumerable. If the enumerable contains items that are null, this + /// method can either throw an exception or ignore the value (your delegate will not be called in this case). + /// + /// The item type of the enumerable. + /// The collection containing the items that will be passed to the action. + /// The action that executes for each item of the collection. + /// The value indicating whether this method should throw a when any of the items is null (optional). Defaults to true. + /// Thrown when or is null. + /// Thrown when contains a value that is null and is set to true. + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) { - MultiplyAddHash.CombineIntoHash(ref _hash, value); - return this; + // ReSharper disable PossibleMultipleEnumeration + action.MustNotBeNull(nameof(action)); + var i = 0; + if (enumerable is IList list) + { + for (; i < list.Count; i++) + { + var item = list[i]; + if (item is null) + { + if (throwWhenItemIsNull) + throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + continue; + } + + action(item); + } + } + else + { + foreach (var item in enumerable.MustNotBeNull(nameof(enumerable))) + { + if (item is null) + { + if (throwWhenItemIsNull) + throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + ++i; + continue; + } + + action(item); + ++i; + } + } + + return enumerable; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Returns the calculated hash code. + /// Tries to cast the specified enumerable as an , or + /// creates a new containing the enumerable items. /// + /// The item type of the enumerable. + /// The enumerable to be transformed. + /// The list containing the items of the enumerable. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int BuildHash() => _hash; + [ContractAnnotation("source:null => halt; source:notnull => notnull")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); /// - /// Initializes a new instance of with the specified initial hash. + /// Tries to cast the specified enumerable as an , or + /// creates a new collection containing the enumerable items by calling the specified delegate. /// + /// The item type of the collection. + /// The enumerable that will be converted to . + /// The delegate that creates the collection containing the specified items. + /// The cast enumerable, or a new collection containing the enumerable items. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new MultiplyAddHashBuilder(initialHash); + [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source, [NotNull, ValidatedNotNull] Func, IReadOnlyList> createCollection) => source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + // ReSharper restore RedundantNullableFlowAttribute + /// + /// Gets the count of the specified enumerable. + /// + /// The enumerable whose count should be determined. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("enumerable:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) + { + if (enumerable is ICollection collection) + return collection.Count; + if (enumerable is string @string) + return @string.Length; + return DetermineCountViaEnumerating(enumerable); + } + + /// + /// Gets the count of the specified enumerable. + /// + /// The enumerable whose count should be determined. + /// The name of the parameter that is passed to the (optional). + /// The message that is passed to the (optional). + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("enumerable:null => halt")] + public static int Count([NotNull, ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) + { + if (enumerable is ICollection collection) + return collection.Count; + if (enumerable is string @string) + return @string.Length; + return DetermineCountViaEnumerating(enumerable, parameterName, message); + } + + /// + /// Gets the count of the specified enumerable. + /// + /// The enumerable whose count should be determined. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("enumerable:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable) + { + if (enumerable is ICollection collection) + return collection.Count; + if (enumerable is string @string) + return @string.Length; + if (TryGetCollectionOfTCount(enumerable, out var count)) + return count; + return DetermineCountViaEnumerating(enumerable); + } + + /// + /// Gets the count of the specified enumerable. + /// + /// The enumerable whose count should be determined. + /// The name of the parameter that is passed to the (optional). + /// The message that is passed to the (optional). + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("enumerable:null => halt")] + public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable, string? parameterName, string? message = null) + { + if (enumerable is ICollection collection) + return collection.Count; + if (enumerable is string @string) + return @string.Length; + if (TryGetCollectionOfTCount(enumerable, out var count)) + return count; + return DetermineCountViaEnumerating(enumerable, parameterName, message); + } + + private static bool TryGetCollectionOfTCount([NoEnumeration] this IEnumerable enumerable, out int count) + { + if (enumerable is ICollection collectionOfT) + { + count = collectionOfT.Count; + return true; + } + + if (enumerable is IReadOnlyCollection readOnlyCollection) + { + count = readOnlyCollection.Count; + return true; + } + + count = 0; + return false; + } + + private static int DetermineCountViaEnumerating(IEnumerable? enumerable) + { + var count = 0; + var enumerator = enumerable.MustNotBeNull(nameof(enumerable)).GetEnumerator(); + while (enumerator.MoveNext()) + count++; + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + + return count; + } + + private static int DetermineCountViaEnumerating([NotNull] IEnumerable? enumerable, string? parameterName, string? message) + { + var count = 0; + var enumerator = enumerable.MustNotBeNull(parameterName, message).GetEnumerator(); + while (enumerator.MoveNext()) + count++; + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + + return count; + } + + internal static bool ContainsViaForeach(this IEnumerable items, TItem item) + { + var equalityComparer = EqualityComparer.Default; + foreach (var i in items) + { + if (equalityComparer.Equals(i, item)) + return true; + } + + return false; + } } /// @@ -5785,7 +6292,7 @@ internal static class TextExtensions /// /// Gets the list of types that will not be surrounded by quotation marks in error messages. /// - public static readonly ReadOnlyCollection UnquotedTypes = new ReadOnlyCollection(new[] { typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float) }); + public static readonly ReadOnlyCollection UnquotedTypes = new([typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float)]); private static bool IsUnquotedType() { if (typeof(T) == typeof(int)) @@ -5830,10 +6337,10 @@ private static bool IsUnquotedType() /// /// The value whose string representation is requested. [ContractAnnotation("value:null => halt; value:notnull => notnull")] - public static string? ToStringRepresentation([ValidatedNotNull] this T value) + public static string? ToStringRepresentation([NotNull, ValidatedNotNull] this T value) { value.MustNotBeNullReference(nameof(value)); - var content = value!.ToString(); + var content = value.ToString(); if (IsUnquotedType() || content.IsNullOrEmpty()) return content; // ReSharper disable UseIndexFromEndExpression -- not possible in netstandard2.0 @@ -5864,7 +6371,9 @@ private static bool IsUnquotedType() /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendCollectionContent([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendCollectionContent([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the string representations of the specified items to the string builder. /// @@ -5874,7 +6383,9 @@ private static bool IsUnquotedType() /// The text that is appended to the string builder when is empty. Defaults to "empty collection". /// Thrown when or is null. [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendItems([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); var list = items.MustNotBeNull(nameof(items)).AsList(); @@ -5903,17 +6414,20 @@ public static StringBuilder AppendItems([ValidatedNotNull] this StringBuilder /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendItemsWithNewLine([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItemsWithNewLine([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the value to the specified string builder if the condition is true. /// /// The string builder where will be appended to. - /// The boolean value indicating whether the append will be performed or not. + /// The boolean value indicating whether the append operation will be performed or not. /// The value to be appended to the string builder. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendIf([ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) + // ReSharper disable once RedundantNullableFlowAttribute + public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) { if (condition) stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); @@ -5924,12 +6438,13 @@ public static StringBuilder AppendIf([ValidatedNotNull] this StringBuilder strin /// Appends the value followed by a new line separator to the specified string builder if the condition is true. /// /// The string builder where will be appended to. - /// The boolean value indicating whether the append will be performed or not. + /// The boolean value indicating whether the append operation will be performed or not. /// The value to be appended to the string builder (optional). This value defaults to an empty string. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendLineIf([ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") + // ReSharper disable once RedundantNullableFlowAttribute + public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") { if (condition) stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); @@ -5941,7 +6456,9 @@ public static StringBuilder AppendLineIf([ValidatedNotNull] this StringBuilder s /// specified . /// /// Thrown when any parameter is null. - public static StringBuilder AppendExceptionMessages([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] Exception exception) + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] Exception exception) + // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); exception.MustNotBeNull(nameof(exception)); @@ -5961,7 +6478,8 @@ public static StringBuilder AppendExceptionMessages([ValidatedNotNull] this Stri /// a single string. /// /// Thrown when is null. - public static string GetAllExceptionMessages([ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); + // ReSharper disable once RedundantNullableFlowAttribute + public static string GetAllExceptionMessages([NotNull, ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space /// of the provided strings. @@ -5980,7 +6498,7 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) bool wasYSuccessful; // This condition of the while loop actually has to use the single '&' operator because // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise the 'wasXSuccessful == wasYSuccessful' comparison would not return + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return // the desired result. while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { @@ -6009,7 +6527,7 @@ public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, strin bool wasYSuccessful; // This condition of the while loop actually has to use the single '&' operator because // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise the 'wasXSuccessful == wasYSuccessful' comparison would not return + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return // the desired result. while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { From eb8f828a3b00e1b4c0c06ab05b71415b3db3960b Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 28 Nov 2024 06:39:33 +0100 Subject: [PATCH 002/123] refactor: move MustNotBeNull into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 31 ---------- .../Light.GuardClauses/Check.MustNotBeNull.cs | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeNull.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 0104f58..a4cf36c 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,37 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that the specified object reference is not null, or otherwise throws an . - /// - /// The object reference to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class - { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. - /// - /// The reference to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : class - { - if (parameter is null) - Throw.CustomException(exceptionFactory); - return parameter; - } - /// /// Ensures that the specified parameter is not the default value, or otherwise throws an /// for reference types, or an for value types. diff --git a/Code/Light.GuardClauses/Check.MustNotBeNull.cs b/Code/Light.GuardClauses/Check.MustNotBeNull.cs new file mode 100644 index 0000000..544a57b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeNull.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified object reference is not null, or otherwise throws an . + /// + /// The object reference to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeNull( + [NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + where T : class + { + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. + /// + /// The reference to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeNull( + [NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, + Func exceptionFactory + ) + where T : class + { + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } +} From f4d034ee26554d73751faad4e3055563142b4e94 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 28 Nov 2024 06:50:10 +0100 Subject: [PATCH 003/123] refactor: move MustNotBeDefault into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 56 ------------- .../Check.MustNotBeDefault.cs | 81 +++++++++++++++++++ 2 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeDefault.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index a4cf36c..9c61881 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,62 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that the specified parameter is not the default value, or otherwise throws an - /// for reference types, or an for value types. - /// - /// The value to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and null. - /// Thrown when is a value type and the default value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (default(T) is null) - { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - return parameter; - } - - if (EqualityComparer.Default.Equals(parameter, default!)) - Throw.ArgumentDefault(parameterName, message); - - // If we end up here, we have a value type which cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; -#pragma warning restore CS8777 - } - - /// - /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. - /// - /// The value to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is the default value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) - { - if (default(T) is null) - { - if (parameter is null) - Throw.CustomException(exceptionFactory); - return parameter; - } - - if (EqualityComparer.Default.Equals(parameter, default!)) - Throw.CustomException(exceptionFactory); - - // If we end up here, we have a value type which cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; -#pragma warning restore CS8777 - - } - /// /// Ensures that the specified parameter is not null when is a reference type, or otherwise /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, diff --git a/Code/Light.GuardClauses/Check.MustNotBeDefault.cs b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs new file mode 100644 index 0000000..167a5c9 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified parameter is not the default value, or otherwise throws an + /// for reference types, or an for value types. + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is a reference type and null. + /// Thrown when is a value type and the default value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeDefault( + [NotNull, ValidatedNotNull] this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (default(T) is null) + { + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default!)) + { + Throw.ArgumentDefault(parameterName, message); + } + +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 + } + + /// + /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is the default value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeDefault( + [NotNull, ValidatedNotNull] this T parameter, + Func exceptionFactory + ) + { + if (default(T) is null) + { + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default!)) + { + Throw.CustomException(exceptionFactory); + } + +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 + } +} \ No newline at end of file From a36e6743c161cafde5a29f82fca4c3327170d548 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 28 Nov 2024 07:02:13 +0100 Subject: [PATCH 004/123] refactor: move MustNotBeNullReference into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 51 ------------ .../Check.MustNotBeNullReference.cs | 77 +++++++++++++++++++ 2 files changed, 77 insertions(+), 51 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeNullReference.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 9c61881..f0933c8 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,57 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. - /// - /// The value to be checked for null. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (default(T) != null) - { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; -#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. - } - - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. - /// - /// The value to be checked for null. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is a reference type and is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) - { - if (default(T) != null) - { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; -#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. - } - - if (parameter is null) - Throw.CustomException(exceptionFactory); - return parameter; - } - /// /// Ensures that can be cast to and returns the cast value, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs new file mode 100644 index 0000000..6b96c5e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. + /// + /// The value to be checked for null. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is a reference type and is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeNullReference( + [NotNull, ValidatedNotNull, NoEnumeration] + this T parameter, + [CallerArgumentExpression("parameter")] + string? parameterName = null, + string? message = null + ) + { + if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. + /// + /// The value to be checked for null. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is a reference type and is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeNullReference( + [NotNull, ValidatedNotNull, NoEnumeration] + this T parameter, + Func exceptionFactory + ) + { + if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } +} \ No newline at end of file From 27f770d8fb8e4ac45bbeee6ccb4705ae4384970d Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Tue, 10 Dec 2024 21:45:43 +0100 Subject: [PATCH 005/123] refactor: move MustBeOfType method to its own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 36 -------------- Code/Light.GuardClauses/Check.MustBeOfType.cs | 47 +++++++++++++++++++ 2 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeOfType.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index f0933c8..6844fd6 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,42 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws a . - /// - /// The value to be cast. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when cannot be cast to . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message) is T castValue) - return castValue; - - Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); - return default; - } - - /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. - /// - /// The value to be cast. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when cannot be cast to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) - { - if (parameter is T castValue) - return castValue; - - Throw.CustomException(exceptionFactory, parameter); - return default; - } - /// /// Checks if the specified value is a valid enum value of its type. This is true when the specified value /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked diff --git a/Code/Light.GuardClauses/Check.MustBeOfType.cs b/Code/Light.GuardClauses/Check.MustBeOfType.cs new file mode 100644 index 0000000..bbf9001 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeOfType.cs @@ -0,0 +1,47 @@ + +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that can be cast to and returns the cast value, or otherwise throws a . + /// + /// The value to be cast. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when cannot be cast to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message) is T castValue) + return castValue; + + Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); + return default; + } + + /// + /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. + /// + /// The value to be cast. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when cannot be cast to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + { + if (parameter is T castValue) + return castValue; + + Throw.CustomException(exceptionFactory, parameter); + return default; + } +} \ No newline at end of file From 61b7e37961f19c36f1bd68bd4f19888fc64958b3 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 04:56:42 +0100 Subject: [PATCH 006/123] refactor: move IsEmpty and MustNotBeEmpty from CommonAssertions to own files Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 37 --------------- Code/Light.GuardClauses/Check.IsEmpty.cs | 14 ++++++ .../Check.MustNotBeEmpty.cs | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+), 37 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsEmpty.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeEmpty.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 6844fd6..d20975c 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -59,43 +59,6 @@ public static T MustBeValidEnumValue(this T parameter, Func exc return parameter; } - /// - /// Checks if the specified GUID is an empty one. - /// - /// The GUID to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; - - /// - /// Ensures that the specified GUID is not empty, or otherwise throws an . - /// - /// The GUID to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty GUID. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid MustNotBeEmpty(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter == Guid.Empty) - Throw.EmptyGuid(parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. - /// - /// The GUID to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is an empty GUID. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) - { - if (parameter == Guid.Empty) - Throw.CustomException(exceptionFactory); - return parameter; - } - /// /// Checks if the specified is true and throws an in this case. /// diff --git a/Code/Light.GuardClauses/Check.IsEmpty.cs b/Code/Light.GuardClauses/Check.IsEmpty.cs new file mode 100644 index 0000000..46574bb --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsEmpty.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified GUID is an empty one. + /// + /// The GUID to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs b/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs new file mode 100644 index 0000000..f933994 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified GUID is not empty, or otherwise throws an . + /// + /// The GUID to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is an empty GUID. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Guid MustNotBeEmpty( + this Guid parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == Guid.Empty) + { + Throw.EmptyGuid(parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. + /// + /// The GUID to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is an empty GUID. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) + { + if (parameter == Guid.Empty) + { + Throw.CustomException(exceptionFactory); + } + return parameter; + } +} From 04769a1ef97ca3a976361b1eab9ef14aa211215a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 04:58:45 +0100 Subject: [PATCH 007/123] refactor: move IsValidEnumValue and MustBeValidEnumValue from CommonAssertions into own files Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 47 --------------- .../Check.IsValidEnumValue.cs | 18 ++++++ .../Check.MustBeValidEnumValue.cs | 57 +++++++++++++++++++ 3 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsValidEnumValue.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index d20975c..7006260 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,53 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if the specified value is a valid enum value of its type. This is true when the specified value - /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked - /// with the . - /// - /// The type of the enum. - /// The enum value to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidEnumValue(this T parameter) where T : struct, Enum - => EnumInfo.IsValidEnumValue(parameter); - - /// - /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . - /// - /// The type of the enum. - /// The value to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid enum value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct, Enum - { - if (!EnumInfo.IsValidEnumValue(parameter)) - Throw.EnumValueNotDefined(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . - /// - /// The type of the enum. - /// The value to be checked. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is no valid enum value, or when is no enum type. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) where T : struct, Enum - { - if (!EnumInfo.IsValidEnumValue(parameter)) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Checks if the specified is true and throws an in this case. /// diff --git a/Code/Light.GuardClauses/Check.IsValidEnumValue.cs b/Code/Light.GuardClauses/Check.IsValidEnumValue.cs new file mode 100644 index 0000000..4869f25 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsValidEnumValue.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified value is a valid enum value of its type. This is true when the specified value + /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked + /// with the . + /// + /// The type of the enum. + /// The enum value to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsValidEnumValue(this T parameter) where T : struct, Enum => + EnumInfo.IsValidEnumValue(parameter); +} diff --git a/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs b/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs new file mode 100644 index 0000000..9672a6d --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . + /// + /// The type of the enum. + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid enum value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeValidEnumValue( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + where T : struct, Enum + { + if (!EnumInfo.IsValidEnumValue(parameter)) + { + Throw.EnumValueNotDefined(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . + /// + /// The type of the enum. + /// The value to be checked. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is no valid enum value, or when is no enum type. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) + where T : struct, Enum + { + if (!EnumInfo.IsValidEnumValue(parameter)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} From 90969cec8a2083a686cc177a6b3ffa8c1ceaa4c2 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:06:43 +0100 Subject: [PATCH 008/123] refactor: move InvalidOperation from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 13 ----------- .../Check.InvalidOperation.cs | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.InvalidOperation.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 7006260..fa82fef 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,19 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if the specified is true and throws an in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the (optional). - /// Thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidOperation(bool condition, string? message = null) - { - if (condition) - Throw.InvalidOperation(message); - } - /// /// Checks if the specified is true and throws an in this case. /// diff --git a/Code/Light.GuardClauses/Check.InvalidOperation.cs b/Code/Light.GuardClauses/Check.InvalidOperation.cs new file mode 100644 index 0000000..c4ff5b7 --- /dev/null +++ b/Code/Light.GuardClauses/Check.InvalidOperation.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified is true and throws an in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the (optional). + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidOperation(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidOperation(message); + } + } +} From 4b3972515610008a07de79194f29d8a6871ee3d8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:07:04 +0100 Subject: [PATCH 009/123] refactor: move InvalidArgument from CommonAssertions to own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 43 -------------- .../Check.InvalidArgument.cs | 58 +++++++++++++++++++ 2 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.InvalidArgument.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index fa82fef..0aee807 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -25,49 +25,6 @@ public static void InvalidState(bool condition, string? message = null) Throw.InvalidState(message); } - /// - /// Checks if the specified is true and throws an in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The name of the parameter (optional). - /// The message that will be passed to the (optional). - /// Thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) - { - if (condition) - Throw.Argument(parameterName, message); - } - - /// - /// Checks if the specified is true and throws your custom exception in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, Func exceptionFactory) - { - if (condition) - Throw.CustomException(exceptionFactory); - } - - /// - /// Checks if the specified is true and throws your custom exception in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The value that is checked in the . This value is passed to the . - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) - { - if (condition) - Throw.CustomException(exceptionFactory, parameter); - } - /// /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.InvalidArgument.cs b/Code/Light.GuardClauses/Check.InvalidArgument.cs new file mode 100644 index 0000000..cc50e69 --- /dev/null +++ b/Code/Light.GuardClauses/Check.InvalidArgument.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified is true and throws an in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The name of the parameter (optional). + /// The message that will be passed to the (optional). + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) + { + if (condition) + { + Throw.Argument(parameterName, message); + } + } + + /// + /// Checks if the specified is true and throws your custom exception in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, Func exceptionFactory) + { + if (condition) + { + Throw.CustomException(exceptionFactory); + } + } + + /// + /// Checks if the specified is true and throws your custom exception in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The value that is checked in the . This value is passed to the . + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) + { + if (condition) + { + Throw.CustomException(exceptionFactory, parameter); + } + } +} From 95df065a90de2be0c9f277808373e911663e628b Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:09:25 +0100 Subject: [PATCH 010/123] refactor: move InvalidState from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 13 ----------- Code/Light.GuardClauses/Check.InvalidState.cs | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.InvalidState.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 0aee807..e34bc88 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,19 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if the specified is true and throws an in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the . - /// Thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidState(bool condition, string? message = null) - { - if (condition) - Throw.InvalidState(message); - } - /// /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.InvalidState.cs b/Code/Light.GuardClauses/Check.InvalidState.cs new file mode 100644 index 0000000..1e89445 --- /dev/null +++ b/Code/Light.GuardClauses/Check.InvalidState.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified is true and throws an in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the . + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidState(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidState(message); + } + } +} From 047c85abc9500dba5020e3c684c7acaee80d1cbb Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:11:27 +0100 Subject: [PATCH 011/123] refactor: move MustHaveValue from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 32 ------------ .../Light.GuardClauses/Check.MustHaveValue.cs | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveValue.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index e34bc88..ffa50d2 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -12,38 +12,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . - /// - /// The nullable to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct - { - if (!parameter.HasValue) - Throw.NullableHasNoValue(parameterName, message); - - return parameter.Value; - } - - /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. - /// - /// The nullable to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct - { - if (!parameter.HasValue) - Throw.CustomException(exceptionFactory); - - return parameter.Value; - } - /// /// Checks if and point to the same object. /// diff --git a/Code/Light.GuardClauses/Check.MustHaveValue.cs b/Code/Light.GuardClauses/Check.MustHaveValue.cs new file mode 100644 index 0000000..46f447b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveValue.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . + /// + /// The nullable to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has no value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustHaveValue( + [NotNull, NoEnumeration] this T? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : struct + { + if (!parameter.HasValue) + { + Throw.NullableHasNoValue(parameterName, message); + } + + return parameter.Value; + } + + /// + /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. + /// + /// The nullable to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct + { + if (!parameter.HasValue) + { + Throw.CustomException(exceptionFactory); + } + + return parameter.Value; + } +} From 375e8a87d16f3753bf7251b81c490a43a888bc1f Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:13:25 +0100 Subject: [PATCH 012/123] refactor: move IsSameAs from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 11 ----------- Code/Light.GuardClauses/Check.IsSameAs.cs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsSameAs.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index ffa50d2..16bf6fa 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -3,7 +3,6 @@ using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.Exceptions; -using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -12,16 +11,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if and point to the same object. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - // ReSharper disable StringLiteralTypo - [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] - // ReSharper restore StringLiteralTypo - public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) where T : class => - ReferenceEquals(parameter, other); - /// /// Ensures that and do not point to the same object instance, or otherwise /// throws a . diff --git a/Code/Light.GuardClauses/Check.IsSameAs.cs b/Code/Light.GuardClauses/Check.IsSameAs.cs new file mode 100644 index 0000000..d8e3d0f --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsSameAs.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if and point to the same object. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable StringLiteralTypo + [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] + // ReSharper restore StringLiteralTypo + public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) where T : class => + ReferenceEquals(parameter, other); +} From 3a6bc25c6a3e301da3658c81183b14d8e13fbe06 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:15:29 +0100 Subject: [PATCH 013/123] refactor: move MustNotBeSameAs from CommonAssertions to own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 33 ------------ .../Check.MustNotBeSameAs.cs | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeSameAs.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 16bf6fa..c54a385 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -11,39 +11,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws a . - /// - /// The first reference to be checked. - /// The second reference to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when both and point to the same object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class - { - if (ReferenceEquals(parameter, other)) - Throw.SameObjectReference(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws your custom exception. - /// - /// The first reference to be checked. - /// The second reference to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Thrown when both and point to the same object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) where T : class - { - if (ReferenceEquals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Ensures that is equal to using the default equality comparer, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs b/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs new file mode 100644 index 0000000..9b1af7f --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that and do not point to the same object instance, or otherwise + /// throws a . + /// + /// The first reference to be checked. + /// The second reference to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when both and point to the same object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? MustNotBeSameAs( + [NoEnumeration] this T? parameter, + [NoEnumeration] T? other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : class + { + if (ReferenceEquals(parameter, other)) + { + Throw.SameObjectReference(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that and do not point to the same object instance, or otherwise + /// throws your custom exception. + /// + /// The first reference to be checked. + /// The second reference to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Thrown when both and point to the same object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) where T : class + { + if (ReferenceEquals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} From ad1e3b23697c15440cab1b15e1fc50a986ce977e Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:17:13 +0100 Subject: [PATCH 014/123] refactor: move MustBe from CommonAssertions to own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 68 ------------ Code/Light.GuardClauses/Check.MustBe.cs | 102 ++++++++++++++++++ 2 files changed, 102 insertions(+), 68 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBe.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index c54a385..556190d 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -11,74 +11,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws a . - /// - /// The first value to be compared. - /// The other value to be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!EqualityComparer.Default.Equals(parameter, other)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. - /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are not equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, Func exceptionFactory) - { - if (!EqualityComparer.Default.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other); - return parameter; - } - - /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . - /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. - /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are not equal, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); - return parameter; - } - /// /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.MustBe.cs b/Code/Light.GuardClauses/Check.MustBe.cs new file mode 100644 index 0000000..5ae33d5 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBe.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are not equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBe( + this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when and are not equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBe(this T parameter, T other, Func exceptionFactory) + { + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; + } + + /// + /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are not equal. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe( + this T parameter, + T other, + IEqualityComparer equalityComparer, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when and are not equal, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe( + this T parameter, + T other, + IEqualityComparer equalityComparer, + Func, Exception> exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + } + return parameter; + } +} From 99421ca64b3ee873adb12cbf924fc9401bac78fd Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:20:56 +0100 Subject: [PATCH 015/123] refactor: move MustNotBe from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 71 ------------ Code/Light.GuardClauses/Check.MustNotBe.cs | 102 ++++++++++++++++++ 2 files changed, 102 insertions(+), 71 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBe.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 556190d..f1e8445 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; -using JetBrains.Annotations; -using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; @@ -11,74 +8,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . - /// - /// The first value to be compared. - /// The other value to be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (EqualityComparer.Default.Equals(parameter, other)) - Throw.ValuesEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. - /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, Func exceptionFactory) - { - if (EqualityComparer.Default.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other); - return parameter; - } - - /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . - /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) - Throw.ValuesEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. - /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are equal, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || equalityComparer.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); - return parameter; - } - /// /// Checks if the specified value is approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.MustNotBe.cs b/Code/Light.GuardClauses/Check.MustNotBe.cs new file mode 100644 index 0000000..360aad3 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBe.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBe( + this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when and are equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBe(this T parameter, T other, Func exceptionFactory) + { + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; + } + + /// + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are equal. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustNotBe( + this T parameter, + T other, + IEqualityComparer equalityComparer, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when and are equal, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustNotBe( + this T parameter, + T other, + IEqualityComparer equalityComparer, + Func, Exception> exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (equalityComparer is null || equalityComparer.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + } + return parameter; + } +} From 49fb37acccc982754a7bc03d15c37b5561fe234b Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:22:18 +0100 Subject: [PATCH 016/123] refactor: move IsApproximately from CommonAssertions into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 54 ---------------- .../Check.IsApproximately.cs | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 54 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsApproximately.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index f1e8445..2c4b16a 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -8,60 +8,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. - /// - /// The first value to be compared. - /// The second value to be compared. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other, double tolerance) => - Math.Abs(value - other) < tolerance; - - /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other) => - Math.Abs(value - other) < 0.0001; - - /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other, float tolerance) => - Math.Abs(value - other) < tolerance; - - /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other) => - Math.Abs(value - other) < 0.0001f; - /// /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsApproximately.cs b/Code/Light.GuardClauses/Check.IsApproximately.cs new file mode 100644 index 0000000..698450e --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsApproximately.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other, double tolerance) => + Math.Abs(value - other) < tolerance; + + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other) => + Math.Abs(value - other) < 0.0001; + + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other, float tolerance) => + Math.Abs(value - other) < tolerance; + + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other) => + Math.Abs(value - other) < 0.0001f; +} From f2cb77c9fb20510d70e2ffd4c1a615b849f413ee Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:23:32 +0100 Subject: [PATCH 017/123] refactor: move IsGreaterThanOrApproximately into own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 54 ----------------- .../Check.IsGreaterThanOrApproximately.cs | 60 +++++++++++++++++++ 2 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 2c4b16a..bc1e519 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -8,60 +8,6 @@ namespace Light.GuardClauses; /// public static partial class Check { - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => - value > other || value.IsApproximately(other, tolerance); - - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other) => - value > other || value.IsApproximately(other); - - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => - value > other || value.IsApproximately(other, tolerance); - - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other) => - value > other || value.IsApproximately(other); - /// /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs new file mode 100644 index 0000000..f3dda4e --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs @@ -0,0 +1,60 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => + value > other || value.IsApproximately(other, tolerance); + + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this double value, double other) => + value > other || value.IsApproximately(other); + + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => + value > other || value.IsApproximately(other, tolerance); + + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this float value, float other) => + value > other || value.IsApproximately(other); +} From 6275fd0ce95c0c8a1771dcc2b70c85a996eb8758 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:24:48 +0100 Subject: [PATCH 018/123] refactor: move IsLessThanOrApproximately to own file Signed-off-by: Kenny Pflug --- .../Check.CommonAssertions.cs | 62 +------------------ .../Check.IsLessThanOrApproximately.cs | 60 ++++++++++++++++++ 2 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index bc1e519..5a744b9 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -1,64 +1,6 @@ -using System; -using System.Runtime.CompilerServices; - -namespace Light.GuardClauses; +namespace Light.GuardClauses; /// /// The class provides access to all assertions of Light.GuardClauses. /// -public static partial class Check -{ - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => - value < other || value.IsApproximately(other, tolerance); - - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other) => - value < other || value.IsApproximately(other); - - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => - value < other || value.IsApproximately(other, tolerance); - - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other) => - value < other || value.IsApproximately(other); -} \ No newline at end of file +public static partial class Check; \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs new file mode 100644 index 0000000..4c84240 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs @@ -0,0 +1,60 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => + value < other || value.IsApproximately(other, tolerance); + + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this double value, double other) => + value < other || value.IsApproximately(other); + + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => + value < other || value.IsApproximately(other, tolerance); + + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other) => + value < other || value.IsApproximately(other); +} From c048a7bc954cd5a7d441ac98fcc1e23fc03e3f26 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 05:25:30 +0100 Subject: [PATCH 019/123] refactor: rename file Check.CommonAssertions.cs to Check.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/{Check.CommonAssertions.cs => Check.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Code/Light.GuardClauses/{Check.CommonAssertions.cs => Check.cs} (100%) diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.cs similarity index 100% rename from Code/Light.GuardClauses/Check.CommonAssertions.cs rename to Code/Light.GuardClauses/Check.cs From d757ec4c978de9675a17a0b0b33ea70895e724b3 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 06:24:40 +0100 Subject: [PATCH 020/123] chore: update ReSharper settings for new style guide Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses.sln.DotSettings | 76 ++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/Code/Light.GuardClauses.sln.DotSettings b/Code/Light.GuardClauses.sln.DotSettings index 47d8c24..be20ec1 100644 --- a/Code/Light.GuardClauses.sln.DotSettings +++ b/Code/Light.GuardClauses.sln.DotSettings @@ -2,16 +2,32 @@ True None False + WARNING + WARNING + DO_NOT_SHOW HINT HINT + WARNING HINT WARNING + True <?xml version="1.0" encoding="utf-16"?><Profile name="Kenny's Kleanup"><CSReorderTypeMembers>True</CSReorderTypeMembers><CSUpdateFileHeader>True</CSUpdateFileHeader><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CppReformatCode>True</CppReformatCode><XMLReformatCode>True</XMLReformatCode><CssReformatCode>True</CssReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" ArrangeVarStyle="False" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><RemoveCodeRedundancies>True</RemoveCodeRedundancies><HtmlReformatCode>True</HtmlReformatCode><JsInsertSemicolon>True</JsInsertSemicolon><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor></Profile> - C# Cleanup - Kenny's Kleanup + Kenny's Kleanup + Built-in: Full Cleanup + False + Required + Required + Required + Required + ExpressionBody + ExpressionBody + ExpressionBody + True + False True True + True True @@ -22,23 +38,64 @@ True True True + True TOGETHER_SAME_LINE + NO_INDENT True True + True + True + False True + True + False + True + 1 + 3 + COMPACT True + True True True NEVER - NEVER - NEVER - NEVER + + + + NEVER + False + ALWAYS + IF_OWNER_IS_SINGLE_LINE + NEVER DO_NOT_CHANGE + True + True True - 500 - False + False + True + True + CHOP_IF_LONG + CHOP_IF_LONG + False + True + True + True + True + True + False + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_IF_LONG + + + CHOP_IF_LONG + CHOP_ALWAYS + CHOP_IF_LONG RemoveIndent RemoveIndent + False + ByFirstAttr + 150 + False False False False @@ -192,16 +249,21 @@ </TypePattern> </Patterns> False + False False True Replace Replace False + True True False False True False + False + True + False <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> From 87ed851c13845fc1ad45ff63cd1d68afd3f5cf50 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 06:25:01 +0100 Subject: [PATCH 021/123] code style: apply new style to some files Signed-off-by: Kenny Pflug --- .../CallerArgumentExpressionAttribute.cs | 8 +++----- .../Check.IsGreaterThanOrApproximately.cs | 2 +- .../Light.GuardClauses/Check.IsLessThanOrApproximately.cs | 6 +++--- Code/Light.GuardClauses/Check.IsSameAs.cs | 4 +++- Code/Light.GuardClauses/Check.MustBe.cs | 4 ++++ Code/Light.GuardClauses/Check.MustHaveValue.cs | 3 ++- Code/Light.GuardClauses/Check.MustNotBeDefault.cs | 2 +- Code/Light.GuardClauses/Check.MustNotBeNullReference.cs | 2 +- Code/Light.GuardClauses/Check.cs | 2 +- Code/Light.GuardClauses/StringComparisonType.cs | 4 ++-- Code/Light.GuardClauses/Types.cs | 2 +- 11 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs b/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs index 36a1f72..f5df610 100644 --- a/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs +++ b/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs @@ -2,14 +2,12 @@ // ReSharper disable once CheckNamespace -- CallerArgumentExpression must be in exactly this namespace namespace System.Runtime.CompilerServices; -[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class CallerArgumentExpressionAttribute : Attribute { - public CallerArgumentExpressionAttribute(string parameterName) - { + public CallerArgumentExpressionAttribute(string parameterName) => ParameterName = parameterName; - } public string ParameterName { get; } } -#endif \ No newline at end of file +#endif diff --git a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs index f3dda4e..caca671 100644 --- a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs @@ -30,7 +30,7 @@ public static bool IsGreaterThanOrApproximately(this double value, double other, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsGreaterThanOrApproximately(this double value, double other) => value > other || value.IsApproximately(other); - + /// /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs index 4c84240..c24c1a0 100644 --- a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs @@ -17,7 +17,7 @@ public static partial class Check [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); - + /// /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. /// @@ -30,7 +30,7 @@ public static bool IsLessThanOrApproximately(this double value, double other, do [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); - + /// /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// @@ -44,7 +44,7 @@ public static bool IsLessThanOrApproximately(this double value, double other) => [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); - + /// /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. /// diff --git a/Code/Light.GuardClauses/Check.IsSameAs.cs b/Code/Light.GuardClauses/Check.IsSameAs.cs index d8e3d0f..8d4c063 100644 --- a/Code/Light.GuardClauses/Check.IsSameAs.cs +++ b/Code/Light.GuardClauses/Check.IsSameAs.cs @@ -10,7 +10,9 @@ public static partial class Check /// [MethodImpl(MethodImplOptions.AggressiveInlining)] // ReSharper disable StringLiteralTypo - [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] + [ContractAnnotation( + "parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull" + )] // ReSharper restore StringLiteralTypo public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) where T : class => ReferenceEquals(parameter, other); diff --git a/Code/Light.GuardClauses/Check.MustBe.cs b/Code/Light.GuardClauses/Check.MustBe.cs index 5ae33d5..9bdff30 100644 --- a/Code/Light.GuardClauses/Check.MustBe.cs +++ b/Code/Light.GuardClauses/Check.MustBe.cs @@ -28,6 +28,7 @@ public static T MustBe( { Throw.ValuesNotEqual(parameter, other, parameterName, message); } + return parameter; } @@ -45,6 +46,7 @@ public static T MustBe(this T parameter, T other, Func excep { Throw.CustomException(exceptionFactory, parameter, other); } + return parameter; } @@ -72,6 +74,7 @@ public static T MustBe( { Throw.ValuesNotEqual(parameter, other, parameterName, message); } + return parameter; } @@ -97,6 +100,7 @@ Func, Exception> exceptionFactory { Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); } + return parameter; } } diff --git a/Code/Light.GuardClauses/Check.MustHaveValue.cs b/Code/Light.GuardClauses/Check.MustHaveValue.cs index 46f447b..b20b452 100644 --- a/Code/Light.GuardClauses/Check.MustHaveValue.cs +++ b/Code/Light.GuardClauses/Check.MustHaveValue.cs @@ -38,7 +38,8 @@ public static T MustHaveValue( /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : struct { if (!parameter.HasValue) { diff --git a/Code/Light.GuardClauses/Check.MustNotBeDefault.cs b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs index 167a5c9..d2fade3 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeDefault.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs @@ -78,4 +78,4 @@ Func exceptionFactory return parameter; #pragma warning restore CS8777 } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs index 6b96c5e..ffd3ce3 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs @@ -74,4 +74,4 @@ Func exceptionFactory return parameter; } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/Check.cs b/Code/Light.GuardClauses/Check.cs index 5a744b9..ac14024 100644 --- a/Code/Light.GuardClauses/Check.cs +++ b/Code/Light.GuardClauses/Check.cs @@ -3,4 +3,4 @@ /// /// The class provides access to all assertions of Light.GuardClauses. /// -public static partial class Check; \ No newline at end of file +public static partial class Check; diff --git a/Code/Light.GuardClauses/StringComparisonType.cs b/Code/Light.GuardClauses/StringComparisonType.cs index f127498..dd2c84a 100644 --- a/Code/Light.GuardClauses/StringComparisonType.cs +++ b/Code/Light.GuardClauses/StringComparisonType.cs @@ -54,5 +54,5 @@ public enum StringComparisonType /// Compare strings using ordinal sort rules, ignoring the case and ignoring the /// white space characters of the strings being compared. /// - OrdinalIgnoreCaseIgnoreWhiteSpace = 7 -} \ No newline at end of file + OrdinalIgnoreCaseIgnoreWhiteSpace = 7, +} diff --git a/Code/Light.GuardClauses/Types.cs b/Code/Light.GuardClauses/Types.cs index 410c463..f340a05 100644 --- a/Code/Light.GuardClauses/Types.cs +++ b/Code/Light.GuardClauses/Types.cs @@ -11,4 +11,4 @@ public abstract class Types /// Gets the type. /// public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); -} \ No newline at end of file +} From 12ef6a6bf460c20342aa0afbc5baeaa82ac5cb43 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 11 Dec 2024 06:33:34 +0100 Subject: [PATCH 022/123] chore: update Light.GuardClauses.AllProjects.sln.DotSettings for new code style Signed-off-by: Kenny Pflug --- ...t.GuardClauses.AllProjects.sln.DotSettings | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/Code/Light.GuardClauses.AllProjects.sln.DotSettings b/Code/Light.GuardClauses.AllProjects.sln.DotSettings index 2c4519b..be20ec1 100644 --- a/Code/Light.GuardClauses.AllProjects.sln.DotSettings +++ b/Code/Light.GuardClauses.AllProjects.sln.DotSettings @@ -2,16 +2,32 @@ True None False + WARNING + WARNING + DO_NOT_SHOW HINT HINT + WARNING HINT WARNING + True <?xml version="1.0" encoding="utf-16"?><Profile name="Kenny's Kleanup"><CSReorderTypeMembers>True</CSReorderTypeMembers><CSUpdateFileHeader>True</CSUpdateFileHeader><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CppReformatCode>True</CppReformatCode><XMLReformatCode>True</XMLReformatCode><CssReformatCode>True</CssReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" ArrangeVarStyle="False" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><RemoveCodeRedundancies>True</RemoveCodeRedundancies><HtmlReformatCode>True</HtmlReformatCode><JsInsertSemicolon>True</JsInsertSemicolon><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor></Profile> - C# Cleanup - Kenny's Kleanup + Kenny's Kleanup + Built-in: Full Cleanup + False + Required + Required + Required + Required + ExpressionBody + ExpressionBody + ExpressionBody + True + False True True + True True @@ -22,23 +38,64 @@ True True True + True TOGETHER_SAME_LINE + NO_INDENT True True + True + True + False True + True + False + True + 1 + 3 + COMPACT True + True True True NEVER - NEVER - NEVER - NEVER + + + + NEVER + False + ALWAYS + IF_OWNER_IS_SINGLE_LINE + NEVER DO_NOT_CHANGE + True + True True - 500 - False + False + True + True + CHOP_IF_LONG + CHOP_IF_LONG + False + True + True + True + True + True + False + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_IF_LONG + + + CHOP_IF_LONG + CHOP_ALWAYS + CHOP_IF_LONG RemoveIndent RemoveIndent + False + ByFirstAttr + 150 + False False False False @@ -192,16 +249,21 @@ </TypePattern> </Patterns> False + False False True Replace Replace False + True True False False True False + False + True + False <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> @@ -255,13 +317,4 @@ True True True - True - True - True - True - True - True - True - True - True - True \ No newline at end of file + True \ No newline at end of file From 40896c31606e40a933fe30dd6273aa842021fd2a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 12 Dec 2024 06:16:50 +0100 Subject: [PATCH 023/123] refactor: move all assertions of Check.ComparableAssertions.cs into own files Signed-off-by: Kenny Pflug --- .../Check.ComparableAssertions.cs | 411 ------------------ Code/Light.GuardClauses/Check.IsIn.cs | 20 + Code/Light.GuardClauses/Check.IsNotIn.cs | 20 + .../Check.MustBeGreaterThan.cs | 60 +++ .../Check.MustBeGreaterThanOrEqualTo.cs | 60 +++ Code/Light.GuardClauses/Check.MustBeIn.cs | 61 +++ .../Check.MustBeLessThan.cs | 60 +++ .../Check.MustBeLessThanOrEqualTo.cs | 60 +++ .../Check.MustNotBeGreaterThan.cs | 45 ++ .../Check.MustNotBeGreaterThanOrEqualTo.cs | 60 +++ Code/Light.GuardClauses/Check.MustNotBeIn.cs | 61 +++ .../Check.MustNotBeLessThan.cs | 60 +++ .../Check.MustNotBeLessThanOrEqualTo.cs | 60 +++ 13 files changed, 627 insertions(+), 411 deletions(-) delete mode 100644 Code/Light.GuardClauses/Check.ComparableAssertions.cs create mode 100644 Code/Light.GuardClauses/Check.IsIn.cs create mode 100644 Code/Light.GuardClauses/Check.IsNotIn.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeGreaterThan.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeIn.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeLessThan.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeIn.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeLessThan.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs diff --git a/Code/Light.GuardClauses/Check.ComparableAssertions.cs b/Code/Light.GuardClauses/Check.ComparableAssertions.cs deleted file mode 100644 index 0eb9020..0000000 --- a/Code/Light.GuardClauses/Check.ComparableAssertions.cs +++ /dev/null @@ -1,411 +0,0 @@ -using System; -using JetBrains.Annotations; -using System.Runtime.CompilerServices; -using Light.GuardClauses.Exceptions; -using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; - -namespace Light.GuardClauses; - -public static partial class Check -{ - /* - * ------------------------------------- - * Must Not Be Less Than - * Must Be Greater Than or Equal To - * ------------------------------------- - */ - /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustNotBeLessThan(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs off - if (parameter is null || parameter.CompareTo(other) < 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /* - * ------------------------------------- - * Must Be Less Than - * Must Not Be Greater Than or Equal To - * ------------------------------------- - */ - /// - /// Ensures that the specified is less than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustBeLessThan(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /// - /// Ensures that the specified is less than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /* - * ------------------------------------- - * Must Be Greater Than - * Must Not Be Less Than or Equal To - * ------------------------------------- - */ - /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustBeGreaterThan(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /* - * ------------------------------------- - * Must Not Be Greater Than - * Must Be Less Than or Equal To - * ------------------------------------- - */ - /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. - /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); - return parameter; - } - - /* - * ------------------------------------- - * Ranges - * ------------------------------------- - */ - /// - /// Checks if the value is within the specified range. - /// - /// The comparable to be checked. - /// The range where must be in-between. - /// True if the parameter is within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsIn([NotNull, ValidatedNotNull] this T parameter, Range range) where T : IComparable => range.IsValueWithinRange(parameter); - - /// - /// Checks if the value is not within the specified range. - /// - /// The comparable to be checked. - /// The range where must not be in-between. - /// True if the parameter is not within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNotIn([NotNull, ValidatedNotNull] this T parameter, Range range) where T : IComparable => !range.IsValueWithinRange(parameter); - - /// - /// Ensures that is within the specified range, or otherwise throws an . - /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must be in-between. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not within . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustBeInRange(parameter, range, parameterName, message); - return parameter; - } - - /// - /// Ensures that is within the specified range, or otherwise throws your custom exception. - /// - /// The parameter to be checked. - /// The range where must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not within , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || !range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); - return parameter; - } - - /// - /// Ensures that is not within the specified range, or otherwise throws an . - /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must not be in-between. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is within . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable - { - if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustNotBeInRange(parameter, range, parameterName, message); - return parameter; - } - - /// - /// Ensures that is not within the specified range, or otherwise throws your custom exception. - /// - /// The parameter to be checked. - /// The range where must not be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is within , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); - return parameter; - } -} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.IsIn.cs b/Code/Light.GuardClauses/Check.IsIn.cs new file mode 100644 index 0000000..b33484f --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsIn.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.CompilerServices; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the value is within the specified range. + /// + /// The comparable to be checked. + /// The range where must be in-between. + /// True if the parameter is within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsIn([NotNull] [ValidatedNotNull] this T parameter, Range range) + where T : IComparable => + range.IsValueWithinRange(parameter); +} diff --git a/Code/Light.GuardClauses/Check.IsNotIn.cs b/Code/Light.GuardClauses/Check.IsNotIn.cs new file mode 100644 index 0000000..3cf76a6 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsNotIn.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.CompilerServices; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the value is not within the specified range. + /// + /// The comparable to be checked. + /// The range where must not be in-between. + /// True if the parameter is not within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNotIn([NotNull] [ValidatedNotNull] this T parameter, Range range) + where T : IComparable => + !range.IsValueWithinRange(parameter); +} diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs new file mode 100644 index 0000000..c8b6234 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeGreaterThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + { + Throw.MustBeGreaterThan(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs new file mode 100644 index 0000000..90cd127 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeGreaterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + { + Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeIn.cs b/Code/Light.GuardClauses/Check.MustBeIn.cs new file mode 100644 index 0000000..b727d36 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeIn.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that is within the specified range, or otherwise throws an . + /// + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not within . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeIn( + [NotNull] [ValidatedNotNull] this T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + { + Throw.MustBeInRange(parameter, range, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that is within the specified range, or otherwise throws your custom exception. + /// + /// The parameter to be checked. + /// The range where must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not within , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeIn( + [NotNull] [ValidatedNotNull] this T parameter, + Range range, + Func, Exception> exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || !range.IsValueWithinRange(parameter)) + { + Throw.CustomException(exceptionFactory, parameter!, range); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeLessThan.cs b/Code/Light.GuardClauses/Check.MustBeLessThan.cs new file mode 100644 index 0000000..21faebd --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLessThan.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + { + Throw.MustBeLessThan(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs new file mode 100644 index 0000000..9e813df --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + { + Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs b/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs new file mode 100644 index 0000000..4c02346 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs @@ -0,0 +1,45 @@ +using System; +using JetBrains.Annotations; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + return parameter; + } + + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs new file mode 100644 index 0000000..26a996e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + { + Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeIn.cs b/Code/Light.GuardClauses/Check.MustNotBeIn.cs new file mode 100644 index 0000000..4b7a385 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeIn.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that is not within the specified range, or otherwise throws an . + /// + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is within . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeIn( + [NotNull] [ValidatedNotNull] this T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + { + Throw.MustNotBeInRange(parameter, range, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// + /// The parameter to be checked. + /// The range where must not be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is within , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeIn( + [NotNull] [ValidatedNotNull] this T parameter, + Range range, + Func, Exception> exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || range.IsValueWithinRange(parameter)) + { + Throw.CustomException(exceptionFactory, parameter!, range); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs b/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs new file mode 100644 index 0000000..e3f21b7 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + { + Throw.MustNotBeLessThan(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThan( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs new file mode 100644 index 0000000..86b0574 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + { + Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThanOrEqualTo( + [NotNull] [ValidatedNotNull] this T parameter, + T other, + Func exceptionFactory + ) where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } +} From 95706b5bdb68ffcb91b7c2b71fd153be3e446f85 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 12 Dec 2024 06:44:14 +0100 Subject: [PATCH 024/123] refactor: move assertions from Check.UriAssertions.cs into their own files Signed-off-by: Kenny Pflug --- .../Check.MustBeAbsoluteUri.cs | 55 ++++ .../Check.MustBeHttpOrHttpsUrl.cs | 58 ++++ .../Light.GuardClauses/Check.MustBeHttpUrl.cs | 42 +++ .../Check.MustBeHttpsUrl.cs | 42 +++ .../Check.MustBeRelativeUri.cs | 55 ++++ .../Check.MustHaveOneSchemeOf.cs | 94 ++++++ .../Check.MustHaveScheme.cs | 83 +++++ .../Light.GuardClauses/Check.UriAssertions.cs | 288 ------------------ 8 files changed, 429 insertions(+), 288 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeHttpUrl.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeHttpsUrl.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeRelativeUri.cs create mode 100644 Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs create mode 100644 Code/Light.GuardClauses/Check.MustHaveScheme.cs delete mode 100644 Code/Light.GuardClauses/Check.UriAssertions.cs diff --git a/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs b/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs new file mode 100644 index 0000000..88415b0 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified URI is an absolute one, or otherwise throws a . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not an absolute URI. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeAbsoluteUri( + [NotNull] [ValidatedNotNull] this Uri? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) + { + Throw.MustBeAbsoluteUri(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when is not an absolute URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeAbsoluteUri( + [NotNull] [ValidatedNotNull] this Uri? parameter, + Func exceptionFactory + ) + { + if (parameter is null || parameter.IsAbsoluteUri == false) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs b/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs new file mode 100644 index 0000000..a431469 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "http" or "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpOrHttpsUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && + parameter.Scheme.Equals("http") == false) + { + Throw.UriMustHaveOneSchemeOf(parameter, ["https", "http"], parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http" or "https", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpOrHttpsUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + Func exceptionFactory + ) + { + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && + parameter.Scheme.Equals("http") == false) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeHttpUrl.cs b/Code/Light.GuardClauses/Check.MustBeHttpUrl.cs new file mode 100644 index 0000000..717be5f --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeHttpUrl.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "http". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustHaveScheme("http", parameterName, message); + + /// + /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + Func exceptionFactory + ) => + parameter.MustHaveScheme("http", exceptionFactory); +} diff --git a/Code/Light.GuardClauses/Check.MustBeHttpsUrl.cs b/Code/Light.GuardClauses/Check.MustBeHttpsUrl.cs new file mode 100644 index 0000000..4e2169c --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeHttpsUrl.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustHaveScheme("https", parameterName, message); + + /// + /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "https", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl( + [NotNull] [ValidatedNotNull] this Uri? parameter, + Func exceptionFactory + ) => + parameter.MustHaveScheme("https", exceptionFactory); +} diff --git a/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs b/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs new file mode 100644 index 0000000..6864c3d --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified URI is a relative one, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is an absolute URI. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeRelativeUri( + [NotNull] [ValidatedNotNull] this Uri? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) + { + Throw.MustBeRelativeUri(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is an absolute URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeRelativeUri( + [NotNull] [ValidatedNotNull] this Uri? parameter, + Func exceptionFactory + ) + { + if (parameter is null || parameter.IsAbsoluteUri) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs b/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs new file mode 100644 index 0000000..19e6b80 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the URI has one of the specified schemes, or otherwise throws an . + /// + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the scheme is not equal to one of the specified schemes. + /// Thrown when is relative and thus has no scheme. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] + public static Uri MustHaveOneSchemeOf( + [NotNull] [ValidatedNotNull] this Uri? parameter, + IEnumerable schemes, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + // ReSharper disable PossibleMultipleEnumeration + parameter.MustBeAbsoluteUri(parameterName, message); + + if (schemes is ICollection collection) + { + if (!collection.Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + } + + return parameter; + } + + if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + } + + return parameter; + // ReSharper restore PossibleMultipleEnumeration + } + + /// + /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, or when is a relative URI, or when is null. + /// Thrown when is null. + /// The type of the collection containing the schemes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveOneSchemeOf( + [NotNull] [ValidatedNotNull] this Uri? parameter, + TCollection schemes, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is null || !parameter.IsAbsoluteUri) + { + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + if (schemes is ICollection collection) + { + if (!collection.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + return parameter; + } + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (schemes is null || !schemes.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes!); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustHaveScheme.cs b/Code/Light.GuardClauses/Check.MustHaveScheme.cs new file mode 100644 index 0000000..aef3ae0 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveScheme.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the has the specified scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than the specified one. + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme( + [NotNull] [ValidatedNotNull] this Uri? parameter, + string scheme, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) + { + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme( + [NotNull] [ValidatedNotNull] this Uri? parameter, + string scheme, + Func exceptionFactory + ) + { + if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. and are passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme( + [NotNull] [ValidatedNotNull] this Uri? parameter, + string scheme, + Func exceptionFactory + ) + { + if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) + { + Throw.CustomException(exceptionFactory, parameter, scheme); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.UriAssertions.cs b/Code/Light.GuardClauses/Check.UriAssertions.cs deleted file mode 100644 index a45d6c8..0000000 --- a/Code/Light.GuardClauses/Check.UriAssertions.cs +++ /dev/null @@ -1,288 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using Light.GuardClauses.Exceptions; -using System.Runtime.CompilerServices; -using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; - -namespace Light.GuardClauses; - -public static partial class Check -{ - /// - /// Ensures that the specified URI is an absolute one, or otherwise throws a . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not an absolute URI. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) - Throw.MustBeAbsoluteUri(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when is not an absolute URI, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) - { - if (parameter is null || parameter.IsAbsoluteUri == false) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the specified URI is a relative one, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an absolute URI. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) - Throw.MustBeRelativeUri(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an absolute URI, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) - { - if (parameter is null || parameter.IsAbsoluteUri) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the has the specified scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than the specified one. - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) - Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); - return parameter; - } - - /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than the specified one, - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) - { - if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. and are passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than the specified one, - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) - { - if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) - Throw.CustomException(exceptionFactory, parameter, scheme); - return parameter; - } - - /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "https". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); - - /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than "https", - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); - - /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); - - /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than "http", - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); - - /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http" or "https". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) - Throw.UriMustHaveOneSchemeOf(parameter, new[] { "https", "http" }, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than "http" or "https", - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) - { - if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws an . - /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the scheme is not equal to one of the specified schemes. - /// Thrown when is relative and thus has no scheme. - /// Throw when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] - public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - // ReSharper disable PossibleMultipleEnumeration - parameter.MustBeAbsoluteUri(parameterName, message); - - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - return parameter; - } - - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - return parameter; - // ReSharper restore PossibleMultipleEnumeration - } - - /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. - /// The delegate that creates your custom exception. - /// - /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, - /// or when is a relative URI, - /// or when is null. - /// - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is null || !parameter.IsAbsoluteUri) - Throw.CustomException(exceptionFactory, parameter, schemes); - - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - Throw.CustomException(exceptionFactory, parameter, schemes); - return parameter; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (schemes is null || !schemes.Contains(parameter.Scheme)) - Throw.CustomException(exceptionFactory, parameter, schemes!); - return parameter; - } -} \ No newline at end of file From dcc709d86dc3a1b88f1137c22fdf31ebe6ae4aa4 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 17:37:04 +0100 Subject: [PATCH 025/123] refactor: move assertions from Check.TypeAssertions.cs into their own files Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.DerivesFrom.cs | 76 ++++ Code/Light.GuardClauses/Check.Implements.cs | 79 +++++ Code/Light.GuardClauses/Check.InheritsFrom.cs | 61 ++++ .../Check.IsEquivalentTypeTo.cs | 36 ++ .../Check.IsOpenConstructedGenericType.cs | 26 ++ .../Check.IsOrDerivesFrom.cs | 48 +++ .../Check.IsOrImplements.cs | 58 +++ .../Check.IsOrInheritsFrom.cs | 56 +++ .../Check.TypeAssertions.cs | 332 ------------------ 9 files changed, 440 insertions(+), 332 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.DerivesFrom.cs create mode 100644 Code/Light.GuardClauses/Check.Implements.cs create mode 100644 Code/Light.GuardClauses/Check.InheritsFrom.cs create mode 100644 Code/Light.GuardClauses/Check.IsEquivalentTypeTo.cs create mode 100644 Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs create mode 100644 Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs create mode 100644 Code/Light.GuardClauses/Check.IsOrImplements.cs create mode 100644 Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs delete mode 100644 Code/Light.GuardClauses/Check.TypeAssertions.cs diff --git a/Code/Light.GuardClauses/Check.DerivesFrom.cs b/Code/Light.GuardClauses/Check.DerivesFrom.cs new file mode 100644 index 0000000..8be3fd3 --- /dev/null +++ b/Code/Light.GuardClauses/Check.DerivesFrom.cs @@ -0,0 +1,76 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified type derives from the other type. Internally, this method uses + /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type info to be checked. + /// The base class that should derive from. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt")] + public static bool DerivesFrom( + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type baseClass + ) + { + baseClass.MustNotBeNull(nameof(baseClass)); + + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (currentBaseType.IsEquivalentTypeTo(baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; + } + + return false; + } + + /// + /// Checks if the specified type derives from the other type. This overload uses the specified + /// to compare the types. + /// + /// The type info to be checked. + /// The base class that should derive from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] + public static bool DerivesFrom( + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type baseClass, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) + { + baseClass.MustNotBeNull(nameof(baseClass)); + typeComparer.MustNotBeNull(nameof(typeComparer)); + + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (typeComparer.Equals(currentBaseType, baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; + } + + return false; + } +} diff --git a/Code/Light.GuardClauses/Check.Implements.cs b/Code/Light.GuardClauses/Check.Implements.cs new file mode 100644 index 0000000..e0643d4 --- /dev/null +++ b/Code/Light.GuardClauses/Check.Implements.cs @@ -0,0 +1,79 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the type implements the specified interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The interface type that should implement. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type interfaceType + ) + { + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) + { + if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) + { + return true; + } + } + + return false; + } + + /// + /// Checks if the type implements the specified interface type. This overload uses the specified + /// to compare the interface types. + /// + /// The type to be checked. + /// The interface type that should implement. + /// The equality comparer used to compare the interface types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type interfaceType, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) + { + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + typeComparer.MustNotBeNull(); + + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) + { + if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) + { + return true; + } + } + + return false; + } +} diff --git a/Code/Light.GuardClauses/Check.InheritsFrom.cs b/Code/Light.GuardClauses/Check.InheritsFrom.cs new file mode 100644 index 0000000..6e2ad44 --- /dev/null +++ b/Code/Light.GuardClauses/Check.InheritsFrom.cs @@ -0,0 +1,61 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type baseClassOrInterfaceType + ) => + baseClassOrInterfaceType + .MustNotBeNull(nameof(baseClassOrInterfaceType)) + .IsInterface ? + type.Implements(baseClassOrInterfaceType) : + type.DerivesFrom(baseClassOrInterfaceType); + + /// + /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified + /// to compare the types. + /// + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type baseClassOrInterfaceType, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) => + baseClassOrInterfaceType + .MustNotBeNull(nameof(baseClassOrInterfaceType)) + .IsInterface ? + type.Implements(baseClassOrInterfaceType, typeComparer) : + type.DerivesFrom(baseClassOrInterfaceType, typeComparer); +} diff --git a/Code/Light.GuardClauses/Check.IsEquivalentTypeTo.cs b/Code/Light.GuardClauses/Check.IsEquivalentTypeTo.cs new file mode 100644 index 0000000..585abaa --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsEquivalentTypeTo.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the two specified types are equivalent. This is true when both types are equal or + /// when one type is a constructed generic type and the other type is the corresponding generic type definition. + /// + /// The first type to be checked. + /// The other type to be checked. + /// + /// True if both types are null, or if both are equal, or if one type + /// is a constructed generic type and the other one is the corresponding generic type definition, else false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEquivalentTypeTo(this Type? type, Type? other) => + ReferenceEquals(type, other) || + (type is not null && + other is not null && + (type == other || + (type.IsConstructedGenericType != other.IsConstructedGenericType && + CheckTypeEquivalency(type, other)))); + + private static bool CheckTypeEquivalency(Type type, Type other) + { + if (type.IsConstructedGenericType) + { + return type.GetGenericTypeDefinition() == other; + } + + return other.GetGenericTypeDefinition() == type; + } +} diff --git a/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs b/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs new file mode 100644 index 0000000..7d8348e --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs @@ -0,0 +1,26 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given is a generic type that has open generic parameters, + /// but is no generic type definition. + /// + /// The type to be checked. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOpenConstructedGenericType([NotNull] [ValidatedNotNull] this Type type) => + type.MustNotBeNull(nameof(type)).IsGenericType && + type.ContainsGenericParameters && + type.IsGenericTypeDefinition == false; +} diff --git a/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs b/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs new file mode 100644 index 0000000..ed74bee --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs @@ -0,0 +1,48 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given is equal to the specified or if it derives from it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrDerivesFrom( + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType + ) => + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); + + /// + /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified + /// to compare the types. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrDerivesFrom( + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) => + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || + type.DerivesFrom(otherType, typeComparer); +} diff --git a/Code/Light.GuardClauses/Check.IsOrImplements.cs b/Code/Light.GuardClauses/Check.IsOrImplements.cs new file mode 100644 index 0000000..aea0e10 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsOrImplements.cs @@ -0,0 +1,58 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given is equal to the specified or if it implements it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType + ) => + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); + + /// + /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified + /// to compare the types. + /// + /// , + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// The equality comparer used to compare the interface types. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) => + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals( + type.MustNotBeNull(nameof(type)), + otherType.MustNotBeNull(nameof(otherType)) + ) || + type.Implements(otherType, typeComparer); +} diff --git a/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs b/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs new file mode 100644 index 0000000..62549f4 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs @@ -0,0 +1,56 @@ +#if NET8_0 +using System.Diagnostics.CodeAnalysis; +#endif +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions + /// are regarded as equal. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType + ) => + type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); + + + /// + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// This overload uses the specified to compare the types. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull] [ValidatedNotNull] this Type type, + [NotNull] [ValidatedNotNull] Type otherType, + [NotNull] [ValidatedNotNull] IEqualityComparer typeComparer + ) => + typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || + type.InheritsFrom(otherType, typeComparer); +} diff --git a/Code/Light.GuardClauses/Check.TypeAssertions.cs b/Code/Light.GuardClauses/Check.TypeAssertions.cs deleted file mode 100644 index 3fd8a0d..0000000 --- a/Code/Light.GuardClauses/Check.TypeAssertions.cs +++ /dev/null @@ -1,332 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using JetBrains.Annotations; -#if NET8_0 -using System.Diagnostics.CodeAnalysis; -#endif -using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; - -namespace Light.GuardClauses; - -public static partial class Check -{ - /// - /// Checks if the two specified types are equivalent. This is true when both types are equal or - /// when one type is a constructed generic type and the other type is the corresponding generic type definition. - /// - /// The first type to be checked. - /// The other type to be checked. - /// - /// True if both types are null, or if both are equal, or if one type - /// is a constructed generic type and the other one is the corresponding generic type definition, else false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEquivalentTypeTo(this Type? type, Type? other) => - ReferenceEquals(type, other) || - !(type is null) && - !(other is null) && - (type == other || - type.IsConstructedGenericType != other.IsConstructedGenericType && - CheckTypeEquivalency(type, other)); - - private static bool CheckTypeEquivalency(Type type, Type other) - { - if (type.IsConstructedGenericType) - return type.GetGenericTypeDefinition() == other; - return other.GetGenericTypeDefinition() == type; - } - - /// - /// Checks if the type implements the specified interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The interface type that should implement. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type interfaceType - // ReSharper restore RedundantNullableFlowAttribute - ) - { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) - return true; - } - - return false; - } - - /// - /// Checks if the type implements the specified interface type. This overload uses the specified - /// to compare the interface types. - /// - /// The type to be checked. - /// The interface type that should implement. - /// The equality comparer used to compare the interface types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type interfaceType, - [NotNull, ValidatedNotNull] IEqualityComparer typeComparer - // ReSharper restore RedundantNullableFlowAttribute - ) - { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - typeComparer.MustNotBeNull(); - - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) - return true; - } - - return false; - } - - /// - /// Checks if the given is equal to the specified or if it implements it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type otherType) => - // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); - - /// - /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified - /// to compare the types. - /// - /// , - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// The equality comparer used to compare the interface types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type otherType, - [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => - // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); - - /// - /// Checks if the specified type derives from the other type. Internally, this method uses - /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type info to be checked. - /// The base class that should derive from. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass) - // ReSharper restore RedundantNullableFlowAttribute - { - baseClass.MustNotBeNull(nameof(baseClass)); - - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) - { - if (currentBaseType.IsEquivalentTypeTo(baseClass)) - return true; - - currentBaseType = currentBaseType.BaseType; - } - - return false; - } - - /// - /// Checks if the specified type derives from the other type. This overload uses the specified - /// to compare the types. - /// - /// The type info to be checked. - /// The base class that should derive from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) - // ReSharper restore RedundantNullableFlowAttribute - { - baseClass.MustNotBeNull(nameof(baseClass)); - typeComparer.MustNotBeNull(nameof(typeComparer)); - - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) - { - if (typeComparer.Equals(currentBaseType, baseClass)) - return true; - - currentBaseType = currentBaseType.BaseType; - } - - return false; - } - - /// - /// Checks if the given is equal to the specified or if it derives from it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => - // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); - - /// - /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => - // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); - - - /// - /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType) => - // ReSharper restore RedundantNullableFlowAttribute - baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)) - .IsInterface ? - type.Implements(baseClassOrInterfaceType) : - type.DerivesFrom(baseClassOrInterfaceType); - - /// - /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType, - [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => - // ReSharper restore RedundantNullableFlowAttribute - baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)) - .IsInterface ? - type.Implements(baseClassOrInterfaceType, typeComparer) : - type.DerivesFrom(baseClassOrInterfaceType, typeComparer); - - /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions - /// are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type otherType) => - // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); - - - /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// This overload uses the specified to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, - [NotNull, ValidatedNotNull] Type otherType, - [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => - // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); - - - /// - /// Checks if the given is a generic type that has open generic parameters, - /// but is no generic type definition. - /// - /// The type to be checked. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOpenConstructedGenericType([NotNull, ValidatedNotNull] this Type type) => - type.MustNotBeNull(nameof(type)).IsGenericType && - type.ContainsGenericParameters && - type.IsGenericTypeDefinition == false; -} \ No newline at end of file From 47efaf2fe6a7ace39279655346a14283efe42c68 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 17:47:11 +0100 Subject: [PATCH 026/123] refactor: move assertions from Check.DateTimeAssertions.cs into their own files Signed-off-by: Kenny Pflug --- .../Check.DateTimeAssertions.cs | 99 ------------------- Code/Light.GuardClauses/Check.MustBeLocal.cs | 49 +++++++++ .../Check.MustBeUnspecified.cs | 49 +++++++++ Code/Light.GuardClauses/Check.MustBeUtc.cs | 49 +++++++++ 4 files changed, 147 insertions(+), 99 deletions(-) delete mode 100644 Code/Light.GuardClauses/Check.DateTimeAssertions.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeLocal.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeUnspecified.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeUtc.cs diff --git a/Code/Light.GuardClauses/Check.DateTimeAssertions.cs b/Code/Light.GuardClauses/Check.DateTimeAssertions.cs deleted file mode 100644 index feee6b2..0000000 --- a/Code/Light.GuardClauses/Check.DateTimeAssertions.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using JetBrains.Annotations; -using System.Runtime.CompilerServices; -using Light.GuardClauses.Exceptions; - -namespace Light.GuardClauses; - -public static partial class Check -{ - /// - /// Ensures that the specified uses , or otherwise throws an . - /// - /// The date time to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Kind != DateTimeKind.Utc) - Throw.MustBeUtcDateTime(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified uses , or otherwise throws your custom exception. - /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) - { - if (parameter.Kind != DateTimeKind.Utc) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the specified uses , or otherwise throws an . - /// - /// The date time to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Kind != DateTimeKind.Local) - Throw.MustBeLocalDateTime(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified uses , or otherwise throws your custom exception. - /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) - { - if (parameter.Kind != DateTimeKind.Local) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the specified uses , or otherwise throws an . - /// - /// The date time to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified uses , or otherwise throws your custom exception. - /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) - { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } -} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.MustBeLocal.cs b/Code/Light.GuardClauses/Check.MustBeLocal.cs new file mode 100644 index 0000000..5a41690 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLocal.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified uses , or otherwise throws an . + /// + /// The date time to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime MustBeLocal( + this DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Kind != DateTimeKind.Local) + { + Throw.MustBeLocalDateTime(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified uses , or otherwise throws your custom exception. + /// + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) + { + if (parameter.Kind != DateTimeKind.Local) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeUnspecified.cs b/Code/Light.GuardClauses/Check.MustBeUnspecified.cs new file mode 100644 index 0000000..97810eb --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeUnspecified.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified uses , or otherwise throws an . + /// + /// The date time to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime MustBeUnspecified( + this DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Kind != DateTimeKind.Unspecified) + { + Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified uses , or otherwise throws your custom exception. + /// + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + { + if (parameter.Kind != DateTimeKind.Unspecified) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeUtc.cs b/Code/Light.GuardClauses/Check.MustBeUtc.cs new file mode 100644 index 0000000..efa2369 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeUtc.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified uses , or otherwise throws an . + /// + /// The date time to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime MustBeUtc( + this DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Kind != DateTimeKind.Utc) + { + Throw.MustBeUtcDateTime(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified uses , or otherwise throws your custom exception. + /// + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) + { + if (parameter.Kind != DateTimeKind.Utc) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} From ff19ecdfd1881ee888b1d5725aa4466b6a89a8fb Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:02:49 +0100 Subject: [PATCH 027/123] refactor: move MustHaveCount assertions from Check.EnumerableAssertions.cs to Check.MustHaveCount.cs --- .../Check.EnumerableAssertions.cs | 34 ----------- .../Light.GuardClauses/Check.MustHaveCount.cs | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveCount.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index a67209b..4e1e731 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,40 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection has the specified number of items, or otherwise throws an . - /// - /// The collection to be checked. - /// The number of items the collection must have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified number of items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter!.Count(parameterName, message) != count) - Throw.InvalidCollectionCount(parameter, count, parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified number of items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() != count) - Throw.CustomException(exceptionFactory, parameter, count); - return parameter; - } - /// /// Checks if the specified collection is null or empty. /// diff --git a/Code/Light.GuardClauses/Check.MustHaveCount.cs b/Code/Light.GuardClauses/Check.MustHaveCount.cs new file mode 100644 index 0000000..3c88fad --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveCount.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection has the specified number of items, or otherwise throws an . + /// + /// The collection to be checked. + /// The number of items the collection must have. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not have the specified number of items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter!.Count(parameterName, message) != count) + { + Throw.InvalidCollectionCount(parameter, count, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The number of items the collection must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified number of items, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is null || parameter.Count() != count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + + return parameter; + } +} From 329c0c49d903a5484cee2146f67d96aad5928d31 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:06:17 +0100 Subject: [PATCH 028/123] refactor: move IsNullOrEmpty method from Check.EnumerableAssertions.cs to Check.IsNullOrEmpty.cs --- .../Check.EnumerableAssertions.cs | 10 ---------- .../Light.GuardClauses/Check.IsNullOrEmpty.cs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsNullOrEmpty.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 4e1e731..3db1edd 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,16 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified collection is null or empty. - /// - /// The collection to be checked. - /// True if the collection is null or empty, else false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => - collection is null || collection.Count() == 0; - /// /// Ensures that the collection is not null or empty, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs b/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs new file mode 100644 index 0000000..f2fd8de --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified collection is null or empty. + /// + /// The collection to be checked. + /// True if the collection is null or empty, else false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => + collection is null || collection.Count() == 0; +} From 21ec6fbb13ca2ce2332e2c08bb08fbed8e98bcb3 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:08:51 +0100 Subject: [PATCH 029/123] refactor: move MustNotBeNullOrEmpty assertions from Check.EnumerableAssertions.cs to Check.MustNotBeNullOrEmpty.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 32 ----------- .../Check.MustNotBeNullOrEmpty.cs | 57 +++++++++++++++++++ 2 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 3db1edd..9878d0a 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,38 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection is not null or empty, or otherwise throws an . - /// - /// The collection to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter.Count(parameterName, message) == 0) - Throw.EmptyCollection(parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() == 0) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Ensures that the collection contains the specified item, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs new file mode 100644 index 0000000..0ac3004 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection is not null or empty, or otherwise throws an . + /// + /// The collection to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has no items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter.Count(parameterName, message) == 0) + { + Throw.EmptyCollection(parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no items, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is null || parameter.Count() == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} From 795a8c436fef5741ac5c0dfe3989ce30b424b634 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:12:21 +0100 Subject: [PATCH 030/123] refactor: move MustContain assertions from Check.EnumerableAssertions.cs to Check.MustContain.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 48 ----------- Code/Light.GuardClauses/Check.MustContain.cs | 81 +++++++++++++++++++ 2 files changed, 81 insertions(+), 48 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustContain.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 9878d0a..8cdd3ab 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,54 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection contains the specified item, or otherwise throws a . - /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - Throw.MissingItem(parameter, item, parameterName, message); - return parameter; - } - - if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.MissingItem(parameter, item, parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - - if (parameter is null || !parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - /// /// Ensures that the collection does not contain the specified item, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustContain.cs b/Code/Light.GuardClauses/Check.MustContain.cs new file mode 100644 index 0000000..2161972 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustContain.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection contains the specified item, or otherwise throws a . + /// + /// The collection to be checked. + /// The item that must be part of the collection. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The item that must be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + TItem item, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } + + if (parameter is null || !parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } +} From 8beaeb27dd536d9b8d33750a55dfce4f904c36dd Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:14:29 +0100 Subject: [PATCH 031/123] refactor: move MustNotContain assertions from Check.EnumerableAssertions.cs to Check.MustNotContain.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 48 ----------- .../Check.MustNotContain.cs | 81 +++++++++++++++++++ 2 files changed, 81 insertions(+), 48 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotContain.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 8cdd3ab..8d696a8 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,54 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection does not contain the specified item, or otherwise throws an . - /// - /// The collection to be checked. - /// The item that must not be part of the collection. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - Throw.ExistingItem(parameter, item, parameterName, message); - return parameter; - } - - if (parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.ExistingItem(parameter, item, parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The item that must not be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - - if (parameter is null || parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; - } - /// /// Checks if the given is one of the specified . /// diff --git a/Code/Light.GuardClauses/Check.MustNotContain.cs b/Code/Light.GuardClauses/Check.MustNotContain.cs new file mode 100644 index 0000000..c622120 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotContain.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection does not contain the specified item, or otherwise throws an . + /// + /// The collection to be checked. + /// The item that must not be part of the collection. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter is ICollection collection) + { + if (collection.Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + if (parameter.MustNotBeNull(parameterName, message).Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The item that must not be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + TItem item, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is ICollection collection) + { + if (collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } + + if (parameter is null || parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } +} From 54c627959ec658acd872c57dbac5c7609fa42c9e Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:16:41 +0100 Subject: [PATCH 032/123] refactor: move IsOneOf method from Check.EnumerableAssertions.cs to Check.IsOneOf.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 20 ----------- Code/Light.GuardClauses/Check.IsOneOf.cs | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsOneOf.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 8d696a8..ab8597d 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,26 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the given is one of the specified . - /// - /// The item to be checked. - /// The collection that might contain the . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static bool IsOneOf(this TItem item, [NotNull, ValidatedNotNull] IEnumerable items) - { - if (items is ICollection collection) - return collection.Contains(item); - - if (items is string @string && item is char character) - return @string.IndexOf(character) != -1; - - return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); - } - /// /// Ensures that the value is one of the specified items, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.IsOneOf.cs b/Code/Light.GuardClauses/Check.IsOneOf.cs new file mode 100644 index 0000000..25b0063 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsOneOf.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the given is one of the specified . + /// + /// The item to be checked. + /// The collection that might contain the . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static bool IsOneOf(this TItem item, [NotNull] [ValidatedNotNull] IEnumerable items) + { + if (items is ICollection collection) + { + return collection.Contains(item); + } + + if (items is string @string && item is char character) + { + return @string.IndexOf(character) != -1; + } + + return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); + } +} From 26a101efd0da2a6f72bb776edb81c70b297fb9a4 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:19:47 +0100 Subject: [PATCH 033/123] refactor: move MustBeOneOf assertions from Check.EnumerableAssertions.cs to Check.MustBeOneOf.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 38 ----------- Code/Light.GuardClauses/Check.MustBeOneOf.cs | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeOneOf.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index ab8597d..3b28b5c 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,44 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the value is one of the specified items, or otherwise throws a . - /// - /// The value to be checked. - /// The items that should contain the value. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to one of the specified . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - // ReSharper disable PossibleMultipleEnumeration - if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueNotOneOf(parameter, items, parameterName, message); - return parameter; - // ReSharper restore PossibleMultipleEnumeration - } - - /// - /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. - /// - /// The value to be checked. - /// The items that should contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to one of the specified , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || !parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); - return parameter; - } - /// /// Ensures that the value is not one of the specified items, or otherwise throws a . /// diff --git a/Code/Light.GuardClauses/Check.MustBeOneOf.cs b/Code/Light.GuardClauses/Check.MustBeOneOf.cs new file mode 100644 index 0000000..6d70417 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeOneOf.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the value is one of the specified items, or otherwise throws a . + /// + /// The value to be checked. + /// The items that should contain the value. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to one of the specified . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf( + this TItem parameter, + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + [NotNull] [ValidatedNotNull] IEnumerable items, + [CallerArgumentExpression("parameter")] + string? parameterName = null, + string? message = null + ) + { + // ReSharper disable PossibleMultipleEnumeration + if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + { + Throw.ValueNotOneOf(parameter, items, parameterName, message); + } + + return parameter; + // ReSharper restore PossibleMultipleEnumeration + } + + /// + /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The items that should contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to one of the specified , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf( + this TItem parameter, + [NotNull] [ValidatedNotNull] TCollection items, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || !parameter.IsOneOf(items)) + { + Throw.CustomException(exceptionFactory, parameter, items!); + } + + return parameter; + } +} From dec6d48cc7edf3ade47542a0382e24606776a908 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:28:54 +0100 Subject: [PATCH 034/123] refactor: move MustNotBeOneOf assertions from Check.EnumerableAssertions.cs to Check.MustNotBeOneOf.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 38 ----------- .../Check.MustNotBeOneOf.cs | 64 +++++++++++++++++++ 2 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeOneOf.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 3b28b5c..a80204f 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,44 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the value is not one of the specified items, or otherwise throws a . - /// - /// The value to be checked. - /// The items that must not contain the value. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to one of the specified . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - // ReSharper disable PossibleMultipleEnumeration - if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueIsOneOf(parameter, items, parameterName, message); - return parameter; - // ReSharper restore PossibleMultipleEnumeration - } - - /// - /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. - /// - /// The value to be checked. - /// The items that must not contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is equal to one of the specified , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); - return parameter; - } - /// /// Ensures that the collection has at least the specified number of items, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs b/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs new file mode 100644 index 0000000..f4001b0 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// + /// The value to be checked. + /// The items that must not contain the value. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is equal to one of the specified . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustNotBeOneOf( + this TItem parameter, + [NotNull] [ValidatedNotNull] IEnumerable items, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + // ReSharper disable PossibleMultipleEnumeration + if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + { + Throw.ValueIsOneOf(parameter, items, parameterName, message); + } + + return parameter; + // ReSharper restore PossibleMultipleEnumeration + } + + /// + /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The items that must not contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is equal to one of the specified , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + public static TItem MustNotBeOneOf( + this TItem parameter, + [NotNull] [ValidatedNotNull] TCollection items, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || parameter.IsOneOf(items)) + { + Throw.CustomException(exceptionFactory, parameter, items!); + } + + return parameter; + } +} From d0006fd4bbb25e0eb696de2a652c645dad5fde4e Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:31:42 +0100 Subject: [PATCH 035/123] refactor: move MustHaveMinimumCount assertions from Check.EnumerableAssertions.cs to Check.MustHaveMinimumCount.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 34 ----------- .../Check.MustHaveMinimumCount.cs | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index a80204f..9e371d4 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,40 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws an . - /// - /// The collection to be checked. - /// The number of items the collection should have at least. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at least the specified number of items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter.Count(parameterName, message) < count) - Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection should have at least. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() < count) - Throw.CustomException(exceptionFactory, parameter, count); - return parameter; - } - /// /// Ensures that the collection has at most the specified number of items, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs b/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs new file mode 100644 index 0000000..c858def --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection has at least the specified number of items, or otherwise throws an . + /// + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain at least the specified number of items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMinimumCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter.Count(parameterName, message) < count) + { + Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMinimumCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is null || parameter.Count() < count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + + return parameter; + } +} From 3fb5c3f3a82273116f08e1693ca29fbcacb6c13f Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:37:29 +0100 Subject: [PATCH 036/123] refactor: move MustHaveMaximumCount assertions from Check.EnumerableAssertions.cs to Check.MustHaveMaximumCount.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 34 ----------- .../Check.MustHaveMaximumCount.cs | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 9e371d4..a764007 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,40 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws an . - /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at most the specified number of items. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable - { - if (parameter.Count(parameterName, message) > count) - Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); - return parameter; - } - - /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() > count) - Throw.CustomException(exceptionFactory, parameter, count); - return parameter; - } - /// /// Ensures that the span has the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs b/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs new file mode 100644 index 0000000..b141e55 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the collection has at most the specified number of items, or otherwise throws an . + /// + /// The collection to be checked. + /// The number of items the collection should have at most. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain at most the specified number of items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where TCollection : class, IEnumerable + { + if (parameter.Count(parameterName, message) > count) + { + Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. + /// + /// The collection to be checked. + /// The number of items the collection should have at most. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount( + [NotNull] [ValidatedNotNull] this TCollection? parameter, + int count, + Func exceptionFactory + ) where TCollection : class, IEnumerable + { + if (parameter is null || parameter.Count() > count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + + return parameter; + } +} From d73646e16fe7507a73ef7d8ee7e1fafe0ee017f0 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 26 Dec 2024 20:41:17 +0100 Subject: [PATCH 037/123] refactor: move MustHaveLength assertions from Check.EnumerableAssertions.cs to Check.MustHaveLength.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 62 ----------- .../Check.MustHaveLength.cs | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveLength.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index a764007..1b9991b 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,68 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the span has the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The length that the span must have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length != length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length that the span must have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Your custom exception thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length != length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the span is longer than the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustHaveLength.cs b/Code/Light.GuardClauses/Check.MustHaveLength.cs new file mode 100644 index 0000000..fdae65b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveLength.cs @@ -0,0 +1,100 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the span has the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The length that the span must have. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not have the specified length. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustHaveLength( + this Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length != length) + { + Throw.InvalidSpanLength(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustHaveLength( + this Span parameter, + int length, + SpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length != length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length that the span must have. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Your custom exception thrown when does not have the specified length. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustHaveLength( + this ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length != length) + { + Throw.InvalidSpanLength(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustHaveLength( + this ReadOnlySpan parameter, + int length, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length != length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } +} From 6458bb47dea3abf78c3936e114b0e5abd82deaeb Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Fri, 27 Dec 2024 11:34:57 +0100 Subject: [PATCH 038/123] refactor: move MustBeLongerThan assertions from Check.EnumerableAssertions.cs to Check.MustBeLongerThan.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 62 ----------- .../Check.MustBeLongerThan.cs | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeLongerThan.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 1b9991b..20ddf5d 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,68 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the span is longer than the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length <= length) - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length value that the span must be longer than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length <= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span is longer than the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length <= length) - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length value that the span must be longer than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length <= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs new file mode 100644 index 0000000..7c139e1 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs @@ -0,0 +1,100 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the span is longer than the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The value that the span must be longer than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeLongerThan( + this Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length <= length) + { + Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be longer than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeLongerThan( + this Span parameter, + int length, + SpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length <= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The value that the span must be longer than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeLongerThan( + this ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length <= length) + { + Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be longer than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeLongerThan( + this ReadOnlySpan parameter, + int length, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length <= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } +} From 65fed4dd541b8339271ddde4ec85961107b269cd Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Fri, 27 Dec 2024 11:37:47 +0100 Subject: [PATCH 039/123] refactor: move MustBeLongerThanOrEqualTo assertions from Check.EnumerableAssertions.cs to Check.MustBeLongerThanOrEqualTo.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 62 ----------- .../Check.MustBeLongerThanOrEqualTo.cs | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 20ddf5d..b7e89d1 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,68 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs new file mode 100644 index 0000000..87280b1 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs @@ -0,0 +1,100 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeLongerThanOrEqualTo( + this Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length < length) + { + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeLongerThanOrEqualTo( + this Span parameter, + int length, + SpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length < length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeLongerThanOrEqualTo( + this ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length < length) + { + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeLongerThanOrEqualTo( + this ReadOnlySpan parameter, + int length, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length < length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } +} From c484cccf1dab4dbb2afb35e92c11a20f582244de Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Fri, 27 Dec 2024 11:40:38 +0100 Subject: [PATCH 040/123] refactor: move MustBeShorterThan assertions from Check.EnumerableAssertions.cs to Check.MustBeShorterThan.cs Signed-off-by: Kenny Pflug --- .../Check.EnumerableAssertions.cs | 62 ----------- .../Check.MustBeShorterThan.cs | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeShorterThan.cs diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index b7e89d1..bdb3b67 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -13,68 +13,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . - /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. - /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs new file mode 100644 index 0000000..8488e0e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs @@ -0,0 +1,100 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is longer than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeShorterThan( + this Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length >= length) + { + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeShorterThan( + this Span parameter, + int length, + SpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length >= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is longer than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeShorterThan( + this ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Length >= length) + { + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeShorterThan( + this ReadOnlySpan parameter, + int length, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (parameter.Length >= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } +} From 5e4896ff6d8539baab14f61448a7ea988414a537 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:05:00 +0100 Subject: [PATCH 041/123] refactor: rename Check.EnumerableAssertions.cs to Check.MustBeShorterThanOrEqualTo.cs Signed-off-by: Kenny Pflug --- ...cs => Check.MustBeShorterThanOrEqualTo.cs} | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) rename Code/Light.GuardClauses/{Check.EnumerableAssertions.cs => Check.MustBeShorterThanOrEqualTo.cs} (68%) diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs similarity index 68% rename from Code/Light.GuardClauses/Check.EnumerableAssertions.cs rename to Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs index bdb3b67..2be3c5a 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs @@ -1,31 +1,32 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; +using System; using System.Runtime.CompilerServices; -using JetBrains.Annotations; using Light.GuardClauses.Exceptions; -using Light.GuardClauses.FrameworkExtensions; -using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// /// The span to be checked. /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeShorterThanOrEqualTo( + this Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) { if (parameter.Length > length) + { Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + } + return parameter; } @@ -34,29 +35,44 @@ public static Span MustBeShorterThanOrEqualTo(this Span parameter, int /// /// The span to be checked. /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static Span MustBeShorterThanOrEqualTo( + this Span parameter, + int length, + SpanExceptionFactory exceptionFactory + ) { if (parameter.Length > length) + { Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// /// The span to be checked. /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeShorterThanOrEqualTo( + this ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) { if (parameter.Length > length) + { Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + } + return parameter; } @@ -65,13 +81,20 @@ public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan /// /// The span to be checked. /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + public static ReadOnlySpan MustBeShorterThanOrEqualTo( + this ReadOnlySpan parameter, + int length, + ReadOnlySpanExceptionFactory exceptionFactory + ) { if (parameter.Length > length) + { Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } -} \ No newline at end of file +} From 7dcdf690350bf134363a4417fa3b0a30d917b0fa Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:21:24 +0100 Subject: [PATCH 042/123] chore: update DotSettings files to include new user dictionary entry --- Code/Light.GuardClauses.AllProjects.sln.DotSettings | 3 ++- Code/Light.GuardClauses.sln.DotSettings | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Code/Light.GuardClauses.AllProjects.sln.DotSettings b/Code/Light.GuardClauses.AllProjects.sln.DotSettings index be20ec1..aceafc4 100644 --- a/Code/Light.GuardClauses.AllProjects.sln.DotSettings +++ b/Code/Light.GuardClauses.AllProjects.sln.DotSettings @@ -317,4 +317,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/Code/Light.GuardClauses.sln.DotSettings b/Code/Light.GuardClauses.sln.DotSettings index be20ec1..aceafc4 100644 --- a/Code/Light.GuardClauses.sln.DotSettings +++ b/Code/Light.GuardClauses.sln.DotSettings @@ -317,4 +317,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file From 90fed4aad4a7266d90ce95441ec824619111f4d5 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:22:42 +0100 Subject: [PATCH 043/123] refactor: move IsNullOrEmpty string check from Check.StringAssertions.cs to Check.IsNullOrEmpty.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsNullOrEmpty.cs | 8 ++++++++ Code/Light.GuardClauses/Check.StringAssertions.cs | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs b/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs index f2fd8de..d858001 100644 --- a/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs +++ b/Code/Light.GuardClauses/Check.IsNullOrEmpty.cs @@ -17,4 +17,12 @@ public static partial class Check [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; + + /// + /// Checks if the specified string is null or empty. + /// + /// The string to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index e8fff60..cb414d2 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,14 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is null or empty. - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); - /// /// Ensures that the specified string is not null or empty, or otherwise throws an or . /// From 5a350bdc048ae932bad9229347885501f3a67e18 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:27:22 +0100 Subject: [PATCH 044/123] refactor: move MustNotBeNullOrEmpty string checks from Check.StringAssertions.cs to Check.MustNotBeNullOrEmpty.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotBeNullOrEmpty.cs | 50 +++++++++++++++++++ .../Check.StringAssertions.cs | 36 ------------- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs index 0ac3004..3ff1488 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs @@ -54,4 +54,54 @@ public static TCollection MustNotBeNullOrEmpty( return parameter; } + + /// + /// Ensures that the specified string is not null or empty, or otherwise throws an or . + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is an empty string. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotBeNullOrEmpty( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + if (parameter.Length == 0) + { + Throw.EmptyString(parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is an empty string or null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static string MustNotBeNullOrEmpty( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (parameter.IsNullOrEmpty()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index cb414d2..ac1370c 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,42 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the specified string is not null or empty, or otherwise throws an or . - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty string. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - if (parameter.Length == 0) - Throw.EmptyString(parameterName, message); - - return parameter; - } - - /// - /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an empty string or null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (parameter.IsNullOrEmpty()) - Throw.CustomException(exceptionFactory, parameter); - - return parameter; - } - /// /// Checks if the specified string is null, empty, or contains only white space. /// From e1991963dd9be9ccfc1a47012863e7bbaa6f5490 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:29:18 +0100 Subject: [PATCH 045/123] refactor: move IsNullOrWhiteSpace check from Check.StringAssertions.cs to Check.IsNullOrWhiteSpace.cs Signed-off-by: Kenny Pflug --- .../Check.IsNullOrWhiteSpace.cs | 17 +++++++++++++++++ .../Check.StringAssertions.cs | 8 -------- 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsNullOrWhiteSpace.cs diff --git a/Code/Light.GuardClauses/Check.IsNullOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.IsNullOrWhiteSpace.cs new file mode 100644 index 0000000..c67f171 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsNullOrWhiteSpace.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string is null, empty, or contains only white space. + /// + /// The string to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => + string.IsNullOrWhiteSpace(@string); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index ac1370c..0a1e4a1 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,14 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is null, empty, or contains only white space. - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); - /// /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . /// From 70a2d82d10e9ef299653315529fd8c3f9d1c276a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:40:36 +0100 Subject: [PATCH 046/123] refactor: move MustNotBeNullOrWhiteSpace checks from Check.StringAssertions.cs to Check.MustNotBeNullOrWhiteSpace.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotBeNullOrWhiteSpace.cs | 62 +++++++++++++++++++ .../Check.StringAssertions.cs | 40 ------------ 2 files changed, 62 insertions(+), 40 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs new file mode 100644 index 0000000..e93d21b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains only white space. + /// Thrown when is an empty string. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotBeNullOrWhiteSpace( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + parameter.MustNotBeNullOrEmpty(parameterName, message); + + foreach (var character in parameter) + { + if (!character.IsWhiteSpace()) + { + return parameter; + } + } + + Throw.WhiteSpaceString(parameter, parameterName, message); + return null; + } + + /// + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null, empty, or contains only white space. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] + public static string MustNotBeNullOrWhiteSpace( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (parameter.IsNullOrWhiteSpace()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 0a1e4a1..9d71e5a 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,46 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains only white space. - /// Thrown when is an empty string. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - parameter.MustNotBeNullOrEmpty(parameterName, message); - - foreach (var character in parameter) - { - if (!character.IsWhiteSpace()) - return parameter; - } - - Throw.WhiteSpaceString(parameter, parameterName, message); - return null; - } - - /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null, empty, or contains only white space. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] - public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (parameter.IsNullOrWhiteSpace()) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Checks if the specified character is a white space character. /// From 05423fb4bdadf3d9d0be8335c0a4b1e010cf5406 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:42:58 +0100 Subject: [PATCH 047/123] refactor: move IsWhiteSpace check from Check.StringAssertions.cs to Check.IsWhiteSpace.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsWhiteSpace.cs | 12 ++++++++++++ Code/Light.GuardClauses/Check.StringAssertions.cs | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsWhiteSpace.cs diff --git a/Code/Light.GuardClauses/Check.IsWhiteSpace.cs b/Code/Light.GuardClauses/Check.IsWhiteSpace.cs new file mode 100644 index 0000000..4a18250 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsWhiteSpace.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified character is a white space character. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 9d71e5a..f38883d 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,12 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified character is a white space character. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); - /// /// Checks if the specified character is a letter. /// From 81229502d9573f18fd9dc57bcf29676a936d21fc Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 08:45:20 +0100 Subject: [PATCH 048/123] refactor: move IsLetter check from Check.StringAssertions.cs to Check.IsLetter.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsLetter.cs | 12 ++++++++++++ Code/Light.GuardClauses/Check.StringAssertions.cs | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsLetter.cs diff --git a/Code/Light.GuardClauses/Check.IsLetter.cs b/Code/Light.GuardClauses/Check.IsLetter.cs new file mode 100644 index 0000000..2639fc9 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsLetter.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified character is a letter. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLetter(this char character) => char.IsLetter(character); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index f38883d..8b6891d 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,12 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified character is a letter. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetter(this char character) => char.IsLetter(character); - /// /// Checks if the specified character is a letter or digit. /// From 3f25229bfcc33d86e50fd0e0f18b1f912bdc14de Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:26:56 +0100 Subject: [PATCH 049/123] refactor: move IsLetterOrDigit check from Check.StringAssertions.cs to Check.IsLetterOrDigit.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsLetterOrDigit.cs | 12 ++++++++++++ Code/Light.GuardClauses/Check.StringAssertions.cs | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsLetterOrDigit.cs diff --git a/Code/Light.GuardClauses/Check.IsLetterOrDigit.cs b/Code/Light.GuardClauses/Check.IsLetterOrDigit.cs new file mode 100644 index 0000000..1acd030 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsLetterOrDigit.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified character is a letter or digit. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 8b6891d..1b14f6b 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,12 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified character is a letter or digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); - /// /// Checks if the specified character is a digit. /// From 008edac4b716d009fd699e257c343406abfbbb7a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:28:06 +0100 Subject: [PATCH 050/123] refactor: move IsDigit check from Check.StringAssertions.cs to Check.IsDigit.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsDigit.cs | 12 ++++++++++++ Code/Light.GuardClauses/Check.StringAssertions.cs | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsDigit.cs diff --git a/Code/Light.GuardClauses/Check.IsDigit.cs b/Code/Light.GuardClauses/Check.IsDigit.cs new file mode 100644 index 0000000..fb7756a --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsDigit.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified character is a digit. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDigit(this char character) => char.IsDigit(character); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 1b14f6b..45550a3 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,12 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified character is a digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsDigit(this char character) => char.IsDigit(character); - /// /// Ensures that the two strings are equal using the specified , or otherwise throws a . /// From 79cb3429033df8b77ab5ea687b55c599e0372ebb Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:33:19 +0100 Subject: [PATCH 051/123] refactor: move MustBe string comparison methods from Check.StringAssertions.cs to Check.MustBe.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.MustBe.cs | 103 ++++++++++++++++++ .../Check.StringAssertions.cs | 69 ------------ 2 files changed, 103 insertions(+), 69 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBe.cs b/Code/Light.GuardClauses/Check.MustBe.cs index 9bdff30..722e499 100644 --- a/Code/Light.GuardClauses/Check.MustBe.cs +++ b/Code/Light.GuardClauses/Check.MustBe.cs @@ -103,4 +103,107 @@ Func, Exception> exceptionFactory return parameter; } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe( + this string? parameter, + string? other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!string.Equals(parameter, other, comparisonType)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe( + this string? parameter, + string? other, + StringComparison comparisonType, + Func exceptionFactory + ) + { + if (!string.Equals(parameter, other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe( + this string? parameter, + string? other, + StringComparisonType comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.Equals(other, comparisonType)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + public static string? MustBe( + this string? parameter, + string? other, + StringComparisonType comparisonType, + Func exceptionFactory + ) + { + if (!parameter.Equals(other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 45550a3..9cd4244 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,75 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!string.Equals(parameter, other, comparisonType)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) - { - if (!string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); - return parameter; - } - - /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.Equals(other, comparisonType)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) - { - if (!parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); - return parameter; - } - /// /// Ensures that the two strings are not equal using the specified , or otherwise throws a . /// From 7a91a3edc709ad47c95afc81a725119709bb63aa Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:36:30 +0100 Subject: [PATCH 052/123] refactor: move MustNotBe string comparison methods from Check.StringAssertions.cs to Check.MustNotBe.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.MustNotBe.cs | 104 ++++++++++++++++++ .../Check.StringAssertions.cs | 70 ------------ 2 files changed, 104 insertions(+), 70 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustNotBe.cs b/Code/Light.GuardClauses/Check.MustNotBe.cs index 360aad3..93b4ced 100644 --- a/Code/Light.GuardClauses/Check.MustNotBe.cs +++ b/Code/Light.GuardClauses/Check.MustNotBe.cs @@ -99,4 +99,108 @@ Func, Exception> exceptionFactory } return parameter; } + + /// + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustNotBe( + this string? parameter, + string? other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (string.Equals(parameter, other, comparisonType)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustNotBe( + this string? parameter, + string? other, + StringComparison comparisonType, + Func exceptionFactory + ) + { + if (string.Equals(parameter, other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustNotBe( + this string? parameter, + string? other, + StringComparisonType comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.Equals(other, comparisonType)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustNotBe( + this string? parameter, + string? other, + StringComparisonType comparisonType, + Func exceptionFactory + ) + { + if (parameter.Equals(other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 9cd4244..021d73e 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,76 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (string.Equals(parameter, other, comparisonType)) - Throw.ValuesEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) - { - if (string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); - return parameter; - } - - /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Equals(other, comparisonType)) - Throw.ValuesEqual(parameter, other, parameterName, message); - return parameter; - } - - /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. - /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) - { - if (parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); - return parameter; - } - /// /// Ensures that the string matches the specified regular expression, or otherwise throws a . /// From 9c141394efec01bc04378f11889576379e671505 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:40:22 +0100 Subject: [PATCH 053/123] refactor: move MustMatch string validation methods from Check.StringAssertions.cs to Check.MustMatch.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.MustMatch.cs | 64 +++++++++++++++++++ .../Check.StringAssertions.cs | 38 ----------- 2 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustMatch.cs diff --git a/Code/Light.GuardClauses/Check.MustMatch.cs b/Code/Light.GuardClauses/Check.MustMatch.cs new file mode 100644 index 0000000..591e357 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustMatch.cs @@ -0,0 +1,64 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string matches the specified regular expression, or otherwise throws a . + /// + /// The string to be checked. + /// The regular expression used for pattern matching. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not match the specified regular expression. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] + public static string MustMatch( + [NotNull] [ValidatedNotNull] this string? parameter, + Regex regex, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) + { + Throw.StringDoesNotMatch(parameter, regex, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The regular expression used for pattern matching. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not match the specified regular expression, + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string MustMatch( + [NotNull] [ValidatedNotNull] this string? parameter, + Regex regex, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || regex is null || !regex.IsMatch(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, regex!); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 021d73e..2012216 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -11,44 +11,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string matches the specified regular expression, or otherwise throws a . - /// - /// The string to be checked. - /// The regular expression used for pattern matching. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not match the specified regular expression. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] - public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) - Throw.StringDoesNotMatch(parameter, regex, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The regular expression used for pattern matching. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not match the specified regular expression, - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || regex is null || !regex.IsMatch(parameter)) - Throw.CustomException(exceptionFactory, parameter, regex!); - return parameter; - } - /// /// Checks if the specified strings are equal, using the given comparison rules. /// From 830ad72a165d25556a1528257789e49ad5b8573a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:45:34 +0100 Subject: [PATCH 054/123] refactor: move Equals string comparison method from Check.StringAssertions.cs to Check.Equals.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.Equals.cs | 37 +++++++++++++++++++ .../Check.StringAssertions.cs | 23 ------------ 2 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.Equals.cs diff --git a/Code/Light.GuardClauses/Check.Equals.cs b/Code/Light.GuardClauses/Check.Equals.cs new file mode 100644 index 0000000..688e191 --- /dev/null +++ b/Code/Light.GuardClauses/Check.Equals.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified strings are equal, using the given comparison rules. + /// + /// The first string to compare. + /// The second string to compare. + /// One of the enumeration values that specifies the rules for the comparison. + /// True if the two strings are considered equal, else false. + /// Thrown when is no valid enum value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) + { + if ((int) comparisonType < 6) + { + return string.Equals(@string, value, (StringComparison) comparisonType); + } + + switch (comparisonType) + { + case StringComparisonType.OrdinalIgnoreWhiteSpace: + return @string.EqualsOrdinalIgnoreWhiteSpace(value); + case StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace: + return @string.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(value); + default: + Throw.EnumValueNotDefined(comparisonType, nameof(comparisonType)); + return false; + } + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 2012216..4575696 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -3,7 +3,6 @@ using System.Text.RegularExpressions; using JetBrains.Annotations; using Light.GuardClauses.Exceptions; -using Light.GuardClauses.FrameworkExtensions; using System.Runtime.CompilerServices; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; @@ -11,28 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified strings are equal, using the given comparison rules. - /// - /// The first string to compare. - /// The second string to compare. - /// One of the enumeration values that specifies the rules for the comparison. - /// True if the two strings are considered equal, else false. - /// Thrown when is no valid enum value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) - { - if ((int) comparisonType < 6) - return string.Equals(@string, value, (StringComparison) comparisonType); - if (comparisonType == StringComparisonType.OrdinalIgnoreWhiteSpace) - return @string.EqualsOrdinalIgnoreWhiteSpace(value); - if (comparisonType == StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace) - return @string.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(value); - - Throw.EnumValueNotDefined(comparisonType, nameof(comparisonType)); - return false; - } - /// /// Ensures that the string contains the specified substring, or otherwise throws a . /// From b29102d0a4966ff4ac9235415fe86c5a515138e8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:50:29 +0100 Subject: [PATCH 055/123] refactor: move MustContain string validation methods from Check.StringAssertions.cs to Check.MustContain.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.MustContain.cs | 118 ++++++++++++++++++ .../Check.StringAssertions.cs | 90 ------------- 2 files changed, 118 insertions(+), 90 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustContain.cs b/Code/Light.GuardClauses/Check.MustContain.cs index 2161972..24b7175 100644 --- a/Code/Light.GuardClauses/Check.MustContain.cs +++ b/Code/Light.GuardClauses/Check.MustContain.cs @@ -78,4 +78,122 @@ public static TCollection MustContain( return parameter; } + + /// + /// Ensures that the string contains the specified substring, or otherwise throws a . + /// + /// The string to be checked. + /// The substring that must be part of . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string? value, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + { + Throw.StringDoesNotContain(parameter, value, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The substring that must be part of . + /// The delegate that creates you custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !parameter.Contains(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string contains the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).IndexOf( + value.MustNotBeNull(nameof(value), message), + comparisonType + ) < + 0) + { + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates you custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 4575696..506335a 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,96 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string contains the specified substring, or otherwise throws a . - /// - /// The string to be checked. - /// The substring that must be part of . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - { - Throw.StringDoesNotContain(parameter, value, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The substring that must be part of . - /// The delegate that creates you custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !parameter.Contains(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - return parameter; - } - - /// - /// Ensures that the string contains the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The substring that must be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) - { - Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The substring that must be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates you custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - return parameter; - } - /// /// Ensures that the string does not contain the specified value, or otherwise throws a . /// From cec7c6be318bf11b8c2288bef26ea19c7cb755c2 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:54:04 +0100 Subject: [PATCH 056/123] refactor: move MustNotContain string validation methods from Check.StringAssertions.cs to Check.MustNotContain.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotContain.cs | 118 ++++++++++++++++++ .../Check.StringAssertions.cs | 84 ------------- 2 files changed, 118 insertions(+), 84 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustNotContain.cs b/Code/Light.GuardClauses/Check.MustNotContain.cs index c622120..d9290ff 100644 --- a/Code/Light.GuardClauses/Check.MustNotContain.cs +++ b/Code/Light.GuardClauses/Check.MustNotContain.cs @@ -78,4 +78,122 @@ public static TCollection MustNotContain( return parameter; } + + /// + /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The string that must not be part of . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + { + Throw.StringContains(parameter, value, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The string that must not be part of . + /// The delegate that creates your custom exception (optional). and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.Contains(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).IndexOf( + value.MustNotBeNull(nameof(value), message), + comparisonType + ) >= + 0) + { + Throw.StringContains(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 506335a..33cb8c6 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,90 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The string that must not be part of . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - { - Throw.StringContains(parameter, value, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The string that must not be part of . - /// The delegate that creates your custom exception (optional). and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.Contains(value)) - Throw.CustomException(exceptionFactory, parameter, value!); - return parameter; - } - - /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) - Throw.StringContains(parameter, value, comparisonType, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - return parameter; - } - /// /// Checks if the string contains the specified value using the given comparison type. /// From 3c20c8667855e2a81a509fd2e8a5d23758019817 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 18:57:12 +0100 Subject: [PATCH 057/123] refactor: move Contains string validation method from Check.StringAssertions.cs to Check.Contains.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.Contains.cs | 28 +++++++++++++++++++ .../Check.StringAssertions.cs | 15 ---------- 2 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.Contains.cs diff --git a/Code/Light.GuardClauses/Check.Contains.cs b/Code/Light.GuardClauses/Check.Contains.cs new file mode 100644 index 0000000..5e5c9f2 --- /dev/null +++ b/Code/Light.GuardClauses/Check.Contains.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the string contains the specified value using the given comparison type. + /// + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if contains , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("string:null => halt; value:null => halt")] + public static bool Contains( + // ReSharper disable once RedundantNullableFlowAttribute -- Caller might have NRTs turned off + [NotNull] [ValidatedNotNull] this string @string, + string value, + StringComparison comparisonType + ) => + @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 33cb8c6..3866b7e 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,21 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the string contains the specified value using the given comparison type. - /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if contains , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("string:null => halt; value:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - public static bool Contains([NotNull, ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => - @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; - /// /// Checks if the string is a substring of the other string. /// From c0d39a7fbdadd616f9bb4860b0774c0ecbc234f8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:00:13 +0100 Subject: [PATCH 058/123] refactor: move IsSubstringOf string validation methods from Check.StringAssertions.cs to Check.IsSubstringOf.cs Signed-off-by: Kenny Pflug --- .../Light.GuardClauses/Check.IsSubstringOf.cs | 46 +++++++++++++++++++ .../Check.StringAssertions.cs | 30 ------------ 2 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsSubstringOf.cs diff --git a/Code/Light.GuardClauses/Check.IsSubstringOf.cs b/Code/Light.GuardClauses/Check.IsSubstringOf.cs new file mode 100644 index 0000000..7812ef2 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsSubstringOf.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the string is a substring of the other string. + /// + /// The string to be checked. + /// The other string. + /// True if is a substring of , else false. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf( + [NotNull] [ValidatedNotNull] this string value, + [NotNull] [ValidatedNotNull] string other + ) => + other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper restore RedundantNullableFlowAttribute + + /// + /// Checks if the string is a substring of the other string. + /// + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if is a substring of , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf( + [NotNull] [ValidatedNotNull] this string value, + [NotNull] [ValidatedNotNull] string other, + StringComparison comparisonType + ) => + other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + // ReSharper disable RedundantNullableFlowAttribute +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 3866b7e..a6c974e 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,36 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the string is a substring of the other string. - /// - /// The string to be checked. - /// The other string. - /// True if is a substring of , else false. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other) => - other.MustNotBeNull(nameof(other)).Contains(value); - // ReSharper restore RedundantNullableFlowAttribute - - /// - /// Checks if the string is a substring of the other string. - /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if is a substring of , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other, StringComparison comparisonType) => - other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; - // ReSharper disable RedundantNullableFlowAttribute - /// /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// From f4df5ffe4ad546825afc6ddb6677f0aa54bba649 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:06:56 +0100 Subject: [PATCH 059/123] refactor: move MustBeSubstringOf string validation methods from Check.StringAssertions.cs to Check.MustBeSubstringOf.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeSubstringOf.cs | 127 ++++++++++++++++++ .../Check.StringAssertions.cs | 82 ----------- 2 files changed, 127 insertions(+), 82 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeSubstringOf.cs diff --git a/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs b/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs new file mode 100644 index 0000000..63fd7f9 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs @@ -0,0 +1,127 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must contain . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + { + Throw.NotSubstring(parameter, value, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must contain . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !value.Contains(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) == -1) + { + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index a6c974e..cc25b7f 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,88 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must contain . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.NotSubstring(parameter, value, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must contain . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !value.Contains(parameter)) - Throw.CustomException(exceptionFactory, parameter, value!); - return parameter; - } - - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must contain . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) == -1) - Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must contain . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - return parameter; - } - /// /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// From c7910bc71532da302b8d6819bc7e3ef801c0a225 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:11:15 +0100 Subject: [PATCH 060/123] refactor: remove MustNotBeSubstringOf string validation methods from Check.StringAssertions.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotBeSubstringOf.cs | 126 ++++++++++++++++++ .../Check.StringAssertions.cs | 83 ------------ 2 files changed, 126 insertions(+), 83 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs diff --git a/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs b/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs new file mode 100644 index 0000000..b098ae8 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must not contain . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + { + Throw.Substring(parameter, value, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not contain . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.Contains(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) != -1) + { + Throw.Substring(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf( + [NotNull] [ValidatedNotNull] this string? parameter, + string value, + StringComparison comparisonType, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index cc25b7f..3106f73 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,89 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must not contain . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.Substring(parameter, value, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must not contain . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.Contains(parameter)) - Throw.CustomException(exceptionFactory, parameter, value!); - return parameter; - } - - /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) != -1) - Throw.Substring(parameter, value, comparisonType, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - return parameter; - } - /// /// Ensures that the string starts with the specified value, or otherwise throws a . /// From b5f24a396e7c2b34b8333755c3e6823575fb18c0 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:14:42 +0100 Subject: [PATCH 061/123] refactor: move MustStartWith string validation methods from Check.StringAssertions.cs to Check.MustStartWith.cs Signed-off-by: Kenny Pflug --- .../Light.GuardClauses/Check.MustStartWith.cs | 97 +++++++++++++++++++ .../Check.StringAssertions.cs | 85 ---------------- 2 files changed, 97 insertions(+), 85 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustStartWith.cs diff --git a/Code/Light.GuardClauses/Check.MustStartWith.cs b/Code/Light.GuardClauses/Check.MustStartWith.cs new file mode 100644 index 0000000..ce3328e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustStartWith.cs @@ -0,0 +1,97 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- Caller might have NRTs turned off + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string starts with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not start with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 3106f73..815aba8 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,91 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string starts with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must start with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not start with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType = StringComparison.CurrentCulture, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) - { - if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) - { - Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string must start with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.StartsWith(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - return parameter; - } - - /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string must start with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - return parameter; - } - /// /// Ensures that the string does not start with the specified value, or otherwise throws a . /// From 9b474edb936f58e010d99054eac06ad2d6f163f7 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:18:34 +0100 Subject: [PATCH 062/123] refactor: add MustNotStartWith string validation methods and remove from Check.StringAssertions.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotStartWith.cs | 96 +++++++++++++++++++ .../Check.StringAssertions.cs | 84 ---------------- 2 files changed, 96 insertions(+), 84 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotStartWith.cs diff --git a/Code/Light.GuardClauses/Check.MustNotStartWith.cs b/Code/Light.GuardClauses/Check.MustNotStartWith.cs new file mode 100644 index 0000000..d4cc668 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotStartWith.cs @@ -0,0 +1,96 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- Caller might have NRTs turned off + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string does not start with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when starts with . + /// Thrown when or is null. + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] + string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 815aba8..05e9578 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,90 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string does not start with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when starts with . - /// Thrown when or is null. - public static string MustNotStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType = StringComparison.CurrentCulture, - [CallerArgumentExpression("parameter")] - string? parameterName = null, - string? message = null - ) - { - if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) - { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); - } - - return parameter; - } - - /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must not start with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - return parameter; - } - - /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - return parameter; - } - /// /// Ensures that the string ends with the specified value, or otherwise throws a . /// From 40192d21e2e42ecbf18b4eedc0d3f205b883bbbf Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:21:16 +0100 Subject: [PATCH 063/123] refactor: add MustEndWith string validation methods and remove from Check.StringAssertions.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.MustEndWith.cs | 94 +++++++++++++++++++ .../Check.StringAssertions.cs | 82 ---------------- 2 files changed, 94 insertions(+), 82 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustEndWith.cs diff --git a/Code/Light.GuardClauses/Check.MustEndWith.cs b/Code/Light.GuardClauses/Check.MustEndWith.cs new file mode 100644 index 0000000..06dc8b2 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustEndWith.cs @@ -0,0 +1,94 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- Caller might have NRTs switched off + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not end with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 05e9578..855d823 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,88 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string ends with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not end with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType = StringComparison.CurrentCulture, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) - { - if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) - { - Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string ends with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - return parameter; - } - - /// - /// Ensures that the string ends with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - return parameter; - } - /// /// Ensures that the string does not end with the specified value, or otherwise throws a . /// From 8f6fef3154a392ca23063d4af9804c0cc46fe263 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:24:31 +0100 Subject: [PATCH 064/123] refactor: add MustNotEndWith string validation methods and remove from Check.StringAssertions.cs Signed-off-by: Kenny Pflug --- .../Check.MustNotEndWith.cs | 94 +++++++++++++++++++ .../Check.StringAssertions.cs | 82 ---------------- 2 files changed, 94 insertions(+), 82 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustNotEndWith.cs diff --git a/Code/Light.GuardClauses/Check.MustNotEndWith.cs b/Code/Light.GuardClauses/Check.MustNotEndWith.cs new file mode 100644 index 0000000..549a631 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotEndWith.cs @@ -0,0 +1,94 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable RedundantNullableFlowAttribute -- Caller might have NRTs turned off + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when ends with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must not end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 855d823..265bc6f 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,88 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when ends with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustNotEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType = StringComparison.CurrentCulture, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) - { - if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) - { - Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); - } - return parameter; - } - - /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must not end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - return parameter; - } - - /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . - /// - /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith( - [NotNull, ValidatedNotNull] this string? parameter, - [NotNull, ValidatedNotNull] string value, - StringComparison comparisonType, - [NotNull, ValidatedNotNull] Func exceptionFactory - ) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - return parameter; - } - /// /// Checks if the specified string is an email address using the default email regular expression /// defined in . From c1a34cf235dbdde8e2a85d54040e041bf05ac969 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:26:51 +0100 Subject: [PATCH 065/123] refactor: add IsEmailAddress validation methods and remove from Check.StringAssertions.cs Signed-off-by: Kenny Pflug --- .../Check.IsEmailAddress.cs | 31 +++++++++++++++++++ .../Check.StringAssertions.cs | 21 ------------- 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsEmailAddress.cs diff --git a/Code/Light.GuardClauses/Check.IsEmailAddress.cs b/Code/Light.GuardClauses/Check.IsEmailAddress.cs new file mode 100644 index 0000000..7cfb698 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsEmailAddress.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string is an email address using the default email regular expression + /// defined in . + /// + /// The string to be checked if it is an email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => + emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); + + /// + /// Checks if the specified string is an email address using the provided regular expression for validation. + /// + /// The string to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => + emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 265bc6f..09c9c30 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,27 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is an email address using the default email regular expression - /// defined in . - /// - /// The string to be checked if it is an email address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => - emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); - - /// - /// Checks if the specified string is an email address using the provided regular expression for validation. - /// - /// The string to be checked. - /// The regular expression that determines whether the input string is an email address. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => - emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); - /// /// Ensures that the string is a valid email address using the default email regular expression /// defined in , or otherwise throws an . From 8a9b839e1aecad57b638d3f071213974718ec5b2 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:43:42 +0100 Subject: [PATCH 066/123] refactor: move MustBeEmailAddress from Check.StringAssertions.cs to Check.MustBeEmailAddress.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeEmailAddress.cs | 110 ++++++++++++++++++ .../Check.StringAssertions.cs | 71 ----------- 2 files changed, 110 insertions(+), 71 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeEmailAddress.cs diff --git a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs new file mode 100644 index 0000000..920e55e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs @@ -0,0 +1,110 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The email address that will be validated. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. + /// + /// The email address that will be validated. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (!parameter.IsEmailAddress()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws an . + /// + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress( + [NotNull] [ValidatedNotNull] this string? parameter, + Regex emailAddressPattern, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. + /// + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress( + [NotNull] [ValidatedNotNull] this string? parameter, + Regex emailAddressPattern, + Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) + { + Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 09c9c30..0818f4b 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,77 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws an . - /// - /// The email address that will be validated. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) - Throw.InvalidEmailAddress(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws your custom exception. - /// - /// The email address that will be validated. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (!parameter.IsEmailAddress()) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - - /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws an . - /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) - Throw.InvalidEmailAddress(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws your custom exception. - /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) - Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); - return parameter; - } - /// /// Ensures that the string is shorter than the specified length, or otherwise throws a . /// From 7bc8934ab8db97507d8f0fc40cba4bfd4ce04f9c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:48:31 +0100 Subject: [PATCH 067/123] refactor: move MustBeShorterThan methods from Check.StringAssertions.cs to Check.MustBeShorterThan.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeShorterThan.cs | 51 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs index 8488e0e..f871358 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs @@ -1,11 +1,62 @@ using System; using System.Runtime.CompilerServices; using Light.GuardClauses.Exceptions; +using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the string is shorter than the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be shorter than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length greater than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThan( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Length >= length) + { + Throw.StringNotShorterThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The length that the string must be shorter than. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length greater than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThan( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + Func exceptionFactory + ) + { + if (parameter is null || parameter.Length >= length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 0818f4b..b39262a 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is shorter than the specified length, or otherwise throws a . - /// - /// The string to be checked. - /// The length that the string must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than or equal to . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Length >= length) - Throw.StringNotShorterThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The length that the string must be shorter than. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length >= length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . /// From 55b4a6b72ef48ce95fa03ca5ea00e93850741491 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:51:23 +0100 Subject: [PATCH 068/123] refactor: move MustBeShorterThanOrEqualTo methods from Check.StringAssertions.cs to Check.MustBeShorterThanOrEqualTo.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeShorterThanOrEqualTo.cs | 51 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs index 2be3c5a..20f2de8 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs @@ -1,11 +1,62 @@ using System; using System.Runtime.CompilerServices; +using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Length > length) + { + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length greater than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + Func exceptionFactory + ) + { + if (parameter is null || parameter.Length > length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index b39262a..0638abd 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . - /// - /// The string to be checked. - /// The length that the string must be shorter than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Length > length) - Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The length that the string must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length > length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the string has the specified length, or otherwise throws a . /// From 756ce0abda368853e70c2547bc45afdadd91963e Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:53:48 +0100 Subject: [PATCH 069/123] refactor: move MustHaveLength methods from Check.StringAssertions.cs to Check.MustHaveLength.cs Signed-off-by: Kenny Pflug --- .../Check.MustHaveLength.cs | 51 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustHaveLength.cs b/Code/Light.GuardClauses/Check.MustHaveLength.cs index fdae65b..d912c6b 100644 --- a/Code/Light.GuardClauses/Check.MustHaveLength.cs +++ b/Code/Light.GuardClauses/Check.MustHaveLength.cs @@ -1,11 +1,62 @@ using System; using System.Runtime.CompilerServices; +using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the string has the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The asserted length of the string. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length other than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLength( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Length != length) + { + Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string has the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The asserted length of the string. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length other than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLength( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + Func exceptionFactory + ) + { + if (parameter is null || parameter.Length != length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// /// Ensures that the span has the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 0638abd..50169ac 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string has the specified length, or otherwise throws a . - /// - /// The string to be checked. - /// The asserted length of the string. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length other than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Length != length) - Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string has the specified length, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The asserted length of the string. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length other than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length != length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the string is longer than the specified length, or otherwise throws a . /// From 2517eb206ce7c6de546ca960f3b63243976cbc6c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:55:58 +0100 Subject: [PATCH 070/123] refactor: move MustBeLongerThan methods from Check.StringAssertions.cs to Check.MustBeLongerThan.cs --- .../Check.MustBeLongerThan.cs | 51 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs index 7c139e1..c884530 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs @@ -1,11 +1,62 @@ using System; using System.Runtime.CompilerServices; +using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the string is longer than the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be longer than. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length shorter than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThan( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Length <= length) + { + Throw.StringNotLongerThan(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is longer than the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The length that the string must be longer than. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length shorter than or equal to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThan( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + Func exceptionFactory + ) + { + if (parameter is null || parameter.Length <= length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// /// Ensures that the span is longer than the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 50169ac..7455844 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is longer than the specified length, or otherwise throws a . - /// - /// The string to be checked. - /// The length that the string must be longer than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length shorter than or equal to . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Length <= length) - Throw.StringNotLongerThan(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is longer than the specified length, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The length that the string must be longer than. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length shorter than or equal to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length <= length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . /// From 93489500c2b0b7914092e0f9508051fc44ba1817 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 19:57:46 +0100 Subject: [PATCH 071/123] refactor: move MustBeLongerThanOrEqualTo methods from Check.StringAssertions.cs to Check.MustBeLongerThanOrEqualTo.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeLongerThanOrEqualTo.cs | 51 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs index 87280b1..f2e98ec 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs @@ -1,11 +1,62 @@ using System; using System.Runtime.CompilerServices; +using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length shorter than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).Length < length) + { + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The length that the string must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo( + [NotNull] [ValidatedNotNull] this string? parameter, + int length, + Func exceptionFactory + ) + { + if (parameter is null || parameter.Length < length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 7455844..87e8bf6 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . - /// - /// The string to be checked. - /// The length that the string must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length shorter than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustNotBeNull(parameterName, message).Length < length) - Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The length that the string must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length shorter than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length < length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - /// /// Ensures that the string's length is within the specified range, or otherwise throws a . /// From 6f61a7e9784cbc9e935f8e98bea3699371e9c4b2 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:00:48 +0100 Subject: [PATCH 072/123] refactor: move MustHaveLengthIn methods from Check.StringAssertions.cs to Check.MustHaveLengthIn.cs Signed-off-by: Kenny Pflug --- .../Check.MustHaveLengthIn.cs | 59 +++++++++++++++++++ .../Check.StringAssertions.cs | 34 ----------- 2 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustHaveLengthIn.cs diff --git a/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs b/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs new file mode 100644 index 0000000..340d142 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs @@ -0,0 +1,59 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string's length is within the specified range, or otherwise throws a . + /// + /// The string to be checked. + /// The range where the string's length must be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the length of is not with the specified . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLengthIn( + [NotNull] [ValidatedNotNull] this string? parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) + { + Throw.StringLengthNotInRange(parameter, range, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The range where the string's length must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or its length is not within the specified range. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLengthIn( + [NotNull] [ValidatedNotNull] this string? parameter, + Range range, + Func, Exception> exceptionFactory + ) + { + if (parameter is null || !range.IsValueWithinRange(parameter.Length)) + { + Throw.CustomException(exceptionFactory, parameter, range); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 87e8bf6..c07953f 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,40 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string's length is within the specified range, or otherwise throws a . - /// - /// The string to be checked. - /// The range where the string's length must be in-between. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the length of is not with the specified . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) - Throw.StringLengthNotInRange(parameter, range, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The range where the string's length must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or its length is not within the specified range. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) - { - if (parameter is null || !range.IsValueWithinRange(parameter.Length)) - Throw.CustomException(exceptionFactory, parameter, range); - return parameter; - } - /// /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . /// From deeac5cb9e155ad9658c2f349b03f04ae173fa6f Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:03:40 +0100 Subject: [PATCH 073/123] refactor: move IsNewLine method from Check.StringAssertions.cs to Check.IsNewLine.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsNewLine.cs | 18 ++++++++++++++++++ .../Check.StringAssertions.cs | 8 -------- 2 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsNewLine.cs diff --git a/Code/Light.GuardClauses/Check.IsNewLine.cs b/Code/Light.GuardClauses/Check.IsNewLine.cs new file mode 100644 index 0000000..80979ba --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsNewLine.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . + /// + /// The string to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] + public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => + parameter == "\n" || parameter == "\r\n"; +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index c07953f..6b53ece 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,14 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] - public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; - /// /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . /// From 66a0de9240712c63f9d55a39e902c678bc967689 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:07:44 +0100 Subject: [PATCH 074/123] refactor: move MustBeNewLine methods from Check.StringAssertions.cs to Check.MustBeNewLine.cs Signed-off-by: Kenny Pflug --- .../Light.GuardClauses/Check.MustBeNewLine.cs | 55 +++++++++++++++++++ .../Check.StringAssertions.cs | 32 ----------- 2 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeNewLine.cs diff --git a/Code/Light.GuardClauses/Check.MustBeNewLine.cs b/Code/Light.GuardClauses/Check.MustBeNewLine.cs new file mode 100644 index 0000000..c5fef10 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeNewLine.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to "\n" or "\r\n". + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeNewLine( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) + { + Throw.NotNewLine(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is not equal to "\n" or "\r\n". + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeNewLine( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (!parameter.IsNewLine()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 6b53ece..e957536 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,38 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to "\n" or "\r\n". - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) - Throw.NotNewLine(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is not equal to "\n" or "\r\n". - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (!parameter.IsNewLine()) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Checks if the specified string is trimmed, i.e. it does not start or end with /// white space characters. Inputting an empty string will return true. When null is passed, From e2c8e505928bd516b5d7b2fb81037a7127edb2a1 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:09:56 +0100 Subject: [PATCH 075/123] refactor: move IsTrimmed methods from Check.StringAssertions.cs to Check.IsTrimmed.cs Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsTrimmed.cs | 39 +++++++++++++++++++ .../Check.StringAssertions.cs | 33 ---------------- 2 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsTrimmed.cs diff --git a/Code/Light.GuardClauses/Check.IsTrimmed.cs b/Code/Light.GuardClauses/Check.IsTrimmed.cs new file mode 100644 index 0000000..727de57 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsTrimmed.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty string will return true. When null is passed, + /// you can control the return value with which will + /// return true by default. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed, else false. An empty string will result in true. + /// You can control the return value with when the + /// is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => + parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); + + /// + /// Checks if the specified character span is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// True if the is trimmed, else false. An empty span will result in true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this ReadOnlySpan parameter) => + parameter.Length == 0 || + !parameter[0].IsWhiteSpace() && + !parameter[parameter.Length - 1].IsWhiteSpace(); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index e957536..cd28c26 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,39 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty string will return true. When null is passed, - /// you can control the return value with which will - /// return true by default. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed, else false. An empty string will result in true. - /// You can control the return value with when the - /// is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => - parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); - - /// - /// Checks if the specified character span is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// True if the is trimmed, else false. An empty span will result in true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this ReadOnlySpan parameter) => - parameter.Length == 0 || - !parameter[0].IsWhiteSpace() && - !parameter[parameter.Length - 1].IsWhiteSpace(); - - /// /// Ensures that the string is not null and trimmed, or otherwise throws a . /// Empty strings are regarded as trimmed. From 59bb8d2e9ebd378684d6f5992a654be674cb445a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:13:26 +0100 Subject: [PATCH 076/123] refactor: move MustBeTrimmed methods from Check.StringAssertions.cs to Check.MustBeTrimmed.cs Signed-off-by: Kenny Pflug --- .../Light.GuardClauses/Check.MustBeTrimmed.cs | 60 +++++++++++++++++++ .../Check.StringAssertions.cs | 38 ------------ 2 files changed, 60 insertions(+), 38 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeTrimmed.cs diff --git a/Code/Light.GuardClauses/Check.MustBeTrimmed.cs b/Code/Light.GuardClauses/Check.MustBeTrimmed.cs new file mode 100644 index 0000000..c4e163e --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeTrimmed.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is not null and trimmed, or otherwise throws a . + /// Empty strings are regarded as trimmed. + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not trimmed, i.e. they start or end with white space characters. + /// Empty strings are regarded as trimmed. + /// + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) + { + Throw.NotTrimmed(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (parameter is null || !parameter.AsSpan().IsTrimmed()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index cd28c26..2207536 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,44 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is not null and trimmed, or otherwise throws a . - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed, i.e. they start or end with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) - Throw.NotTrimmed(parameter, parameterName, message); - return parameter; - - } - - /// - /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (parameter is null || !parameter.AsSpan().IsTrimmed()) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Checks if the specified string is trimmed at the start, i.e. it does not start with /// white space characters. Inputting an empty string will return true. From 2a76442623f00405e4a3172c08a0759beceeb090 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:17:21 +0100 Subject: [PATCH 077/123] refactor: move IsTrimmedAtStart methods from Check.StringAssertions.cs to Check.IsTrimmedAtStart.cs Signed-off-by: Kenny Pflug --- .../Check.IsTrimmedAtStart.cs | 37 +++++++++++++++++++ .../Check.StringAssertions.cs | 30 --------------- 2 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsTrimmedAtStart.cs diff --git a/Code/Light.GuardClauses/Check.IsTrimmedAtStart.cs b/Code/Light.GuardClauses/Check.IsTrimmedAtStart.cs new file mode 100644 index 0000000..3e2e3d1 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsTrimmedAtStart.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty string will return true. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the start, else false. + /// An empty string will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => + parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); + + /// + /// Checks if the specified character span is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// + /// True if the is trimmed at the start, else false. + /// An empty span will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => + parameter.Length == 0 || !parameter[0].IsWhiteSpace(); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 2207536..e92b211 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,36 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the start, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => - parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); - - /// - /// Checks if the specified character span is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// - /// True if the is trimmed at the start, else false. - /// An empty span will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => - parameter.Length == 0 || !parameter[0].IsWhiteSpace(); - /// /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . /// Empty strings are regarded as trimmed. From 9382b25efddd199806c2da538582a3298b13f5e7 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:19:07 +0100 Subject: [PATCH 078/123] refactor: move MustBeTrimmedAtStart methods from Check.StringAssertions.cs to Check.MustBeTrimmedAtStart.cs Signed-off-by: Kenny Pflug --- .../Check.MustBeTrimmedAtStart.cs | 60 +++++++++++++++++++ .../Check.StringAssertions.cs | 37 ------------ 2 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs diff --git a/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs b/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs new file mode 100644 index 0000000..34dad8b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . + /// Empty strings are regarded as trimmed. + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not trimmed at the start, i.e. they start with white space characters. + /// Empty strings are regarded as trimmed. + /// + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtStart( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) + { + Throw.NotTrimmedAtStart(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtStart( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index e92b211..2d6407d 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,43 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed at the start, i.e. they start with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) - Throw.NotTrimmedAtStart(parameter, parameterName, message); - return parameter; - } - - /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) - Throw.CustomException(exceptionFactory, parameter); - return parameter; - } - /// /// Checks if the specified string is trimmed at the end, i.e. it does not end with /// white space characters. Inputting an empty string will return true. From 15f486930d385d69c624cbb6a27091c1049f923c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:21:21 +0100 Subject: [PATCH 079/123] refactor: move IsTrimmedAtEnd methods from Check.StringAssertions.cs to Check.IsTrimmedAtEnd.cs Signed-off-by: Kenny Pflug --- .../Check.IsTrimmedAtEnd.cs | 37 +++++++++++++++++++ .../Check.StringAssertions.cs | 30 --------------- 2 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 Code/Light.GuardClauses/Check.IsTrimmedAtEnd.cs diff --git a/Code/Light.GuardClauses/Check.IsTrimmedAtEnd.cs b/Code/Light.GuardClauses/Check.IsTrimmedAtEnd.cs new file mode 100644 index 0000000..644ba56 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsTrimmedAtEnd.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty string will return true. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the end, else false. + /// An empty string will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => + parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); + + /// + /// Checks if the specified character span is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// + /// True if the is trimmed at the end, else false. + /// An empty span will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => + parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); +} diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 2d6407d..e6b86a6 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -10,36 +10,6 @@ namespace Light.GuardClauses; public static partial class Check { - /// - /// Checks if the specified string is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the start, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => - parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); - - /// - /// Checks if the specified character span is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// - /// True if the is trimmed at the end, else false. - /// An empty span will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => - parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); - /// /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . /// Empty strings are regarded as trimmed. From 41b2f2a588f7182da09330e856164c5a252a8fe7 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:23:59 +0100 Subject: [PATCH 080/123] refactor: rename Check.StringAssertions.cs to Check.MustBeTrimmedAtEnd.cs Signed-off-by: Kenny Pflug --- ...ertions.cs => Check.MustBeTrimmedAtEnd.cs} | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) rename Code/Light.GuardClauses/{Check.StringAssertions.cs => Check.MustBeTrimmedAtEnd.cs} (68%) diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs similarity index 68% rename from Code/Light.GuardClauses/Check.StringAssertions.cs rename to Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs index e6b86a6..de90549 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs @@ -1,9 +1,7 @@ using System; -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; +using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.Exceptions; -using System.Runtime.CompilerServices; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -11,39 +9,52 @@ namespace Light.GuardClauses; public static partial class Check { /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . + /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . /// Empty strings are regarded as trimmed. /// /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when is not trimmed at the end, i.e. they end with white space characters. + /// Thrown when is not trimmed at the end, i.e. they end with white space characters. /// Empty strings are regarded as trimmed. /// - /// Thrown when is null. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtEnd( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) + { Throw.NotTrimmedAtEnd(parameter, parameterName, message); + } + return parameter; } - + /// /// Ensures that the string is not null and trimmed at the end, or otherwise throws your custom exception. /// Empty strings are regarded as trimmed. /// /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtEnd( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) { if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) + { Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } -} \ No newline at end of file +} From e357f992160e28229f1339fedc3145b361907ec6 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:35:29 +0100 Subject: [PATCH 081/123] chore: reformat framework extensions and other files in main namespace Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/EnumInfo.cs | 21 ++- .../EquivalentTypeComparer.cs | 6 +- .../EnumerableExtensions.cs | 132 ++++++++++++--- .../ExpressionExtensions.cs | 28 ++- .../FrameworkExtensions/MultiplyAddHash.cs | 159 ++++++++++++++++-- .../MultiplyAddHashBuilder.cs | 13 +- .../FrameworkExtensions/TextExtensions.cs | 123 ++++++++++++-- ...dinalIgnoreCaseIgnoreWhiteSpaceComparer.cs | 10 +- .../OrdinalIgnoreWhiteSpaceComparer.cs | 10 +- Code/Light.GuardClauses/Range.cs | 58 ++++--- Code/Light.GuardClauses/RegularExpressions.cs | 7 +- Code/Light.GuardClauses/SpanDelegates.cs | 2 +- .../StringComparisonType.cs | 4 +- 13 files changed, 454 insertions(+), 119 deletions(-) diff --git a/Code/Light.GuardClauses/EnumInfo.cs b/Code/Light.GuardClauses/EnumInfo.cs index d10ad44..49232e9 100644 --- a/Code/Light.GuardClauses/EnumInfo.cs +++ b/Code/Light.GuardClauses/EnumInfo.cs @@ -21,7 +21,7 @@ public static class EnumInfo where T : struct, Enum typeof(T).GetCustomAttribute(Types.FlagsAttributeType) != null; /// - /// Gets the flags pattern when is true. If the enum is not a flags enum, then 0UL is returned. + /// Gets the flags pattern when is true. If the enum is not a flags enum, then 0UL is returned. /// public static readonly ulong FlagsPattern; @@ -40,10 +40,12 @@ static EnumInfo() #else EnumConstantsArray = (T[]) Enum.GetValues(typeof(T)); #endif - EnumConstants = new (EnumConstantsArray); + EnumConstants = new ReadOnlyMemory(EnumConstantsArray); if (!IsFlagsEnum) + { return; + } for (var i = 0; i < EnumConstantsArray.Length; ++i) { @@ -63,8 +65,12 @@ private static bool IsValidValue(T parameter) { var comparer = EqualityComparer.Default; for (var i = 0; i < EnumConstantsArray.Length; ++i) + { if (comparer.Equals(EnumConstantsArray[i], parameter)) + { return true; + } + } return false; } @@ -90,12 +96,11 @@ private static ulong ConvertToUInt64(T value) case 8: return Unsafe.As(ref value); default: ThrowUnknownEnumSize(); - return default; + return 0UL; } } - private static void ThrowUnknownEnumSize() - { - throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); - } -} \ No newline at end of file + private static void ThrowUnknownEnumSize() => throw new InvalidOperationException( + $"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones." + ); +} diff --git a/Code/Light.GuardClauses/EquivalentTypeComparer.cs b/Code/Light.GuardClauses/EquivalentTypeComparer.cs index 02784ac..6123f9e 100644 --- a/Code/Light.GuardClauses/EquivalentTypeComparer.cs +++ b/Code/Light.GuardClauses/EquivalentTypeComparer.cs @@ -15,7 +15,7 @@ public sealed class EquivalentTypeComparer : IEqualityComparer /// /// Gets a singleton instance of the equality comparer. /// - public static readonly EquivalentTypeComparer Instance = new EquivalentTypeComparer(); + public static readonly EquivalentTypeComparer Instance = new (); /// /// Checks if the two types are equivalent (using ). @@ -35,8 +35,8 @@ public sealed class EquivalentTypeComparer : IEqualityComparer /// The type whose hash code is requested. [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetHashCode(Type type) => - // ReSharper disable once ConditionIsAlwaysTrueOrFalse + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs index f204458..9ecd452 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs @@ -2,8 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using System.Runtime.CompilerServices; +using JetBrains.Annotations; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; @@ -25,7 +25,7 @@ public static class EnumerableExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source) => + public static IList AsList([NotNull] [ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); /// @@ -39,9 +39,13 @@ public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => - source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + public static IList AsList( + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull] [ValidatedNotNull] this IEnumerable source, + Func, IList> createCollection + ) => + source as IList ?? + createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); /// /// Tries to downcast the specified enumerable to an array, or creates a new array with the specified items. @@ -53,7 +57,8 @@ public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static T[] AsArray([NotNull, ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); + public static T[] AsArray([NotNull] [ValidatedNotNull] this IEnumerable source) => + source as T[] ?? source.ToArray(); /// /// Performs the action on each item of the specified enumerable. If the enumerable contains items that are null, this @@ -65,8 +70,12 @@ public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable /// The value indicating whether this method should throw a when any of the items is null (optional). Defaults to true. /// Thrown when or is null. /// Thrown when contains a value that is null and is set to true. - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) + public static IEnumerable ForEach( + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull] [ValidatedNotNull] this IEnumerable enumerable, + Action action, + bool throwWhenItemIsNull = true + ) { // ReSharper disable PossibleMultipleEnumeration action.MustNotBeNull(nameof(action)); @@ -79,7 +88,14 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer var item = list[i]; if (item is null) { - if (throwWhenItemIsNull) throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + if (throwWhenItemIsNull) + { + throw new CollectionException( + nameof(enumerable), + $"The collection contains null at index {i}." + ); + } + continue; } @@ -92,7 +108,14 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer { if (item is null) { - if (throwWhenItemIsNull) throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + if (throwWhenItemIsNull) + { + throw new CollectionException( + nameof(enumerable), + $"The collection contains null at index {i}." + ); + } + ++i; continue; } @@ -117,7 +140,7 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source) => + public static IReadOnlyList AsReadOnlyList([NotNull] [ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); /// @@ -132,8 +155,12 @@ public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] thi [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source, [NotNull, ValidatedNotNull] Func, IReadOnlyList> createCollection) => - source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + public static IReadOnlyList AsReadOnlyList( + [NotNull] [ValidatedNotNull] this IEnumerable source, + [NotNull] [ValidatedNotNull] Func, IReadOnlyList> createCollection + ) => + source as IReadOnlyList ?? + createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); // ReSharper restore RedundantNullableFlowAttribute @@ -141,16 +168,21 @@ public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] thi /// Gets the count of the specified enumerable. /// /// The enumerable whose count should be determined. - /// Thrown when is null. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) + public static int Count([NotNull] [ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } return DetermineCountViaEnumerating(enumerable); } @@ -159,37 +191,54 @@ public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) /// Gets the count of the specified enumerable. /// /// The enumerable whose count should be determined. - /// The name of the parameter that is passed to the (optional). - /// The message that is passed to the (optional). - /// Thrown when is null. + /// The name of the parameter that is passed to the (optional). + /// The message that is passed to the (optional). + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int Count([NotNull, ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) + public static int Count( + [NotNull] [ValidatedNotNull] this IEnumerable? enumerable, + string? parameterName, + string? message + ) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } return DetermineCountViaEnumerating(enumerable, parameterName, message); } - + /// /// Gets the count of the specified enumerable. /// /// The enumerable whose count should be determined. - /// Thrown when is null. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable) + public static int GetCount([NotNull] [ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + if (TryGetCollectionOfTCount(enumerable, out var count)) + { return count; + } return DetermineCountViaEnumerating(enumerable); } @@ -198,19 +247,31 @@ public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable en /// Gets the count of the specified enumerable. /// /// The enumerable whose count should be determined. - /// The name of the parameter that is passed to the (optional). - /// The message that is passed to the (optional). - /// Thrown when is null. + /// The name of the parameter that is passed to the (optional). + /// The message that is passed to the (optional). + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable, string? parameterName, string? message = null) + public static int GetCount( + [NotNull] [ValidatedNotNull] this IEnumerable enumerable, + string? parameterName, + string? message = null + ) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + if (TryGetCollectionOfTCount(enumerable, out var count)) + { return count; + } return DetermineCountViaEnumerating(enumerable, parameterName, message); } @@ -222,6 +283,7 @@ private static bool TryGetCollectionOfTCount([NoEnumeration] this IEnumerable count = collectionOfT.Count; return true; } + if (enumerable is IReadOnlyCollection readOnlyCollection) { count = readOnlyCollection.Count; @@ -237,24 +299,36 @@ private static int DetermineCountViaEnumerating(IEnumerable? enumerable) var count = 0; var enumerator = enumerable.MustNotBeNull(nameof(enumerable)).GetEnumerator(); while (enumerator.MoveNext()) + { count++; + } + if (enumerator is IDisposable disposable) { disposable.Dispose(); } + return count; } - private static int DetermineCountViaEnumerating([NotNull] IEnumerable? enumerable, string? parameterName, string? message) + private static int DetermineCountViaEnumerating( + [NotNull] IEnumerable? enumerable, + string? parameterName, + string? message + ) { var count = 0; var enumerator = enumerable.MustNotBeNull(parameterName, message).GetEnumerator(); while (enumerator.MoveNext()) + { count++; + } + if (enumerator is IDisposable disposable) { disposable.Dispose(); } + return count; } @@ -264,9 +338,11 @@ internal static bool ContainsViaForeach(this IEnumerable items, TI foreach (var i in items) { if (equalityComparer.Equals(i, item)) + { return true; + } } return false; } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs index c695215..28ff16c 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs @@ -20,14 +20,21 @@ public static class ExpressionExtensions /// /// Throw when the is not of the shape "object => object.Property". /// - // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - public static PropertyInfo ExtractProperty([NotNull, ValidatedNotNull] this Expression> expression) + public static PropertyInfo ExtractProperty( + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull] [ValidatedNotNull] this Expression> expression + ) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; if (!(memberExpression?.Member is PropertyInfo propertyInfo)) - throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Property", nameof(expression)); + { + throw new ArgumentException( + "The specified expression is not valid. Please use an expression like the following one: o => o.Property", + nameof(expression) + ); + } return propertyInfo; } @@ -42,15 +49,22 @@ public static PropertyInfo ExtractProperty([NotNull, ValidatedNotN /// /// Throw when the is not of the shape "object => object.Field". /// - // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - public static FieldInfo ExtractField([NotNull, ValidatedNotNull] this Expression> expression) + public static FieldInfo ExtractField( + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull] [ValidatedNotNull] this Expression> expression + ) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; if (!(memberExpression?.Member is FieldInfo fieldInfo)) - throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Field", nameof(expression)); + { + throw new ArgumentException( + "The specified expression is not valid. Please use an expression like the following one: o => o.Field", + nameof(expression) + ); + } return fieldInfo; } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHash.cs b/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHash.cs index 2e36ae8..92a2880 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHash.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHash.cs @@ -83,7 +83,14 @@ public static int CreateHashCode(T1 value1, T2 value2, T3 va /// Creates a hash code from the six specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -99,7 +106,15 @@ public static int CreateHashCode(T1 value1, T2 value2, T /// Creates a hash code from the seven specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -116,7 +131,16 @@ public static int CreateHashCode(T1 value1, T2 value /// Creates a hash code from the eight specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -134,7 +158,17 @@ public static int CreateHashCode(T1 value1, T2 v /// Creates a hash code from the nine specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -153,7 +187,18 @@ public static int CreateHashCode(T1 value1, /// Creates a hash code from the ten specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -173,7 +218,19 @@ public static int CreateHashCode(T1 val /// Creates a hash code from the eleven specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -194,7 +251,20 @@ public static int CreateHashCode(T /// Creates a hash code from the eleven specified values. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11, + T12 value12 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -216,7 +286,21 @@ public static int CreateHashCode [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11, + T12 value12, + T13 value13 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -239,7 +323,22 @@ public static int CreateHashCode [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11, + T12 value12, + T13 value13, + T14 value14 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -263,7 +362,23 @@ public static int CreateHashCode [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11, + T12 value12, + T13 value13, + T14 value14, + T15 value15 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -288,7 +403,24 @@ public static int CreateHashCode [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15, T16 value16) + public static int CreateHashCode( + T1 value1, + T2 value2, + T3 value3, + T4 value4, + T5 value5, + T6 value6, + T7 value7, + T8 value8, + T9 value9, + T10 value10, + T11 value11, + T12 value12, + T13 value13, + T14 value14, + T15 value15, + T16 value16 + ) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -315,5 +447,6 @@ public static int CreateHashCodehash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CombineIntoHash(ref int hash, T value) => hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); -} \ No newline at end of file + public static void CombineIntoHash(ref int hash, T value) => + hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); +} diff --git a/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHashBuilder.cs b/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHashBuilder.cs index 7bb438f..c411eeb 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHashBuilder.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/MultiplyAddHashBuilder.cs @@ -3,10 +3,10 @@ namespace Light.GuardClauses.FrameworkExtensions; /// -/// Represents a builder for the algorithm that does not allocate. +/// Represents a builder for the algorithm that does not allocate. /// Should only be used in cases where the overload for sixteen values is not enough or a dedicated /// initial hash must be provided (e.g. for test reasons). -/// Instantiate the builder with the method. You have to instantiate a new builder +/// Instantiate the builder with the method. You have to instantiate a new builder /// for each hash code that you want to calculate. /// public struct MultiplyAddHashBuilder @@ -17,7 +17,7 @@ public struct MultiplyAddHashBuilder private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; /// - /// Combines the given value into the hash using the method. + /// Combines the given value into the hash using the method. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiplyAddHashBuilder CombineIntoHash(T value) @@ -33,9 +33,8 @@ public MultiplyAddHashBuilder CombineIntoHash(T value) public int BuildHash() => _hash; /// - /// Initializes a new instance of with the specified initial hash. + /// Initializes a new instance of with the specified initial hash. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new MultiplyAddHashBuilder(initialHash); - -} \ No newline at end of file + public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new (initialHash); +} diff --git a/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs index 10f3b7c..96901a9 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs @@ -35,36 +35,71 @@ public static class TextExtensions typeof(bool), typeof(double), typeof(decimal), - typeof(float) + typeof(float), ] ); private static bool IsUnquotedType() { if (typeof(T) == typeof(int)) + { return true; + } + if (typeof(T) == typeof(long)) + { return true; + } + if (typeof(T) == typeof(short)) + { return true; + } + if (typeof(T) == typeof(sbyte)) + { return true; + } + if (typeof(T) == typeof(uint)) + { return true; + } + if (typeof(T) == typeof(ulong)) + { return true; + } + if (typeof(T) == typeof(ushort)) + { return true; + } + if (typeof(T) == typeof(byte)) + { return true; + } + if (typeof(T) == typeof(bool)) + { return true; + } + if (typeof(T) == typeof(double)) + { return true; + } + if (typeof(T) == typeof(decimal)) + { return true; + } + if (typeof(T) == typeof(float)) + { return true; + } return false; } @@ -77,7 +112,8 @@ private static bool IsUnquotedType() /// The text that is returned when is null (defaults to "null"). [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("=> notnull")] - public static string ToStringOrNull(this T value, string nullText = "null") => value?.ToStringRepresentation() ?? nullText; + public static string ToStringOrNull(this T value, string nullText = "null") => + value?.ToStringRepresentation() ?? nullText; /// /// Returns the string representation of . This is done by calling . If the type of @@ -85,13 +121,15 @@ private static bool IsUnquotedType() /// /// The value whose string representation is requested. [ContractAnnotation("value:null => halt; value:notnull => notnull")] - public static string? ToStringRepresentation([NotNull, ValidatedNotNull] this T value) + public static string? ToStringRepresentation([NotNull] [ValidatedNotNull] this T value) { value.MustNotBeNullReference(nameof(value)); var content = value.ToString(); if (IsUnquotedType() || content.IsNullOrEmpty()) + { return content; + } // ReSharper disable UseIndexFromEndExpression -- not possible in netstandard2.0 if (content.Length <= 126) @@ -123,7 +161,12 @@ private static bool IsUnquotedType() [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendCollectionContent([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => + public static StringBuilder AppendCollectionContent( + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + [NotNull] [ValidatedNotNull] IEnumerable items, + string headerLine = "Content of the collection:", + bool finishWithNewLine = true + ) => stringBuilder.MustNotBeNull(nameof(stringBuilder)) .AppendLine(headerLine) .AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); @@ -139,7 +182,12 @@ public static StringBuilder AppendCollectionContent([NotNull, ValidatedNotNul /// Thrown when or is null. [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + public static StringBuilder AppendItems( + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + [NotNull] [ValidatedNotNull] IEnumerable items, + string itemSeparator = ", ", + string emptyCollectionText = "empty collection" + ) // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); @@ -148,15 +196,22 @@ public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this Stri var currentIndex = 0; var itemsCount = list.Count; if (itemsCount == 0) + { return stringBuilder.Append(emptyCollectionText); + } while (true) { stringBuilder.Append(list[currentIndex].ToStringOrNull()); if (currentIndex < itemsCount - 1) + { stringBuilder.Append(itemSeparator); + } else + { return stringBuilder; + } + ++currentIndex; } } @@ -172,7 +227,12 @@ public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this Stri [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItemsWithNewLine([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => + public static StringBuilder AppendItemsWithNewLine( + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + [NotNull] [ValidatedNotNull] IEnumerable items, + string emptyCollectionText = "empty collection", + bool finishWithNewLine = true + ) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText) .AppendLineIf(finishWithNewLine); // ReSharper restore RedundantNullableFlowAttribute @@ -186,11 +246,18 @@ public static StringBuilder AppendItemsWithNewLine([NotNull, ValidatedNotNull /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable once RedundantNullableFlowAttribute - public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) + public static StringBuilder AppendIf( + // ReSharper disable once RedundantNullableFlowAttribute + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + bool condition, + string value + ) { if (condition) + { stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); + } + return stringBuilder; } @@ -203,11 +270,18 @@ public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuil /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable once RedundantNullableFlowAttribute - public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") + public static StringBuilder AppendLineIf( + // ReSharper disable once RedundantNullableFlowAttribute + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + bool condition, + string value = "" + ) { if (condition) + { stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); + } + return stringBuilder; } @@ -217,7 +291,10 @@ public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this String /// /// Thrown when any parameter is null. // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] Exception exception) + public static StringBuilder AppendExceptionMessages( + [NotNull] [ValidatedNotNull] this StringBuilder stringBuilder, + [NotNull] [ValidatedNotNull] Exception exception + ) // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); @@ -228,7 +305,9 @@ public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] // ReSharper disable once PossibleNullReferenceException stringBuilder.AppendLine(exception.Message); if (exception.InnerException is null) + { return stringBuilder; + } stringBuilder.AppendLine(); exception = exception.InnerException; @@ -241,7 +320,7 @@ public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] /// /// Thrown when is null. // ReSharper disable once RedundantNullableFlowAttribute - public static string GetAllExceptionMessages([NotNull, ValidatedNotNull] this Exception exception) => + public static string GetAllExceptionMessages([NotNull] [ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// @@ -251,13 +330,19 @@ public static string GetAllExceptionMessages([NotNull, ValidatedNotNull] this Ex public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) { if (ReferenceEquals(x, y)) + { return true; + } if (x is null || y is null) + { return false; + } if (x.Length == 0) + { return y.Length == 0; + } var indexX = 0; var indexY = 0; @@ -271,7 +356,9 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { if (x[indexX++] != y[indexY++]) + { return false; + } } return wasXSuccessful == wasYSuccessful; @@ -284,13 +371,19 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, string? y) { if (ReferenceEquals(x, y)) + { return true; + } if (x is null || y is null) + { return false; + } if (x.Length == 0) + { return y.Length == 0; + } var indexX = 0; var indexY = 0; @@ -304,7 +397,9 @@ public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, strin (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { if (char.ToLowerInvariant(x[indexX++]) != char.ToLowerInvariant(y[indexY++])) + { return false; + } } return wasXSuccessful == wasYSuccessful; @@ -315,11 +410,13 @@ private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, while (currentIndex < @string.Length) { if (!char.IsWhiteSpace(@string[currentIndex])) + { return true; + } ++currentIndex; } return false; } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/OrdinalIgnoreCaseIgnoreWhiteSpaceComparer.cs b/Code/Light.GuardClauses/OrdinalIgnoreCaseIgnoreWhiteSpaceComparer.cs index 5ed7ed6..fc1f2d7 100644 --- a/Code/Light.GuardClauses/OrdinalIgnoreCaseIgnoreWhiteSpaceComparer.cs +++ b/Code/Light.GuardClauses/OrdinalIgnoreCaseIgnoreWhiteSpaceComparer.cs @@ -5,7 +5,7 @@ namespace Light.GuardClauses; /// -/// Represents an that compares strings using the +/// Represents an that compares strings using the /// ordinal sort rules, ignoring the case and the white space characters. /// public sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityComparer @@ -14,7 +14,7 @@ public sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityCompare /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the case and /// the white space of the provided strings. /// - /// Thrown when or are null. + /// Thrown when or are null. public bool Equals(string? x, string? y) { x.MustNotBeNull(nameof(x)); @@ -27,7 +27,7 @@ public bool Equals(string? x, string? y) /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters /// which are interpreted as case-insensitive. /// - /// Thrown when is null. + /// Thrown when is null. public int GetHashCode(string @string) { @string.MustNotBeNull(nameof(@string)); @@ -37,9 +37,11 @@ public int GetHashCode(string @string) foreach (var character in @string) { if (!character.IsWhiteSpace()) + { hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); + } } return hashBuilder.BuildHash(); } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/OrdinalIgnoreWhiteSpaceComparer.cs b/Code/Light.GuardClauses/OrdinalIgnoreWhiteSpaceComparer.cs index 07cb57c..7a150e6 100644 --- a/Code/Light.GuardClauses/OrdinalIgnoreWhiteSpaceComparer.cs +++ b/Code/Light.GuardClauses/OrdinalIgnoreWhiteSpaceComparer.cs @@ -5,7 +5,7 @@ namespace Light.GuardClauses; /// -/// Represents an that compares strings using the +/// Represents an that compares strings using the /// ordinal sort rules and ignoring the white space characters. /// public sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer @@ -14,7 +14,7 @@ public sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space /// of the provided strings. /// - /// Thrown when or are null. + /// Thrown when or are null. public bool Equals(string? x, string? y) { x.MustNotBeNull(nameof(x)); @@ -26,7 +26,7 @@ public bool Equals(string? x, string? y) /// /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. /// - /// Thrown when is null. + /// Thrown when is null. public int GetHashCode(string @string) { @string.MustNotBeNull(nameof(@string)); @@ -35,9 +35,11 @@ public int GetHashCode(string @string) foreach (var character in @string) { if (!character.IsWhiteSpace()) + { hashCodeBuilder.CombineIntoHash(character); + } } return hashCodeBuilder.BuildHash(); } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/Range.cs b/Code/Light.GuardClauses/Range.cs index 623df25..8ef1f30 100644 --- a/Code/Light.GuardClauses/Range.cs +++ b/Code/Light.GuardClauses/Range.cs @@ -1,8 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; -using Light.GuardClauses.FrameworkExtensions; using System.Runtime.CompilerServices; +using Light.GuardClauses.FrameworkExtensions; namespace Light.GuardClauses; @@ -62,7 +62,7 @@ public Range(T from, T to, bool isFromInclusive = true, bool isToInclusive = tru /// True if value is within range, otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsValueWithinRange(T value) => - value.MustNotBeNullReference(nameof(value)).CompareTo(From) >= _expectedLowerBoundaryResult && + value.MustNotBeNullReference(nameof(value)).CompareTo(From) >= _expectedLowerBoundaryResult && value.CompareTo(To) <= _expectedUpperBoundaryResult; /// @@ -72,7 +72,7 @@ public bool IsValueWithinRange(T value) => /// The value that indicates the inclusive lower boundary of the resulting range. /// A value you can use to fluently define the upper boundary of a new range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RangeFromInfo FromInclusive(T value) => new RangeFromInfo(value, true); + public static RangeFromInfo FromInclusive(T value) => new (value, true); /// /// Use this method to create a range in a fluent style using method chaining. @@ -81,7 +81,7 @@ public bool IsValueWithinRange(T value) => /// The value that indicates the exclusive lower boundary of the resulting range. /// A value you can use to fluently define the upper boundary of a new range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RangeFromInfo FromExclusive(T value) => new RangeFromInfo(value, false); + public static RangeFromInfo FromExclusive(T value) => new (value, false); /// /// The nested can be used to fluently create a . @@ -113,7 +113,7 @@ public RangeFromInfo(T from, bool isFromInclusive) /// Thrown when is less than the lower boundary value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range ToExclusive(T value) => new Range(_from, value, _isFromInclusive, false); + public Range ToExclusive(T value) => new (_from, value, _isFromInclusive, false); /// /// Use this method to create a range in a fluent style using method chaining. @@ -125,15 +125,14 @@ public RangeFromInfo(T from, bool isFromInclusive) /// Thrown when is less than the lower boundary value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range ToInclusive(T value) => new Range(_from, value, _isFromInclusive); + public Range ToInclusive(T value) => new (_from, value, _isFromInclusive); } /// - public override string ToString() => - $"Range from {CreateRangeDescriptionText()}"; + public override string ToString() => $"Range from {CreateRangeDescriptionText()}"; /// - /// Returns either "inclusive" or "exclusive", depending on whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string LowerBoundaryText { @@ -142,7 +141,7 @@ public string LowerBoundaryText } /// - /// Returns either "inclusive" or "exclusive", depending on whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string UpperBoundaryText { @@ -164,7 +163,10 @@ public bool Equals(Range other) { if (IsFromInclusive != other.IsFromInclusive || IsToInclusive != other.IsToInclusive) + { return false; + } + var comparer = EqualityComparer.Default; return comparer.Equals(From, other.From) && comparer.Equals(To, other.To); @@ -173,7 +175,11 @@ public bool Equals(Range other) /// public override bool Equals(object? other) { - if (other is null) return false; + if (other is null) + { + return false; + } + return other is Range range && Equals(range); } @@ -194,7 +200,7 @@ public override bool Equals(object? other) } /// -/// Provides methods to simplify the creation of instances. +/// Provides methods to simplify the creation of instances. /// public static class Range { @@ -243,11 +249,11 @@ public static class Range /// The count of this enumerable will be used to create the index range. Please ensure that this enumerable /// is actually a collection, not a lazy enumerable. /// - /// Thrown when is null. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(IEnumerable enumerable) => - new (0, enumerable.Count(), isFromInclusive: true, isToInclusive: false); - + new (0, enumerable.Count(), true, false); + /// /// Creates a range for the specified enumerable that encompasses all valid indexes. /// @@ -255,10 +261,10 @@ public static Range For(IEnumerable enumerable) => /// The count of this enumerable will be used to create the index range. Please ensure that this enumerable /// is actually a collection, not a lazy enumerable. /// - /// Thrown when is null. + /// Thrown when is null. public static Range For(IEnumerable enumerable) => - new (0, enumerable.GetCount(), isFromInclusive: true, isToInclusive: false); - + new (0, enumerable.GetCount(), true, false); + /// /// Creates a range for the specified span that encompasses all valid indexes. /// @@ -267,8 +273,8 @@ public static Range For(IEnumerable enumerable) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(ReadOnlySpan span) => - new (0, span.Length, isFromInclusive: true, isToInclusive: false); - + new (0, span.Length, true, false); + /// /// Creates a range for the specified span that encompasses all valid indexes. /// @@ -277,7 +283,7 @@ public static Range For(ReadOnlySpan span) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(Span span) => - new (0 , span.Length, isFromInclusive: true, isToInclusive: false); + new (0, span.Length, true, false); /// /// Creates a range for the specified memory that encompasses all valid indexes. @@ -287,8 +293,8 @@ public static Range For(Span span) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(Memory memory) => - new (0, memory.Length, isFromInclusive: true, isToInclusive: false); - + new (0, memory.Length, true, false); + /// /// Creates a range for the specified memory that encompasses all valid indexes. /// @@ -297,8 +303,8 @@ public static Range For(Memory memory) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(ReadOnlyMemory memory) => - new (0, memory.Length, isFromInclusive: true, isToInclusive: false); - + new (0, memory.Length, true, false); + /// /// Creates a range for the specified memory that encompasses all valid indexes. /// @@ -307,5 +313,5 @@ public static Range For(ReadOnlyMemory memory) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(ArraySegment segment) => - new (0, segment.Count, isFromInclusive: true, isToInclusive: false); + new (0, segment.Count, true, false); } diff --git a/Code/Light.GuardClauses/RegularExpressions.cs b/Code/Light.GuardClauses/RegularExpressions.cs index 62ff37a..0a40137 100644 --- a/Code/Light.GuardClauses/RegularExpressions.cs +++ b/Code/Light.GuardClauses/RegularExpressions.cs @@ -12,7 +12,8 @@ public static class RegularExpressions /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. /// - public static readonly Regex EmailRegex = new Regex( + public static readonly Regex EmailRegex = new ( @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$", - RegexOptions.CultureInvariant | RegexOptions.ECMAScript); -} \ No newline at end of file + RegexOptions.CultureInvariant | RegexOptions.ECMAScript + ); +} diff --git a/Code/Light.GuardClauses/SpanDelegates.cs b/Code/Light.GuardClauses/SpanDelegates.cs index 2e3e7d5..68b1776 100644 --- a/Code/Light.GuardClauses/SpanDelegates.cs +++ b/Code/Light.GuardClauses/SpanDelegates.cs @@ -10,4 +10,4 @@ namespace Light.GuardClauses; /// /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. /// -public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); \ No newline at end of file +public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); diff --git a/Code/Light.GuardClauses/StringComparisonType.cs b/Code/Light.GuardClauses/StringComparisonType.cs index dd2c84a..93768c8 100644 --- a/Code/Light.GuardClauses/StringComparisonType.cs +++ b/Code/Light.GuardClauses/StringComparisonType.cs @@ -4,9 +4,9 @@ /// Specifies the culture, case , and sort rules when comparing strings. /// /// -/// This enum is en extension of , adding +/// This enum is en extension of , adding /// capabilities to ignore white space when making string equality comparisons. -/// See the when +/// See the when /// you want to compare in such a way. /// public enum StringComparisonType From 0f52dd52b05df7513523d13489167dc316ecdab2 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 28 Dec 2024 20:45:02 +0100 Subject: [PATCH 082/123] fix: update copyright year in LICENSE file Removes the current year from the LICENSE file --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 4020006..e3ea1a2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 - 2023 Kenny Pflug mailto:kenny.pflug@live.de +Copyright (c) 2016 Kenny Pflug mailto:kenny.pflug@live.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 67de246bb806d9ed5aba8bfad05625925b14586a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 1 Mar 2025 01:16:51 +0100 Subject: [PATCH 083/123] feat: add IsEmptyOrWhiteSpace for Span Signed-off-by: Kenny Pflug --- .../IsEmptyOrWhiteSpaceTests.cs | 36 +++++++++++++++++++ .../Check.IsEmptyOrWhiteSpace.cs | 30 ++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs create mode 100644 Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs new file mode 100644 index 0000000..04d8a0d --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs @@ -0,0 +1,36 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class CheckIsEmptyOrWhiteSpaceTests +{ + [Fact] + public static void EmptySpan() + { + var span = new Span([]); + span.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void WhiteSpaceSpan() + { + var span = new Span([' ', '\t', '\n']); + span.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void NonWhiteSpaceSpan() + { + var span = new Span(['a', 'b', 'c']); + span.IsEmptyOrWhiteSpace().Should().BeFalse(); + } + + [Fact] + public static void MixedSpan() + { + var span = new Span([' ', 'a', '\t']); + span.IsEmptyOrWhiteSpace().Should().BeFalse(); + } +} diff --git a/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs new file mode 100644 index 0000000..56efe90 --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified span is empty or contains only white space characters. + /// + /// The span to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this Span span) + { + if (span.IsEmpty) + { + return true; + } + + foreach (var character in span) + { + if (!character.IsWhiteSpace()) + { + return false; + } + } + + return true; + } +} From 32a7aa9eebf1ceb0bdbcdcbe224669e674e95048 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 1 Mar 2025 01:38:13 +0100 Subject: [PATCH 084/123] feat: add IsEmptyOrWhiteSpace for ReadOnlySpan Signed-off-by: Kenny Pflug --- .../IsEmptyOrWhiteSpaceTests.cs | 36 ++++++++++++++++--- .../Check.IsEmptyOrWhiteSpace.cs | 9 ++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs index 04d8a0d..522103d 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs @@ -4,7 +4,7 @@ namespace Light.GuardClauses.Tests.StringAssertions; -public static class CheckIsEmptyOrWhiteSpaceTests +public static class IsEmptyOrWhiteSpaceTests { [Fact] public static void EmptySpan() @@ -16,21 +16,49 @@ public static void EmptySpan() [Fact] public static void WhiteSpaceSpan() { - var span = new Span([' ', '\t', '\n']); + var span = new Span(" \t\n".ToCharArray()); span.IsEmptyOrWhiteSpace().Should().BeTrue(); } [Fact] public static void NonWhiteSpaceSpan() { - var span = new Span(['a', 'b', 'c']); + var span = new Span("abc".ToCharArray()); span.IsEmptyOrWhiteSpace().Should().BeFalse(); } [Fact] public static void MixedSpan() { - var span = new Span([' ', 'a', '\t']); + var span = new Span(" a\t".ToCharArray()); span.IsEmptyOrWhiteSpace().Should().BeFalse(); } + + [Fact] + public static void EmptyReadOnlySpan() + { + var readOnlySpan = string.Empty.AsSpan(); + readOnlySpan.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void WhiteSpaceReadOnlySpan() + { + var readOnlySpan = " \t\n".AsSpan(); + readOnlySpan.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void NonWhiteSpaceReadOnlySpan() + { + var readOnlySpan = "abc".AsSpan(); + readOnlySpan.IsEmptyOrWhiteSpace().Should().BeFalse(); + } + + [Fact] + public static void MixedReadOnlySpan() + { + var readOnlySpan = " a\t".AsSpan(); + readOnlySpan.IsEmptyOrWhiteSpace().Should().BeFalse(); + } } diff --git a/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs index 56efe90..24740d0 100644 --- a/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs +++ b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs @@ -10,7 +10,14 @@ public static partial class Check /// /// The span to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this Span span) + public static bool IsEmptyOrWhiteSpace(this Span span) => ((ReadOnlySpan) span).IsEmptyOrWhiteSpace(); + + /// + /// Checks if the specified span is empty or contains only white space characters. + /// + /// The span to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) { if (span.IsEmpty) { From e8db798896997ddb0be330e902a4c2751e89759c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 1 Mar 2025 01:54:01 +0100 Subject: [PATCH 085/123] feat: add IsEmptyOrWhiteSpace for Memory and ReadOnlyMemory Signed-off-by: Kenny Pflug --- .../IsEmptyOrWhiteSpaceTests.cs | 56 +++++++++++++++++++ .../Check.IsEmptyOrWhiteSpace.cs | 14 +++++ 2 files changed, 70 insertions(+) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs index 522103d..2048883 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmptyOrWhiteSpaceTests.cs @@ -61,4 +61,60 @@ public static void MixedReadOnlySpan() var readOnlySpan = " a\t".AsSpan(); readOnlySpan.IsEmptyOrWhiteSpace().Should().BeFalse(); } + + [Fact] + public static void EmptyMemory() + { + var memory = new Memory([]); + memory.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void WhiteSpaceMemory() + { + var memory = new Memory(" \t\n".ToCharArray()); + memory.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void NonWhiteSpaceMemory() + { + var memory = new Memory("abc".ToCharArray()); + memory.IsEmptyOrWhiteSpace().Should().BeFalse(); + } + + [Fact] + public static void MixedMemory() + { + var memory = new Memory(" a\t".ToCharArray()); + memory.IsEmptyOrWhiteSpace().Should().BeFalse(); + } + + [Fact] + public static void EmptyReadOnlyMemory() + { + var readOnlyMemory = new ReadOnlyMemory([]); + readOnlyMemory.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void WhiteSpaceReadOnlyMemory() + { + var readOnlyMemory = new ReadOnlyMemory(" \t\n".ToCharArray()); + readOnlyMemory.IsEmptyOrWhiteSpace().Should().BeTrue(); + } + + [Fact] + public static void NonWhiteSpaceReadOnlyMemory() + { + var readOnlyMemory = new ReadOnlyMemory("abc".ToCharArray()); + readOnlyMemory.IsEmptyOrWhiteSpace().Should().BeFalse(); + } + + [Fact] + public static void MixedReadOnlyMemory() + { + var readOnlyMemory = new ReadOnlyMemory(" a\t".ToCharArray()); + readOnlyMemory.IsEmptyOrWhiteSpace().Should().BeFalse(); + } } diff --git a/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs index 24740d0..580c677 100644 --- a/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs +++ b/Code/Light.GuardClauses/Check.IsEmptyOrWhiteSpace.cs @@ -34,4 +34,18 @@ public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) return true; } + + /// + /// Checks if the specified memory is empty or contains only white space characters. + /// + /// The memory to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this Memory memory) => memory.Span.IsEmptyOrWhiteSpace(); + + /// + /// Checks if the specified memory is empty or contains only white space characters. + /// + /// The memory to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); } From ed3a0f78ff672bc08d07c231f52c00f5603d8438 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 1 Mar 2025 02:39:52 +0100 Subject: [PATCH 086/123] feat: add IsFileExtension Signed-off-by: Kenny Pflug --- .../StringAssertions/IsFileExtensionTests.cs | 38 ++++++++++++++++++ .../Check.IsFileExtension.cs | 40 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs create mode 100644 Code/Light.GuardClauses/Check.IsFileExtension.cs diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs new file mode 100644 index 0000000..7226b38 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class IsFileExtensionTests +{ + [Fact] + public static void Null() => + // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- we need to test this + ((string) null).IsFileExtension().Should().BeFalse(); + + [Fact] + public static void Empty() => + string.Empty.IsFileExtension().Should().BeFalse(); + + [Theory] + [InlineData(".txt")] + [InlineData(".tar.gz")] + [InlineData(".docx")] + [InlineData(".config")] + public static void ValidFileExtensions(string extension) => + extension.IsFileExtension().Should().BeTrue(); + + [Theory] + [InlineData("txt")] // No leading period + [InlineData(".")] // Just a period + [InlineData(".txt!")] // Invalid character + [InlineData(".txt ")] // Contains space + [InlineData(".doc/")] // Invalid character + [InlineData("..")] // Just periods + [InlineData("...")] + [InlineData("....")] + [InlineData(".txt.")] // Invalid - ends with period + [InlineData(".docx.")] + public static void InvalidFileExtensions(string extension) => + extension.IsFileExtension().Should().BeFalse(); +} diff --git a/Code/Light.GuardClauses/Check.IsFileExtension.cs b/Code/Light.GuardClauses/Check.IsFileExtension.cs new file mode 100644 index 0000000..a69c0eb --- /dev/null +++ b/Code/Light.GuardClauses/Check.IsFileExtension.cs @@ -0,0 +1,40 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Checks if the specified string represents a valid file extension. + /// + /// + /// The string to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the string is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension([NotNullWhen(true)] this string? value) + { + if (value is not { Length: > 1 } || value[0] != '.' || value[value.Length - 1] == '.') + { + return false; + } + + var hasAlphanumeric = false; + for (var i = 1; i < value.Length; i++) + { + var c = value[i]; + if (c.IsLetterOrDigit()) + { + hasAlphanumeric = true; + } + else if (c != '.') + { + return false; + } + } + + return hasAlphanumeric; + } +} From 3c8e247703298f73b7bc7ff3fa0a2100fad234a8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 06:21:59 +0100 Subject: [PATCH 087/123] refactor: rename c to character in IsFileExtension Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsFileExtension.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Light.GuardClauses/Check.IsFileExtension.cs b/Code/Light.GuardClauses/Check.IsFileExtension.cs index a69c0eb..ffae491 100644 --- a/Code/Light.GuardClauses/Check.IsFileExtension.cs +++ b/Code/Light.GuardClauses/Check.IsFileExtension.cs @@ -24,12 +24,12 @@ public static bool IsFileExtension([NotNullWhen(true)] this string? value) var hasAlphanumeric = false; for (var i = 1; i < value.Length; i++) { - var c = value[i]; - if (c.IsLetterOrDigit()) + var character = value[i]; + if (character.IsLetterOrDigit()) { hasAlphanumeric = true; } - else if (c != '.') + else if (character != '.') { return false; } From 88663bcc8523dcb751bf877ef35e74ae681b8aaa Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 06:54:08 +0100 Subject: [PATCH 088/123] feat: add IsFileExtension overloads for Span and Memory Signed-off-by: Kenny Pflug --- .../StringAssertions/IsFileExtensionTests.cs | 104 +++++++++++++----- .../Check.IsFileExtension.cs | 50 ++++++++- 2 files changed, 126 insertions(+), 28 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs index 7226b38..ae1bc19 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsFileExtensionTests.cs @@ -1,38 +1,90 @@ using FluentAssertions; +using System; using Xunit; namespace Light.GuardClauses.Tests.StringAssertions; public static class IsFileExtensionTests { - [Fact] - public static void Null() => - // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- we need to test this - ((string) null).IsFileExtension().Should().BeFalse(); - - [Fact] - public static void Empty() => - string.Empty.IsFileExtension().Should().BeFalse(); - [Theory] - [InlineData(".txt")] - [InlineData(".tar.gz")] - [InlineData(".docx")] - [InlineData(".config")] - public static void ValidFileExtensions(string extension) => + [MemberData(nameof(ValidFileExtensionData))] + public static void ValidFileExtensions_String(string extension) => extension.IsFileExtension().Should().BeTrue(); + + [Theory] + [MemberData(nameof(ValidFileExtensionData))] + public static void ValidFileExtensions_Span(string extension) + { + var span = extension.ToCharArray().AsSpan(); + span.IsFileExtension().Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(ValidFileExtensionData))] + public static void ValidFileExtensions_Memory(string extension) + { + var memory = extension.ToCharArray().AsMemory(); + memory.IsFileExtension().Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(ValidFileExtensionData))] + public static void ValidFileExtensions_ReadOnlyMemory(string extension) + { + var readOnlyMemory = extension.AsMemory(); + readOnlyMemory.IsFileExtension().Should().BeTrue(); + } [Theory] - [InlineData("txt")] // No leading period - [InlineData(".")] // Just a period - [InlineData(".txt!")] // Invalid character - [InlineData(".txt ")] // Contains space - [InlineData(".doc/")] // Invalid character - [InlineData("..")] // Just periods - [InlineData("...")] - [InlineData("....")] - [InlineData(".txt.")] // Invalid - ends with period - [InlineData(".docx.")] - public static void InvalidFileExtensions(string extension) => + [MemberData(nameof(InvalidFileExtensionData))] + public static void InvalidFileExtensions_String(string extension) => extension.IsFileExtension().Should().BeFalse(); -} + + [Theory] + [MemberData(nameof(InvalidFileExtensionData))] + public static void InvalidFileExtensions_Span(string extension) + { + var span = extension?.ToCharArray() ?? Span.Empty; + span.IsFileExtension().Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionData))] + public static void InvalidFileExtensions_Memory(string extension) + { + var memory = extension?.ToCharArray() ?? Memory.Empty; + memory.IsFileExtension().Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionData))] + public static void InvalidFileExtensions_ReadOnlyMemory(string extension) + { + var readOnlyMemory = extension?.ToCharArray() ?? ReadOnlyMemory.Empty; + readOnlyMemory.IsFileExtension().Should().BeFalse(); + } + + public static TheoryData ValidFileExtensionData() => + [ + ".txt", + ".tar.gz", + ".docx", + ".config", + ]; + + public static TheoryData InvalidFileExtensionData() => + [ + null, + string.Empty, + "txt", // No leading period + ".", // Just a period + ".txt!", // Invalid character + ".txt ", // Contains space + ".doc/", // Invalid character + "..", // Just periods + "...", + "....", + ".txt.", // Invalid - ends with period + ".docx.", + ]; +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.IsFileExtension.cs b/Code/Light.GuardClauses/Check.IsFileExtension.cs index ffae491..d9c76cc 100644 --- a/Code/Light.GuardClauses/Check.IsFileExtension.cs +++ b/Code/Light.GuardClauses/Check.IsFileExtension.cs @@ -1,3 +1,4 @@ +using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -14,9 +15,54 @@ public static partial class Check /// /// True if the string is a valid file extension, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension([NotNullWhen(true)] this string? value) + public static bool IsFileExtension([NotNullWhen(true)] this string? value) => + value != null && IsFileExtension(value.AsSpan()); + + /// + /// Checks if the specified character span represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this Span value) => IsFileExtension((ReadOnlySpan) value); + + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlyMemory value) => IsFileExtension(value.Span); + + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this Memory value) => IsFileExtension(value.Span); + + /// + /// Checks if the specified character span represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlySpan value) { - if (value is not { Length: > 1 } || value[0] != '.' || value[value.Length - 1] == '.') + if (value.Length <= 1 || value[0] != '.' || value[value.Length - 1] == '.') { return false; } From 9ce91ea09e5f3093b5be8f1dfdd0396926635456 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 07:20:06 +0100 Subject: [PATCH 089/123] feat: optimize email regex For .NET 8, we use GeneratedRegex. For .NET Standard, we use RegexOptions.Compiled. Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/RegularExpressions.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Code/Light.GuardClauses/RegularExpressions.cs b/Code/Light.GuardClauses/RegularExpressions.cs index 0a40137..3fec571 100644 --- a/Code/Light.GuardClauses/RegularExpressions.cs +++ b/Code/Light.GuardClauses/RegularExpressions.cs @@ -5,15 +5,30 @@ namespace Light.GuardClauses; /// /// Provides regular expressions that are used in string assertions. /// +#if NET8_0 +public static partial class RegularExpressions +#else public static class RegularExpressions +#endif { + /// + /// Gets the string that represents the . + /// + public const string EmailRegexText = + @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$"; + /// /// Gets the default regular expression for email validation. /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. /// - public static readonly Regex EmailRegex = new ( - @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$", - RegexOptions.CultureInvariant | RegexOptions.ECMAScript - ); + public static readonly Regex EmailRegex = +#if NET8_0 + GenerateEmailRegex(); + + [GeneratedRegex(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant)] + private static partial Regex GenerateEmailRegex(); +#else + new (EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); +#endif } From 64a822168d20b38ef343b06d62a86c79130a3ce7 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 08:04:52 +0100 Subject: [PATCH 090/123] feat: add MustBeFileExtension for strings Signed-off-by: Kenny Pflug --- .../MustBeFileExtensionTests.cs | 77 +++++++++++++++++++ .../Check.MustBeFileExtension.cs | 56 ++++++++++++++ Code/Light.GuardClauses/Exceptions/Throw.cs | 10 ++- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeFileExtension.cs diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs new file mode 100644 index 0000000..bba0446 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -0,0 +1,77 @@ +using System; +using FluentAssertions; +using Light.GuardClauses.FrameworkExtensions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class MustBeFileExtensionTests +{ + public static readonly TheoryData InvalidFileExtensionsData = + [ + "txt", + ".jpg/", + ".", + "..", + "...", + "....", + ".docx.", + ]; + + [Theory] + [InlineData(".txt")] + [InlineData(".jpg")] + [InlineData(".tar.gz")] + public static void ValidFileExtensions(string input) => + input.MustBeFileExtension().Should().BeSameAs(input); + + [Fact] + public static void StringIsNull() + { + var nullString = default(string); + + // ReSharper disable once ExpressionIsAlwaysNull + var act = () => nullString.MustBeFileExtension(nameof(nullString)); + + act.Should().Throw() + .WithParameterName(nameof(nullString)); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void InvalidFileExtensions(string invalidString) + { + var act = () => invalidString.MustBeFileExtension(nameof(invalidString)); + + act.Should().Throw() + .And.Message.Should().Contain( + $"invalidString must be a valid file extension, but it actually is {invalidString.ToStringOrNull()}" + ); + } + + [Fact] + public static void CustomExceptionStringNull() => + Test.CustomException( + default(string), + (@null, exceptionFactory) => @null.MustBeFileExtension(exceptionFactory) + ); + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void CustomExceptionInvalidFileExtensions(string invalidString) => + Test.CustomException( + invalidString, + (@string, exceptionFactory) => @string.MustBeFileExtension(exceptionFactory) + ); + + [Fact] + public static void CallerArgumentExpression() + { + const string invalidString = "txt"; + + var act = () => invalidString.MustBeFileExtension(); + + act.Should().Throw() + .WithParameterName(nameof(invalidString)); + } +} diff --git a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs new file mode 100644 index 0000000..ce26b6b --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the string is a valid file extension, or otherwise throws an . + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not a valid file extension. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeFileExtension( + [NotNull] [ValidatedNotNull] this string? parameter, + [CallerArgumentExpression("parameter")] + string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) + { + Throw.NotFileExtension(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a valid file extension, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not a valid file extension. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeFileExtension( + [NotNull] [ValidatedNotNull] this string? parameter, + Func exceptionFactory + ) + { + if (parameter is null || !parameter.IsFileExtension()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +} diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs index 5b3bbf9..9a7566f 100644 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ b/Code/Light.GuardClauses/Exceptions/Throw.cs @@ -381,6 +381,14 @@ public static void NotTrimmedAtStart(string? parameter, string? parameterName, s public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(string? parameter, string? parameterName, string? message) => + throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); + /// /// Throws the default indicating that two values are not equal, using the optional parameter name and message. /// @@ -667,6 +675,4 @@ public static void CustomSpanException(SpanExceptionFactory [DoesNotReturn] public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); - - } \ No newline at end of file From 03b3c502a80a84db38c87f2e282a897c86060c8f Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 09:56:38 +0100 Subject: [PATCH 091/123] chore: update ReSharper team-shared settings Signed-off-by: Kenny Pflug --- ...ht.GuardClauses.AllProjects.sln.DotSettings | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Code/Light.GuardClauses.AllProjects.sln.DotSettings b/Code/Light.GuardClauses.AllProjects.sln.DotSettings index aceafc4..ac545e2 100644 --- a/Code/Light.GuardClauses.AllProjects.sln.DotSettings +++ b/Code/Light.GuardClauses.AllProjects.sln.DotSettings @@ -2,17 +2,24 @@ True None False + HINT + SUGGESTION + HINT + SUGGESTION + SUGGESTION + HINT WARNING WARNING DO_NOT_SHOW - HINT + DO_NOT_SHOW HINT WARNING + DO_NOT_SHOW HINT WARNING True <?xml version="1.0" encoding="utf-16"?><Profile name="Kenny's Kleanup"><CSReorderTypeMembers>True</CSReorderTypeMembers><CSUpdateFileHeader>True</CSUpdateFileHeader><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CppReformatCode>True</CppReformatCode><XMLReformatCode>True</XMLReformatCode><CssReformatCode>True</CssReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" ArrangeVarStyle="False" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><RemoveCodeRedundancies>True</RemoveCodeRedundancies><HtmlReformatCode>True</HtmlReformatCode><JsInsertSemicolon>True</JsInsertSemicolon><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor></Profile> - Kenny's Kleanup + Built-in: Full Cleanup Built-in: Full Cleanup False Required @@ -40,6 +47,9 @@ True True TOGETHER_SAME_LINE + True + True + True NO_INDENT True True @@ -50,8 +60,9 @@ True False True + 90 1 - 3 + 10000 COMPACT True True @@ -77,6 +88,7 @@ CHOP_IF_LONG False True + True True True True From 1b66e61c667d5c224f1e94c99a3d39a9fdc6bf0d Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 09:57:12 +0100 Subject: [PATCH 092/123] feat: add MustBeFileExtension for Span and Memory Signed-off-by: Kenny Pflug --- .../Check.MustBeFileExtension.cs | 87 ++++++++++++++++++- Code/Light.GuardClauses/Exceptions/Throw.cs | 8 ++ 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs index ce26b6b..e1b8929 100644 --- a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs +++ b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs @@ -9,19 +9,18 @@ namespace Light.GuardClauses; public static partial class Check { /// - /// Ensures that the string is a valid file extension, or otherwise throws an . + /// Ensures that the string is a valid file extension, or otherwise throws an . /// /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not a valid file extension. + /// Thrown when is not a valid file extension. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] public static string MustBeFileExtension( [NotNull] [ValidatedNotNull] this string? parameter, - [CallerArgumentExpression("parameter")] - string? parameterName = null, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null ) { @@ -53,4 +52,84 @@ public static string MustBeFileExtension( return parameter; } + + /// + /// Ensures that the character span is a valid file extension, or otherwise throws a . + /// + /// The character span to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original character span. + /// Thrown when is not a valid file extension. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeFileExtension( + this Span parameter, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter).MustBeFileExtension(parameterName, message); + return parameter; + } + + /// + /// Ensures that the character memory is a valid file extension, or otherwise throws a . + /// + /// The character memory to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original character memory. + /// Thrown when is not a valid file extension. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustBeFileExtension( + this Memory parameter, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter.Span).MustBeFileExtension(parameterName, message); + return parameter; + } + + /// + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . + /// + /// The read-only character memory to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original read-only character memory. + /// Thrown when is not a valid file extension. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeFileExtension( + this ReadOnlyMemory parameter, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + parameter.Span.MustBeFileExtension(parameterName, message); + return parameter; + } + + /// + /// Ensures that the read-only character span is a valid file extension, or otherwise throws a . + /// + /// The read-only character span to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original read-only character span. + /// Thrown when is not a valid file extension. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeFileExtension( + this ReadOnlySpan parameter, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsFileExtension()) + { + Throw.NotFileExtension(parameter, parameterName, message); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs index 9a7566f..c0ce61c 100644 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ b/Code/Light.GuardClauses/Exceptions/Throw.cs @@ -388,6 +388,14 @@ public static void NotTrimmedAtEnd(string? parameter, string? parameterName, str [DoesNotReturn] public static void NotFileExtension(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); + + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => + throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}."); /// /// Throws the default indicating that two values are not equal, using the optional parameter name and message. From 21b10eec4382b9790f6dced2cdd8c8ac331cc5ee Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:11:01 +0100 Subject: [PATCH 093/123] feat: add custom exception factory overloads for MustBeFileExtension for Span and Memory Signed-off-by: Kenny Pflug --- .../Check.MustBeFileExtension.cs | 76 ++++++++++++++++++- Code/Light.GuardClauses/Exceptions/Throw.cs | 12 ++- Code/Light.GuardClauses/SpanDelegates.cs | 7 +- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs index e1b8929..1b3834f 100644 --- a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs +++ b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs @@ -19,7 +19,7 @@ public static partial class Check [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] public static string MustBeFileExtension( - [NotNull] [ValidatedNotNull] this string? parameter, + [NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null ) @@ -41,7 +41,7 @@ public static string MustBeFileExtension( [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] public static string MustBeFileExtension( - [NotNull] [ValidatedNotNull] this string? parameter, + [NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory ) { @@ -68,7 +68,23 @@ public static Span MustBeFileExtension( string? message = null ) { - ((ReadOnlySpan) parameter).MustBeFileExtension(parameterName, message); + ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); + return parameter; + } + + /// + /// Ensures that the character span is a valid file extension, or otherwise throws your custom exception. + /// + /// The character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character span. + /// Your custom exception thrown when is not a valid file extension. + public static Span MustBeFileExtension( + this Span parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan)parameter).MustBeFileExtension(exceptionFactory); return parameter; } @@ -87,7 +103,23 @@ public static Memory MustBeFileExtension( string? message = null ) { - ((ReadOnlySpan) parameter.Span).MustBeFileExtension(parameterName, message); + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); + return parameter; + } + + /// + /// Ensures that the character memory is a valid file extension, or otherwise throws your custom exception. + /// + /// The character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character memory. + /// Your custom exception thrown when is not a valid file extension. + public static Memory MustBeFileExtension( + this Memory parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(exceptionFactory); return parameter; } @@ -110,6 +142,22 @@ public static ReadOnlyMemory MustBeFileExtension( return parameter; } + /// + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws your custom exception. + /// + /// The read-only character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character memory. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlyMemory MustBeFileExtension( + this ReadOnlyMemory parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + parameter.Span.MustBeFileExtension(exceptionFactory); + return parameter; + } + /// /// Ensures that the read-only character span is a valid file extension, or otherwise throws a . /// @@ -132,4 +180,24 @@ public static ReadOnlySpan MustBeFileExtension( return parameter; } + + /// + /// Ensures that the read-only character span is a valid file extension, or otherwise throws your custom exception. + /// + /// The read-only character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character span. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlySpan MustBeFileExtension( + this ReadOnlySpan parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (!parameter.IsFileExtension()) + { + Throw.CustomSpanException(exceptionFactory, parameter); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs index c0ce61c..7df3649 100644 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ b/Code/Light.GuardClauses/Exceptions/Throw.cs @@ -674,8 +674,16 @@ public static void CustomException(Func excep [ContractAnnotation("=> halt")] [DoesNotReturn] public static void CustomSpanException(SpanExceptionFactory exceptionFactory, in Span span, T value) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); - + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); + + /// + /// Throws the exception that is returned by . is passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); + /// /// Throws the exception that is returned by . and are passed to . /// diff --git a/Code/Light.GuardClauses/SpanDelegates.cs b/Code/Light.GuardClauses/SpanDelegates.cs index 68b1776..0d4c018 100644 --- a/Code/Light.GuardClauses/SpanDelegates.cs +++ b/Code/Light.GuardClauses/SpanDelegates.cs @@ -7,7 +7,12 @@ namespace Light.GuardClauses; /// public delegate Exception SpanExceptionFactory(Span span, T value); +/// +/// Represents a delegate that receives a read-only span and produces an exception. +/// +public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span); + /// /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. /// -public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); +public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); \ No newline at end of file From 47e175708f08af65b42c0ed83a1af83406952d9d Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:29:03 +0100 Subject: [PATCH 094/123] chore: remove obsolete curly braces in Test.ExceptionDummy Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses.Tests/Test.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Light.GuardClauses.Tests/Test.cs b/Code/Light.GuardClauses.Tests/Test.cs index 6ee8957..9487c42 100644 --- a/Code/Light.GuardClauses.Tests/Test.cs +++ b/Code/Light.GuardClauses.Tests/Test.cs @@ -115,7 +115,7 @@ public static void CustomMessage(Action executeAssertion) wh } } - private sealed class ExceptionDummy : Exception { }; + private sealed class ExceptionDummy : Exception; public static void WriteExceptionTo(this ExceptionAssertions exceptionAssertions, ITestOutputHelper output) where T : Exception => output.WriteLine(exceptionAssertions.Which.ToString()); From d55acce4a9dceb5c9c1b8e195f7c75278b8ceafe Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:30:25 +0100 Subject: [PATCH 095/123] tests: add MustBeFileExtension tests for ReadOnlySpan Signed-off-by: Kenny Pflug --- .../MustBeFileExtensionTests.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs index bba0446..0a71c55 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -1,5 +1,6 @@ using System; using FluentAssertions; +using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using Xunit; @@ -74,4 +75,63 @@ public static void CallerArgumentExpression() act.Should().Throw() .WithParameterName(nameof(invalidString)); } + + [Theory] + [InlineData(".txt")] + [InlineData(".jpg")] + [InlineData(".tar.gz")] + public static void ValidFileExtensions_Span(string input) + { + var span = input.AsSpan(); + + var result = span.MustBeFileExtension(); + + result.ToString().Should().Be(input); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void InvalidFileExtensions_Span(string invalidString) + { + var act = () => + { + var span = new ReadOnlySpan(invalidString?.ToCharArray() ?? []); + span.MustBeFileExtension("parameterName"); + }; + + act.Should().Throw() + .And.Message.Should().Contain( + $"parameterName must be a valid file extension, but it actually is {invalidString}" + ); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void CustomExceptionInvalidFileExtensions_Span(string invalidString) + { + var exception = new Exception(); + + var act = () => + { + var span = new ReadOnlySpan(invalidString?.ToCharArray() ?? []); + span.MustBeFileExtension(_ => exception); + }; + + act.Should().Throw().Which.Should().BeSameAs(exception); + } + + [Fact] + public static void CallerArgumentExpression_Span() + { + // Act & Assert + var act = () => + { + var invalidSpan = "txt".AsSpan(); + invalidSpan.MustBeFileExtension(); + }; + + // Should throw with parameter name matching the variable name + act.Should().Throw() + .WithParameterName("invalidSpan"); + } } From a8c2c6af4184042c05b1cb5d1498c5cbde83f9ea Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:38:31 +0100 Subject: [PATCH 096/123] refactor: assertion in ValidFileExtensions_Span now uses ReadOnlySpan.Equals Signed-off-by: Kenny Pflug --- .../StringAssertions/MustBeFileExtensionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs index 0a71c55..2f7b1e7 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -86,7 +86,7 @@ public static void ValidFileExtensions_Span(string input) var result = span.MustBeFileExtension(); - result.ToString().Should().Be(input); + result.Equals(span, StringComparison.Ordinal).Should().BeTrue(); } [Theory] From 9721ef4071020cff4ac9e83b3546ffbdd1dcb2b4 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:39:00 +0100 Subject: [PATCH 097/123] refactor: unify ValidFileExtensionsData in MustBeFileExtensionTests Signed-off-by: Kenny Pflug --- .../StringAssertions/MustBeFileExtensionTests.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs index 2f7b1e7..3d3b118 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -18,11 +18,16 @@ public static class MustBeFileExtensionTests "....", ".docx.", ]; + + public static readonly TheoryData ValidFileExtensionsData = + [ + ".txt", + ".jpg", + ".tar.gz", + ]; [Theory] - [InlineData(".txt")] - [InlineData(".jpg")] - [InlineData(".tar.gz")] + [MemberData(nameof(ValidFileExtensionsData))] public static void ValidFileExtensions(string input) => input.MustBeFileExtension().Should().BeSameAs(input); @@ -77,9 +82,7 @@ public static void CallerArgumentExpression() } [Theory] - [InlineData(".txt")] - [InlineData(".jpg")] - [InlineData(".tar.gz")] + [MemberData(nameof(ValidFileExtensionsData))] public static void ValidFileExtensions_Span(string input) { var span = input.AsSpan(); From 92f9e3a4871cfec2ae7b3946169805627f660fbd Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:39:36 +0100 Subject: [PATCH 098/123] chore: remove obsolete comments in MustBeFileExtensionTests.cs Signed-off-by: Kenny Pflug --- .../StringAssertions/MustBeFileExtensionTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs index 3d3b118..bf1385a 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -126,14 +126,12 @@ public static void CustomExceptionInvalidFileExtensions_Span(string invalidStrin [Fact] public static void CallerArgumentExpression_Span() { - // Act & Assert var act = () => { var invalidSpan = "txt".AsSpan(); invalidSpan.MustBeFileExtension(); }; - // Should throw with parameter name matching the variable name act.Should().Throw() .WithParameterName("invalidSpan"); } From 6204bc226e5c3d99bf84e303edf045dd3c8b38b4 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 10:50:41 +0100 Subject: [PATCH 099/123] tests: add MustBeFileExtension tests for remaining types Signed-off-by: Kenny Pflug --- .../MustBeFileExtensionTests.cs | 185 +++++++++++++++++- 1 file changed, 176 insertions(+), 9 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs index bf1385a..a5df073 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeFileExtensionTests.cs @@ -18,7 +18,7 @@ public static class MustBeFileExtensionTests "....", ".docx.", ]; - + public static readonly TheoryData ValidFileExtensionsData = [ ".txt", @@ -83,25 +83,25 @@ public static void CallerArgumentExpression() [Theory] [MemberData(nameof(ValidFileExtensionsData))] - public static void ValidFileExtensions_Span(string input) + public static void ValidFileExtensions_ReadOnlySpan(string input) { var span = input.AsSpan(); - + var result = span.MustBeFileExtension(); - + result.Equals(span, StringComparison.Ordinal).Should().BeTrue(); } [Theory] [MemberData(nameof(InvalidFileExtensionsData))] - public static void InvalidFileExtensions_Span(string invalidString) + public static void InvalidFileExtensions_ReadOnlySpan(string invalidString) { var act = () => { var span = new ReadOnlySpan(invalidString?.ToCharArray() ?? []); span.MustBeFileExtension("parameterName"); }; - + act.Should().Throw() .And.Message.Should().Contain( $"parameterName must be a valid file extension, but it actually is {invalidString}" @@ -110,7 +110,7 @@ public static void InvalidFileExtensions_Span(string invalidString) [Theory] [MemberData(nameof(InvalidFileExtensionsData))] - public static void CustomExceptionInvalidFileExtensions_Span(string invalidString) + public static void CustomExceptionInvalidFileExtensions_ReadOnlySpan(string invalidString) { var exception = new Exception(); @@ -124,15 +124,182 @@ public static void CustomExceptionInvalidFileExtensions_Span(string invalidStrin } [Fact] - public static void CallerArgumentExpression_Span() + public static void CallerArgumentExpression_ReadOnlySpan() { var act = () => { var invalidSpan = "txt".AsSpan(); invalidSpan.MustBeFileExtension(); }; - + + act.Should().Throw() + .WithParameterName("invalidSpan"); + } + + [Theory] + [MemberData(nameof(ValidFileExtensionsData))] + public static void ValidFileExtensions_Span(string input) + { + var span = input.AsSpan().ToArray().AsSpan(); + + var result = span.MustBeFileExtension(); + + result.ToString().Should().Be(input); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void InvalidFileExtensions_Span(string invalidString) + { + var act = () => + { + var span = new Span(invalidString?.ToCharArray() ?? []); + span.MustBeFileExtension("parameterName"); + }; + + act.Should().Throw() + .And.Message.Should().Contain( + $"parameterName must be a valid file extension, but it actually is {invalidString}" + ); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void CustomExceptionInvalidFileExtensions_Span(string invalidString) + { + var exception = new Exception(); + + var act = () => + { + var span = new Span(invalidString?.ToCharArray() ?? []); + span.MustBeFileExtension(_ => exception); + }; + + act.Should().Throw().Which.Should().BeSameAs(exception); + } + + [Fact] + public static void CallerArgumentExpression_Span() + { + var act = () => + { + var charArray = "txt".ToCharArray(); + var invalidSpan = new Span(charArray); + invalidSpan.MustBeFileExtension(); + }; + act.Should().Throw() .WithParameterName("invalidSpan"); } + + [Theory] + [MemberData(nameof(ValidFileExtensionsData))] + public static void ValidFileExtensions_Memory(string input) + { + var memory = input.ToCharArray().AsMemory(); + + var result = memory.MustBeFileExtension(); + + result.ToString().Should().Be(input); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void InvalidFileExtensions_Memory(string invalidString) + { + var act = () => + { + var memory = new Memory(invalidString?.ToCharArray() ?? []); + memory.MustBeFileExtension("parameterName"); + }; + + act.Should().Throw() + .And.Message.Should().Contain( + $"parameterName must be a valid file extension, but it actually is {invalidString}" + ); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void CustomExceptionInvalidFileExtensions_Memory(string invalidString) + { + var exception = new Exception(); + + var act = () => + { + var memory = new Memory(invalidString?.ToCharArray() ?? []); + memory.MustBeFileExtension(_ => exception); + }; + + act.Should().Throw().Which.Should().BeSameAs(exception); + } + + [Fact] + public static void CallerArgumentExpression_Memory() + { + var act = () => + { + var charArray = "txt".ToCharArray(); + var invalidMemory = new Memory(charArray); + invalidMemory.MustBeFileExtension(); + }; + + act.Should().Throw() + .WithParameterName("invalidMemory"); + } + + [Theory] + [MemberData(nameof(ValidFileExtensionsData))] + public static void ValidFileExtensions_ReadOnlyMemory(string input) + { + var memory = input.AsMemory(); + + var result = memory.MustBeFileExtension(); + + result.Equals(memory).Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void InvalidFileExtensions_ReadOnlyMemory(string invalidString) + { + var act = () => + { + var memory = new ReadOnlyMemory(invalidString?.ToCharArray() ?? []); + memory.MustBeFileExtension("parameterName"); + }; + + act.Should().Throw() + .And.Message.Should().Contain( + $"parameterName must be a valid file extension, but it actually is {invalidString}" + ); + } + + [Theory] + [MemberData(nameof(InvalidFileExtensionsData))] + public static void CustomExceptionInvalidFileExtensions_ReadOnlyMemory(string invalidString) + { + var exception = new Exception(); + + var act = () => + { + var memory = new ReadOnlyMemory(invalidString?.ToCharArray() ?? []); + memory.MustBeFileExtension(_ => exception); + }; + + act.Should().Throw().Which.Should().BeSameAs(exception); + } + + [Fact] + public static void CallerArgumentExpression_ReadOnlyMemory() + { + var act = void () => + { + var invalidMemory = "txt".AsMemory(); + invalidMemory.MustBeFileExtension(); + }; + + act.Should().Throw() + .WithParameterName("invalidMemory"); + } } From 9e618deed237a5547061544108d0c44faa535c5e Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 17:28:23 +0100 Subject: [PATCH 100/123] feat: extend Email regex Signed-off-by: Kenny Pflug --- .../{IsEmailAddressTest.cs => IsEmailAddressTests.cs} | 11 +++++++++-- Code/Light.GuardClauses/RegularExpressions.cs | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) rename Code/Light.GuardClauses.Tests/StringAssertions/{IsEmailAddressTest.cs => IsEmailAddressTests.cs} (71%) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTest.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs similarity index 71% rename from Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTest.cs rename to Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs index 9e0b994..8e32970 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTest.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs @@ -3,7 +3,7 @@ namespace Light.GuardClauses.Tests.StringAssertions; -public sealed class IsEmailAddressTest +public sealed class IsEmailAddressTests { [Theory] [InlineData(null)] @@ -16,12 +16,12 @@ public sealed class IsEmailAddressTest [InlineData(".email@domain.com")] [InlineData("email.@domain.com")] [InlineData("email..email@domain.com")] - [InlineData("あいうえお@domain.com")] [InlineData("email@domain.com (Joe Smith)")] [InlineData("email@domain")] [InlineData("email@-domain.com")] [InlineData("email@111.222.333.44444")] [InlineData("email@domain..com")] + [InlineData("email@256.256.256.256")] // Invalid IP (values > 255) public void IsNotValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); @@ -41,6 +41,13 @@ public void IsNotValidEmailAddress(string email) [InlineData("email@domain.name")] [InlineData("email@domain.co.jp")] [InlineData("firstname-lastname@domain.com")] + [InlineData("email@domain.museum")] // Long TLD (>4 chars) + [InlineData("email@domain.travel")] // Another long TLD + [InlineData("email@domain.photography")] // Even longer TLD + [InlineData("email@[IPv6:2001:db8::1]")] // IPv6 format + [InlineData("\"quoted\"@domain.com")] // Quoted local part + [InlineData("user.name+tag+sorting@example.com")] // Gmail-style + addressing + [InlineData("あいうえお@domain.com")] // Unicode character test public void IsValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); diff --git a/Code/Light.GuardClauses/RegularExpressions.cs b/Code/Light.GuardClauses/RegularExpressions.cs index 3fec571..238f17c 100644 --- a/Code/Light.GuardClauses/RegularExpressions.cs +++ b/Code/Light.GuardClauses/RegularExpressions.cs @@ -14,8 +14,9 @@ public static class RegularExpressions /// /// Gets the string that represents the . /// + // This is an AI-generated regex. I don't have any clue and I find it way to complex to ever understand it. public const string EmailRegexText = - @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$"; + @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+(?:\.[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+)*)@(?:(?:[A-Za-z0-9](?:[A-Za-z0-9\-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}|(?:\[(?:IPv6:[0-9A-Fa-f:.]+)\])|(?:25[0-5]|2[0-4]\d|[01]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)){3}))$"; /// /// Gets the default regular expression for email validation. From 08adfbcaaf314f2e701a87280a8aed7450cf8587 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 17:51:47 +0100 Subject: [PATCH 101/123] feat: add IsEmailAddress overloads for Span and Memory Signed-off-by: Kenny Pflug --- .../StringAssertions/IsEmailAddressTests.cs | 165 ++++++++++++++---- .../Check.IsEmailAddress.cs | 76 +++++++- 2 files changed, 202 insertions(+), 39 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs index 8e32970..d5aa069 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs @@ -1,27 +1,33 @@ -using FluentAssertions; +using System; +using FluentAssertions; using Xunit; namespace Light.GuardClauses.Tests.StringAssertions; public sealed class IsEmailAddressTests { + public static readonly TheoryData InvalidEmailAddresses = + [ + null, + "plainaddress", + "#@%^%#$@#$@#.com", + "@domain.com", + "Joe Smith ", + "email.domain.com", + "email@domain@domain.com", + ".email@domain.com", + "email.@domain.com", + "email..email@domain.com", + "email@domain.com (Joe Smith)", + "email@domain", + "email@-domain.com", + "email@111.222.333.44444", + "email@domain..com", + "email@256.256.256.256", + ]; + [Theory] - [InlineData(null)] - [InlineData("plainaddress")] - [InlineData("#@%^%#$@#$@#.com")] - [InlineData("@domain.com")] - [InlineData("Joe Smith ")] - [InlineData("email.domain.com")] - [InlineData("email@domain@domain.com")] - [InlineData(".email@domain.com")] - [InlineData("email.@domain.com")] - [InlineData("email..email@domain.com")] - [InlineData("email@domain.com (Joe Smith)")] - [InlineData("email@domain")] - [InlineData("email@-domain.com")] - [InlineData("email@111.222.333.44444")] - [InlineData("email@domain..com")] - [InlineData("email@256.256.256.256")] // Invalid IP (values > 255) + [MemberData(nameof(InvalidEmailAddresses))] public void IsNotValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); @@ -29,29 +35,116 @@ public void IsNotValidEmailAddress(string email) isValid.Should().BeFalse(); } + public static readonly TheoryData ValidEmailAddresses = + [ + "email@domain.com", + "firstname.lastname@domain.com", + "email@subdomain.domain.com", + "firstname+lastname@domain.com", + "email@123.123.123.123", + "1234567890@domain.com", + "email@domain-one.com", + "_______@domain.com", + "email@domain.name", + "email@domain.co.jp", + "firstname-lastname@domain.com", + "email@domain.museum", // Long TLD (>4 chars) + "email@domain.travel", // Another long TLD + "email@domain.photography", // Even longer TLD + "email@[IPv6:2001:db8::1]", // IPv6 format + "\"quoted\"@domain.com", // Quoted local part + "user.name+tag+sorting@example.com", // Gmail-style + addressing + "あいうえお@domain.com", // Unicode character test + ]; + [Theory] - [InlineData("email@domain.com")] - [InlineData("firstname.lastname@domain.com")] - [InlineData("email@subdomain.domain.com")] - [InlineData("firstname+lastname@domain.com")] - [InlineData("email@123.123.123.123")] - [InlineData("1234567890@domain.com")] - [InlineData("email@domain-one.com")] - [InlineData("_______@domain.com")] - [InlineData("email@domain.name")] - [InlineData("email@domain.co.jp")] - [InlineData("firstname-lastname@domain.com")] - [InlineData("email@domain.museum")] // Long TLD (>4 chars) - [InlineData("email@domain.travel")] // Another long TLD - [InlineData("email@domain.photography")] // Even longer TLD - [InlineData("email@[IPv6:2001:db8::1]")] // IPv6 format - [InlineData("\"quoted\"@domain.com")] // Quoted local part - [InlineData("user.name+tag+sorting@example.com")] // Gmail-style + addressing - [InlineData("あいうえお@domain.com")] // Unicode character test + [MemberData(nameof(ValidEmailAddresses))] public void IsValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); isValid.Should().BeTrue(); } -} \ No newline at end of file + +#if NET8_0 + [Theory] + [MemberData(nameof(InvalidEmailAddresses))] + public void IsNotValidEmailAddress_ReadOnlySpan(string email) + { + var span = new ReadOnlySpan(email?.ToCharArray() ?? []); + var isValid = span.IsEmailAddress(); + + isValid.Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(ValidEmailAddresses))] + public void IsValidEmailAddress_ReadOnlySpan(string email) + { + var span = email.AsSpan(); + var isValid = span.IsEmailAddress(); + + isValid.Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(InvalidEmailAddresses))] + public void IsNotValidEmailAddress_Span(string email) + { + var span = new Span(email?.ToCharArray() ?? []); + var isValid = span.IsEmailAddress(); + + isValid.Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(ValidEmailAddresses))] + public void IsValidEmailAddress_Span(string email) + { + var span = new Span(email.ToCharArray()); + var isValid = span.IsEmailAddress(); + + isValid.Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(InvalidEmailAddresses))] + public void IsNotValidEmailAddress_Memory(string email) + { + var memory = email?.ToCharArray().AsMemory() ?? Memory.Empty; + var isValid = memory.IsEmailAddress(); + + isValid.Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(ValidEmailAddresses))] + public void IsValidEmailAddress_Memory(string email) + { + var memory = email.ToCharArray().AsMemory(); + var isValid = memory.IsEmailAddress(); + + isValid.Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(InvalidEmailAddresses))] + public void IsNotValidEmailAddress_ReadOnlyMemory(string email) + { + var memory = new ReadOnlyMemory(email?.ToCharArray() ?? []); + var isValid = memory.IsEmailAddress(); + + isValid.Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(ValidEmailAddresses))] + public void IsValidEmailAddress_ReadOnlyMemory(string email) + { + var memory = new ReadOnlyMemory(email.ToCharArray()); + var isValid = memory.IsEmailAddress(); + + isValid.Should().BeTrue(); + } +#endif +} diff --git a/Code/Light.GuardClauses/Check.IsEmailAddress.cs b/Code/Light.GuardClauses/Check.IsEmailAddress.cs index 7cfb698..396e2fd 100644 --- a/Code/Light.GuardClauses/Check.IsEmailAddress.cs +++ b/Code/Light.GuardClauses/Check.IsEmailAddress.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using JetBrains.Annotations; -using System.Runtime.CompilerServices; namespace Light.GuardClauses; @@ -10,7 +10,7 @@ public static partial class Check { /// /// Checks if the specified string is an email address using the default email regular expression - /// defined in . + /// defined in . /// /// The string to be checked if it is an email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -23,9 +23,79 @@ public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) /// /// The string to be checked. /// The regular expression that determines whether the input string is an email address. - /// Thrown when is null. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); + +#if NET8_0 + /// + /// Checks if the specified span is an email address using the default email regular expression + /// defined in . + /// + /// The span to be checked. + public static bool IsEmailAddress(this Span emailAddress) => + RegularExpressions.EmailRegex.IsMatch(emailAddress); + + /// + /// Checks if the specified span is an email address using the provided regular expression for validation. + /// + /// The span to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + public static bool IsEmailAddress(this Span emailAddress, Regex emailAddressPattern) => + emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); + + /// + /// Checks if the specified span is an email address using the default email regular expression + /// defined in . + /// + /// The span to be checked. + public static bool IsEmailAddress(this ReadOnlySpan emailAddress) => + RegularExpressions.EmailRegex.IsMatch(emailAddress); + + /// + /// Checks if the specified span is an email address using the provided regular expression for validation. + /// + /// The span to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + public static bool IsEmailAddress(this ReadOnlySpan emailAddress, Regex emailAddressPattern) => + emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); + + /// + /// Checks if the specified character memory is an email address using the default email regular expression + /// defined in . + /// + /// The character memory to be checked. + public static bool IsEmailAddress(this Memory emailAddress) => + RegularExpressions.EmailRegex.IsMatch(emailAddress.Span); + + /// + /// Checks if the specified character memory is an email address using the provided regular expression for validation. + /// + /// The character memory to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + public static bool IsEmailAddress(this Memory emailAddress, Regex emailAddressPattern) => + emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress.Span); + + /// + /// Checks if the specified character memory is an email address using the default email regular expression + /// defined in . + /// + /// The character memory to be checked. + public static bool IsEmailAddress(this ReadOnlyMemory emailAddress) => + RegularExpressions.EmailRegex.IsMatch(emailAddress.Span); + + /// + /// Checks if the specified character memory is an email address using the provided regular expression for validation. + /// + /// The character memory to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + public static bool IsEmailAddress(this ReadOnlyMemory emailAddress, Regex emailAddressPattern) => + emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress.Span); +#endif } From 4b6892860d9bbd22df8585c82631430cf7ca61a0 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 18:18:36 +0100 Subject: [PATCH 102/123] feat: add MustBeEmailAddress overloads for Span and Memory Signed-off-by: Kenny Pflug --- .../Check.MustBeEmailAddress.cs | 333 ++++++++++++++++++ Code/Light.GuardClauses/Exceptions/Throw.cs | 8 + 2 files changed, 341 insertions(+) diff --git a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs index 920e55e..ef2b33e 100644 --- a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs +++ b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs @@ -107,4 +107,337 @@ public static string MustBeEmailAddress( return parameter; } + +#if NET8_0 + /// + /// Ensures that the span represents a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The span that will be validated as an email address. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeEmailAddress( + this ReadOnlySpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsEmailAddress()) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. + /// + /// The span that will be validated as an email address. + /// The delegate that creates your custom exception. A string created from is passed to this delegate. + /// Your custom exception thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeEmailAddress( + this ReadOnlySpan parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + if (!parameter.IsEmailAddress()) + { + Throw.CustomSpanException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the provided regular expression, + /// or otherwise throws an . + /// + /// The span that will be validated as an email address. + /// The regular expression that determines if the span represents a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddressPattern:null => halt")] + public static ReadOnlySpan MustBeEmailAddress( + this ReadOnlySpan parameter, + Regex emailAddressPattern, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)); + + if (!parameter.IsEmailAddress(emailAddressPattern)) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. + /// + /// The span that will be validated as an email address. + /// The regular expression that determines if the span represents a valid email. + /// The delegate that creates your custom exception. A string created from and are passed to this delegate. + /// Your custom exception thrown when is no valid email address or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeEmailAddress( + this ReadOnlySpan parameter, + Regex emailAddressPattern, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) + { + Throw.CustomSpanException(exceptionFactory, parameter, emailAddressPattern!); + } + + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The span that will be validated as an email address. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeEmailAddress( + this Span parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter).MustBeEmailAddress(parameterName, message); + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. + /// + /// The span that will be validated as an email address. + /// The delegate that creates your custom exception. A string created from is passed to this delegate. + /// Your custom exception thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeEmailAddress( + this Span parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan) parameter).MustBeEmailAddress(exceptionFactory); + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the provided regular expression, + /// or otherwise throws an . + /// + /// The span that will be validated as an email address. + /// The regular expression that determines if the span represents a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddressPattern:null => halt")] + public static Span MustBeEmailAddress( + this Span parameter, + Regex emailAddressPattern, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter).MustBeEmailAddress(emailAddressPattern, parameterName, message); + return parameter; + } + + /// + /// Ensures that the span represents a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. + /// + /// The span that will be validated as an email address. + /// The regular expression that determines if the span represents a valid email. + /// The delegate that creates your custom exception. A string created from and are passed to this delegate. + /// Your custom exception thrown when is no valid email address or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeEmailAddress( + this Span parameter, + Regex emailAddressPattern, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan) parameter).MustBeEmailAddress(emailAddressPattern, exceptionFactory); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The memory that will be validated as an email address. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustBeEmailAddress( + this Memory parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter.Span).MustBeEmailAddress(parameterName, message); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. + /// + /// The memory that will be validated as an email address. + /// The delegate that creates your custom exception. A string created from is passed to this delegate. + /// Your custom exception thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustBeEmailAddress( + this Memory parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan) parameter.Span).MustBeEmailAddress(exceptionFactory); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the provided regular expression, + /// or otherwise throws an . + /// + /// The memory that will be validated as an email address. + /// The regular expression that determines if the memory represents a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddressPattern:null => halt")] + public static Memory MustBeEmailAddress( + this Memory parameter, + Regex emailAddressPattern, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + ((ReadOnlySpan) parameter.Span).MustBeEmailAddress(emailAddressPattern, parameterName, message); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. + /// + /// The memory that will be validated as an email address. + /// The regular expression that determines if the memory represents a valid email. + /// The delegate that creates your custom exception. A string created from and are passed to this delegate. + /// Your custom exception thrown when is no valid email address or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustBeEmailAddress( + this Memory parameter, + Regex emailAddressPattern, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + ((ReadOnlySpan) parameter.Span).MustBeEmailAddress(emailAddressPattern, exceptionFactory); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The memory that will be validated as an email address. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeEmailAddress( + this ReadOnlyMemory parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + parameter.Span.MustBeEmailAddress(parameterName, message); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. + /// + /// The memory that will be validated as an email address. + /// The delegate that creates your custom exception. A string created from is passed to this delegate. + /// Your custom exception thrown when is no valid email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeEmailAddress( + this ReadOnlyMemory parameter, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + parameter.Span.MustBeEmailAddress(exceptionFactory); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the provided regular expression, + /// or otherwise throws an . + /// + /// The memory that will be validated as an email address. + /// The regular expression that determines if the memory represents a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddressPattern:null => halt")] + public static ReadOnlyMemory MustBeEmailAddress( + this ReadOnlyMemory parameter, + Regex emailAddressPattern, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + parameter.Span.MustBeEmailAddress(emailAddressPattern, parameterName, message); + return parameter; + } + + /// + /// Ensures that the memory represents a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. + /// + /// The memory that will be validated as an email address. + /// The regular expression that determines if the memory represents a valid email. + /// The delegate that creates your custom exception. A string created from and are passed to this delegate. + /// Your custom exception thrown when is no valid email address or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeEmailAddress( + this ReadOnlyMemory parameter, + Regex emailAddressPattern, + ReadOnlySpanExceptionFactory exceptionFactory + ) + { + parameter.Span.MustBeEmailAddress(emailAddressPattern, exceptionFactory); + return parameter; + } +#endif } diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs index 7df3649..104c6b2 100644 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ b/Code/Light.GuardClauses/Exceptions/Throw.cs @@ -84,6 +84,14 @@ public static void Argument(string? parameterName = null, string? message = null [DoesNotReturn] public static void InvalidEmailAddress(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"."); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidEmailAddress(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => + throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"."); /// /// Throws the default indicating that a has no value, using the optional parameter name and message. From da4d18acaadab1172d8df7e17015e0d7409db111 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 19:18:28 +0100 Subject: [PATCH 103/123] refactor: move valid and invalid email addresses to test classes Signed-off-by: Kenny Pflug --- .../StringAssertions/InvalidEmailAddresses.cs | 33 ++++++++++ .../StringAssertions/IsEmailAddressTests.cs | 62 +++---------------- .../StringAssertions/ValidEmailAddresses.cs | 28 +++++++++ 3 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 Code/Light.GuardClauses.Tests/StringAssertions/InvalidEmailAddresses.cs create mode 100644 Code/Light.GuardClauses.Tests/StringAssertions/ValidEmailAddresses.cs diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/InvalidEmailAddresses.cs b/Code/Light.GuardClauses.Tests/StringAssertions/InvalidEmailAddresses.cs new file mode 100644 index 0000000..a31d041 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/InvalidEmailAddresses.cs @@ -0,0 +1,33 @@ +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public class InvalidEmailAddresses : TheoryData +{ + public InvalidEmailAddresses() + { + Add("plainaddress"); + Add("#@%^%#$@#$@#.com"); + Add("@domain.com"); + Add("Joe Smith "); + Add("email.domain.com"); + Add("email@domain@domain.com"); + Add(".email@domain.com"); + Add("email.@domain.com"); + Add("email..email@domain.com"); + Add("email@domain.com (Joe Smith)"); + Add("email@domain"); + Add("email@-domain.com"); + Add("email@111.222.333.44444"); + Add("email@domain..com"); + Add("email@256.256.256.256"); + } +} + +public sealed class InvalidEmailAddressesWithNull : InvalidEmailAddresses +{ + public InvalidEmailAddressesWithNull() + { + Add(null); + } +} diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs index d5aa069..0cd3750 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs @@ -6,28 +6,8 @@ namespace Light.GuardClauses.Tests.StringAssertions; public sealed class IsEmailAddressTests { - public static readonly TheoryData InvalidEmailAddresses = - [ - null, - "plainaddress", - "#@%^%#$@#$@#.com", - "@domain.com", - "Joe Smith ", - "email.domain.com", - "email@domain@domain.com", - ".email@domain.com", - "email.@domain.com", - "email..email@domain.com", - "email@domain.com (Joe Smith)", - "email@domain", - "email@-domain.com", - "email@111.222.333.44444", - "email@domain..com", - "email@256.256.256.256", - ]; - [Theory] - [MemberData(nameof(InvalidEmailAddresses))] + [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); @@ -35,30 +15,8 @@ public void IsNotValidEmailAddress(string email) isValid.Should().BeFalse(); } - public static readonly TheoryData ValidEmailAddresses = - [ - "email@domain.com", - "firstname.lastname@domain.com", - "email@subdomain.domain.com", - "firstname+lastname@domain.com", - "email@123.123.123.123", - "1234567890@domain.com", - "email@domain-one.com", - "_______@domain.com", - "email@domain.name", - "email@domain.co.jp", - "firstname-lastname@domain.com", - "email@domain.museum", // Long TLD (>4 chars) - "email@domain.travel", // Another long TLD - "email@domain.photography", // Even longer TLD - "email@[IPv6:2001:db8::1]", // IPv6 format - "\"quoted\"@domain.com", // Quoted local part - "user.name+tag+sorting@example.com", // Gmail-style + addressing - "あいうえお@domain.com", // Unicode character test - ]; - [Theory] - [MemberData(nameof(ValidEmailAddresses))] + [ClassData(typeof(ValidEmailAddresses))] public void IsValidEmailAddress(string email) { var isValid = email.IsEmailAddress(); @@ -68,7 +26,7 @@ public void IsValidEmailAddress(string email) #if NET8_0 [Theory] - [MemberData(nameof(InvalidEmailAddresses))] + [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress_ReadOnlySpan(string email) { var span = new ReadOnlySpan(email?.ToCharArray() ?? []); @@ -78,7 +36,7 @@ public void IsNotValidEmailAddress_ReadOnlySpan(string email) } [Theory] - [MemberData(nameof(ValidEmailAddresses))] + [ClassData(typeof(ValidEmailAddresses))] public void IsValidEmailAddress_ReadOnlySpan(string email) { var span = email.AsSpan(); @@ -88,7 +46,7 @@ public void IsValidEmailAddress_ReadOnlySpan(string email) } [Theory] - [MemberData(nameof(InvalidEmailAddresses))] + [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress_Span(string email) { var span = new Span(email?.ToCharArray() ?? []); @@ -98,7 +56,7 @@ public void IsNotValidEmailAddress_Span(string email) } [Theory] - [MemberData(nameof(ValidEmailAddresses))] + [ClassData(typeof(ValidEmailAddresses))] public void IsValidEmailAddress_Span(string email) { var span = new Span(email.ToCharArray()); @@ -108,7 +66,7 @@ public void IsValidEmailAddress_Span(string email) } [Theory] - [MemberData(nameof(InvalidEmailAddresses))] + [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress_Memory(string email) { var memory = email?.ToCharArray().AsMemory() ?? Memory.Empty; @@ -118,7 +76,7 @@ public void IsNotValidEmailAddress_Memory(string email) } [Theory] - [MemberData(nameof(ValidEmailAddresses))] + [ClassData(typeof(ValidEmailAddresses))] public void IsValidEmailAddress_Memory(string email) { var memory = email.ToCharArray().AsMemory(); @@ -128,7 +86,7 @@ public void IsValidEmailAddress_Memory(string email) } [Theory] - [MemberData(nameof(InvalidEmailAddresses))] + [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress_ReadOnlyMemory(string email) { var memory = new ReadOnlyMemory(email?.ToCharArray() ?? []); @@ -138,7 +96,7 @@ public void IsNotValidEmailAddress_ReadOnlyMemory(string email) } [Theory] - [MemberData(nameof(ValidEmailAddresses))] + [ClassData(typeof(ValidEmailAddresses))] public void IsValidEmailAddress_ReadOnlyMemory(string email) { var memory = new ReadOnlyMemory(email.ToCharArray()); diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/ValidEmailAddresses.cs b/Code/Light.GuardClauses.Tests/StringAssertions/ValidEmailAddresses.cs new file mode 100644 index 0000000..d134ce1 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/ValidEmailAddresses.cs @@ -0,0 +1,28 @@ +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public sealed class ValidEmailAddresses : TheoryData +{ + public ValidEmailAddresses() + { + Add("email@domain.com"); + Add("firstname.lastname@domain.com"); + Add("email@subdomain.domain.com"); + Add("firstname+lastname@domain.com"); + Add("email@123.123.123.123"); + Add("1234567890@domain.com"); + Add("email@domain-one.com"); + Add("_______@domain.com"); + Add("email@domain.name"); + Add("email@domain.co.jp"); + Add("firstname-lastname@domain.com"); + Add("email@domain.museum"); // Long TLD (>4 chars) + Add("email@domain.travel"); // Another long TLD + Add("email@domain.photography"); // Even longer TLD + Add("email@[IPv6:2001:db8::1]"); // IPv6 format + Add("\"quoted\"@domain.com"); // Quoted local part + Add("user.name+tag+sorting@example.com"); // Gmail-style + addressing + Add("あいうえお@domain.com"); // Unicode character test + } +} From 83c1cd9b7dfd8ad63d1cc1e318d8bb434654beba Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 2 Mar 2025 20:43:05 +0100 Subject: [PATCH 104/123] tests: add tests for MustBeValidEmail for Span and Memory Signed-off-by: Kenny Pflug --- .../ExecuteReadOnlySpanAssertion.cs | 26 ++ .../MustBeEmailAddressTests.cs | 393 +++++++++++++++++- Code/Light.GuardClauses.Tests/Test.cs | 241 ++++++++++- 3 files changed, 630 insertions(+), 30 deletions(-) create mode 100644 Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs diff --git a/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs b/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs new file mode 100644 index 0000000..32536e1 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs @@ -0,0 +1,26 @@ +#nullable enable +using System; + +namespace Light.GuardClauses.Tests; + +public delegate void ExecuteReadOnlySpanAssertion( + ReadOnlySpan span, + ReadOnlySpanExceptionFactory exceptionFactory +); + +public delegate void ExecuteSpanAssertion( + Span span, + ReadOnlySpanExceptionFactory exceptionFactory +); + +public delegate void ExecuteReadOnlySpanAssertion( + ReadOnlySpan span, + TValue additionalValue, + ReadOnlySpanExceptionFactory exceptionFactory +); + +public delegate void ExecuteSpanAssertion( + Span span, + TValue additionalValue, + ReadOnlySpanExceptionFactory exceptionFactory +); diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs index 8d75d3a..f6d1314 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs @@ -9,35 +9,36 @@ namespace Light.GuardClauses.Tests.StringAssertions; public static class MustBeEmailAddressTests { [Theory] - [InlineData("email@domain.com")] - [InlineData("email@123.123.123.123")] - public static void ValidEmailAddress(string emailAddress) => emailAddress.MustBeEmailAddress().Should().BeSameAs(emailAddress); + [ClassData(typeof(ValidEmailAddresses))] + public static void ValidEmailAddress(string emailAddress) => + emailAddress.MustBeEmailAddress().Should().BeSameAs(emailAddress); [Theory] - [InlineData("plainAddress")] - [InlineData("Joe Smith ")] + [ClassData(typeof(InvalidEmailAddresses))] public static void InvalidEmailAddress(string emailAddress) { Action act = () => emailAddress.MustBeEmailAddress(); act.Should().Throw() - .And.Message.Should().Contain($"emailAddress must be a valid email address, but it actually is \"{emailAddress}\"."); + .And.Message.Should().Contain( + $"emailAddress must be a valid email address, but it actually is \"{emailAddress}\"." + ); } [Theory] - [InlineData(".email@domain.com")] - [InlineData("email.@domain.com")] + [ClassData(typeof(InvalidEmailAddresses))] public static void InvalidEmailAddressArgumentName(string emailAddress) { Action act = () => emailAddress.MustBeEmailAddress(nameof(emailAddress)); act.Should().Throw() - .And.Message.Should().Contain($"emailAddress must be a valid email address, but it actually is \"{emailAddress}\"."); + .And.Message.Should().Contain( + $"emailAddress must be a valid email address, but it actually is \"{emailAddress}\"." + ); } [Theory] - [InlineData("email@domain")] - [InlineData("email@-domain.com")] + [ClassData(typeof(InvalidEmailAddresses))] public static void InvalidEmailAddressCustomMessage(string emailAddress) { const string customMessage = "This email address is not valid"; @@ -55,7 +56,7 @@ public static void InvalidEmailCustomRegex() { const string email = "email@domain@domain.com"; - Action act = () => email.MustBeEmailAddress(CustomRegex, "email"); + Action act = () => email.MustBeEmailAddress(CustomRegex); act.Should().Throw() .And.Message.Should().Contain($"email must be a valid email address, but it actually is \"{email}\"."); @@ -82,27 +83,35 @@ public static void NullCustomRegex(string email, Regex regex) new () { { null, CustomRegex }, - { "invalidEmailAddress", null } + { "invalidEmailAddress", null }, }; [Fact] public static void CustomException() => - Test.CustomException("email.domain.com", - (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory)); + Test.CustomException( + "email.domain.com", + (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory) + ); [Fact] public static void CustomMessage() => - Test.CustomMessage(message => "#@%^%#$@#$@#.com".MustBeEmailAddress(message: message)); + Test.CustomMessage( + message => "#@%^%#$@#$@#.com".MustBeEmailAddress(message: message) + ); [Fact] public static void CustomExceptionCustomRegex() => - Test.CustomException("invalidEmailAddress", - CustomRegex, - (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory)); + Test.CustomException( + "invalidEmailAddress", + CustomRegex, + (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory) + ); [Fact] public static void CustomMessageCustomRegex() => - Test.CustomMessage(message => "invalidEmailAddress".MustBeEmailAddress(CustomRegex, message: message)); + Test.CustomMessage( + message => "invalidEmailAddress".MustBeEmailAddress(CustomRegex, message: message) + ); [Fact] public static void CallerArgumentExpression() @@ -114,4 +123,346 @@ public static void CallerArgumentExpression() act.Should().Throw() .WithParameterName(nameof(email)); } -} \ No newline at end of file + +#if NET8_0 + [Theory] + [ClassData(typeof(ValidEmailAddresses))] + public static void ValidEmailAddress_ReadOnlySpan(string email) + { + var result = email.AsSpan().MustBeEmailAddress(); + result.ToString().Should().Be(email); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddress_ReadOnlySpan(string email) + { + var act = () => + { + var readOnlySpan = email.AsSpan(); + readOnlySpan.MustBeEmailAddress(); + }; + act.Should().Throw() + .And.Message.Should() + .Contain($"readOnlySpan must be a valid email address, but it actually is \"{email}\"."); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressArgumentName_ReadOnlySpan(string email) + { + Action act = () => email.AsSpan().MustBeEmailAddress(nameof(email)); + act.Should().Throw() + .And.Message.Should().Contain(nameof(email)); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressCustomMessage_ReadOnlySpan(string email) + { + const string customMessage = "This email address is not valid"; + Action act = () => email.AsSpan().MustBeEmailAddress(nameof(email), customMessage); + act.Should().Throw() + .And.Message.Should().Contain(customMessage); + } + + [Fact] + public static void CallerArgumentExpression_ReadOnlySpan() + { + var act = () => + { + var email = "This is not an email address".AsSpan(); + email.MustBeEmailAddress(); + }; + act.Should().Throw() + .WithParameterName("email"); + } + + [Fact] + public static void CustomException_ReadOnlySpan() => + Test.CustomSpanException( + "email.domain.com".AsSpan(), + (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory) + ); + + [Fact] + public static void CustomExceptionCustomRegex_ReadOnlySpan() => + Test.CustomSpanException( + "invalidEmailAddress".AsSpan(), + CustomRegex, + (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory) + ); + + [Fact] + public static void CustomMessageCustomRegex_ReadOnlySpan() => + Test.CustomMessage( + message => "invalidEmailAddress".AsSpan().MustBeEmailAddress(CustomRegex, message: message) + ); + + // Tests for Span + [Theory] + [ClassData(typeof(ValidEmailAddresses))] + public static void ValidEmailAddress_Span(string email) + { + var emailChars = email.ToCharArray(); + var span = new Span(emailChars); + var result = span.MustBeEmailAddress(); + new string(result).Should().Be(email); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddress_Span(string email) + { + var emailChars = email.ToCharArray(); + var act = () => + { + var span = new Span(emailChars); + span.MustBeEmailAddress(); + }; + act.Should().Throw() + .And.Message.Should().Contain($"span must be a valid email address, but it actually is \"{email}\"."); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressArgumentName_Span(string email) + { + var emailChars = email.ToCharArray(); + var act = () => + { + var span = new Span(emailChars); + span.MustBeEmailAddress(nameof(span)); + }; + act.Should().Throw() + .And.Message.Should().Contain("span"); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressCustomMessage_Span(string email) + { + const string customMessage = "This email address is not valid"; + var emailChars = email.ToCharArray(); + var act = () => + { + var span = new Span(emailChars); + span.MustBeEmailAddress(nameof(span), customMessage); + }; + act.Should().Throw() + .And.Message.Should().Contain(customMessage); + } + + [Fact] + public static void CallerArgumentExpression_Span() + { + var act = () => + { + var emailChars = "This is not an email address".ToCharArray(); + var span = new Span(emailChars); + span.MustBeEmailAddress(); + }; + act.Should().Throw() + .WithParameterName("span"); + } + + [Fact] + public static void CustomException_Span() + { + var emailChars = "email.domain.com".ToCharArray(); + var span = new Span(emailChars); + Test.CustomSpanException( + span, + (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory) + ); + } + + [Fact] + public static void CustomExceptionCustomRegex_Span() + { + var emailChars = "invalidEmailAddress".ToCharArray(); + var span = new Span(emailChars); + Test.CustomSpanException( + span, + CustomRegex, + (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory) + ); + } + + [Fact] + public static void CustomMessageCustomRegex_Span() => + Test.CustomMessage( + message => + { + var span = new Span("invalidEmailAddress".ToCharArray()); + span.MustBeEmailAddress(CustomRegex, message: message); + } + ); + + // Tests for Memory + [Theory] + [ClassData(typeof(ValidEmailAddresses))] + public static void ValidEmailAddress_Memory(string email) + { + var memory = email.ToCharArray().AsMemory(); + var result = memory.MustBeEmailAddress(); + result.ToString().Should().Be(email); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddress_Memory(string email) + { + var memory = email.ToCharArray().AsMemory(); + Action act = () => memory.MustBeEmailAddress(); + act.Should().Throw() + .And.Message.Should().Contain($"memory must be a valid email address, but it actually is \"{email}\"."); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressArgumentName_Memory(string email) + { + var memory = email.ToCharArray().AsMemory(); + Action act = () => memory.MustBeEmailAddress(nameof(memory)); + act.Should().Throw() + .And.Message.Should().Contain(nameof(memory)); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressCustomMessage_Memory(string email) + { + const string customMessage = "This email address is not valid"; + var memory = email.ToCharArray().AsMemory(); + Action act = () => memory.MustBeEmailAddress(nameof(memory), customMessage); + act.Should().Throw() + .And.Message.Should().Contain(customMessage); + } + + [Fact] + public static void CallerArgumentExpression_Memory() + { + var act = () => + { + var memory = "This is not an email address".ToCharArray().AsMemory(); + memory.MustBeEmailAddress(); + }; + act.Should().Throw() + .WithParameterName("memory"); + } + + [Fact] + public static void CustomException_Memory() + { + var memory = "email.domain.com".ToCharArray().AsMemory(); + Test.CustomMemoryException( + memory, + (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory) + ); + } + + [Fact] + public static void CustomExceptionCustomRegex_Memory() + { + var memory = "invalidEmailAddress".ToCharArray().AsMemory(); + Test.CustomMemoryException( + memory, + CustomRegex, + (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory) + ); + } + + [Fact] + public static void CustomMessageCustomRegex_Memory() + { + var memory = "invalidEmailAddress".AsMemory(); + Test.CustomMessage( + message => memory.MustBeEmailAddress(CustomRegex, message: message) + ); + } + + // Tests for ReadOnlyMemory + [Theory] + [ClassData(typeof(ValidEmailAddresses))] + public static void ValidEmailAddress_ReadOnlyMemory(string email) + { + var memory = email.AsMemory(); + var result = memory.MustBeEmailAddress(); + result.ToString().Should().Be(email); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddress_ReadOnlyMemory(string email) + { + var memory = email.AsMemory(); + Action act = () => memory.MustBeEmailAddress(); + act.Should().Throw() + .And.Message.Should().StartWith($"memory must be a valid email address, but it actually is \"{email}\"."); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressArgumentName_ReadOnlyMemory(string email) + { + var readOnlyMemory = email.AsMemory(); + Action act = () => readOnlyMemory.MustBeEmailAddress(nameof(readOnlyMemory)); + act.Should().Throw() + .And.Message.Should().Contain(nameof(readOnlyMemory)); + } + + [Theory] + [ClassData(typeof(InvalidEmailAddresses))] + public static void InvalidEmailAddressCustomMessage_ReadOnlyMemory(string email) + { + const string customMessage = "This email address is not valid"; + var readOnlyMemory = email.AsMemory(); + Action act = () => readOnlyMemory.MustBeEmailAddress(nameof(readOnlyMemory), customMessage); + act.Should().Throw() + .And.Message.Should().Contain(customMessage); + } + + [Fact] + public static void CallerArgumentExpression_ReadOnlyMemory() + { + var act = () => + { + var readOnlyMemory = "This is not an email address".AsMemory(); + readOnlyMemory.MustBeEmailAddress(); + }; + act.Should().Throw() + .WithParameterName("readOnlyMemory"); + } + + [Fact] + public static void CustomException_ReadOnlyMemory() + { + var readOnlyMemory = "email.domain.com".AsMemory(); + Test.CustomMemoryException( + readOnlyMemory, + (input, exceptionFactory) => input.MustBeEmailAddress(exceptionFactory) + ); + } + + [Fact] + public static void CustomExceptionCustomRegex_ReadOnlyMemory() + { + var readOnlyMemory = "invalidEmailAddress".AsMemory(); + Test.CustomMemoryException( + readOnlyMemory, + CustomRegex, + (i, r, exceptionFactory) => i.MustBeEmailAddress(r, exceptionFactory) + ); + } + + [Fact] + public static void CustomMessageCustomRegex_ReadOnlyMemory() + { + var readOnlyMemory = "invalidEmailAddress".AsMemory(); + Test.CustomMessage( + message => readOnlyMemory.MustBeEmailAddress(CustomRegex, message: message) + ); + } +#endif +} diff --git a/Code/Light.GuardClauses.Tests/Test.cs b/Code/Light.GuardClauses.Tests/Test.cs index 9487c42..42922f5 100644 --- a/Code/Light.GuardClauses.Tests/Test.cs +++ b/Code/Light.GuardClauses.Tests/Test.cs @@ -1,11 +1,11 @@ -using System; +#nullable enable + +using System; using FluentAssertions; using FluentAssertions.Specialized; using Xunit.Abstractions; using Xunit.Sdk; -#nullable enable - namespace Light.GuardClauses.Tests; public static class Test @@ -48,7 +48,108 @@ Exception ExceptionFactory(T parameter) } } - public static void CustomException(T1 first, T2 second, Action> executeAssertion) + public static void CustomSpanException( + ReadOnlySpan invalidValue, + ExecuteReadOnlySpanAssertion executeAssertion + ) + { + T[]? capturedParameter = null; + + Exception ExceptionFactory(ReadOnlySpan parameter) + { + capturedParameter = parameter.ToArray(); + return Exception; + } + + try + { + executeAssertion(invalidValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedParameter.Should().Equal(invalidValue.ToArray()); + } + } + + public static void CustomSpanException(Span invalidValue, ExecuteSpanAssertion executeAssertion) + { + T[]? capturedParameter = null; + + Exception ExceptionFactory(ReadOnlySpan parameter) + { + capturedParameter = parameter.ToArray(); + return Exception; + } + + try + { + executeAssertion(invalidValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedParameter.Should().Equal(invalidValue.ToArray()); + } + } + + public static void CustomMemoryException( + Memory invalidValue, + Action, ReadOnlySpanExceptionFactory> executeAssertion + ) + { + Memory capturedParameter = default; + + Exception ExceptionFactory(ReadOnlySpan parameter) + { + capturedParameter = parameter.ToArray(); + return Exception; + } + + try + { + executeAssertion(invalidValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedParameter.ToArray().Should().Equal(invalidValue.ToArray()); + } + } + + public static void CustomMemoryException( + ReadOnlyMemory invalidValue, + Action, ReadOnlySpanExceptionFactory> executeAssertion + ) + { + ReadOnlyMemory capturedParameter = default; + + Exception ExceptionFactory(ReadOnlySpan parameter) + { + capturedParameter = parameter.ToArray(); + return Exception; + } + + try + { + executeAssertion(invalidValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedParameter.ToArray().Should().Equal(invalidValue.ToArray()); + } + } + + public static void CustomException( + T1 first, + T2 second, + Action> executeAssertion + ) { T1? capturedFirst = default; T2? capturedSecond = default; @@ -73,7 +174,128 @@ Exception ExceptionFactory(T1 x, T2 y) } } - public static void CustomException(T1 first, T2 second, T3 third, Action> executeAssertion) + public static void CustomSpanException( + ReadOnlySpan invalidValue, + T2 additionalValue, + ExecuteReadOnlySpanAssertion executeAssertion + ) + { + T1[]? capturedFirst = null; + T2? capturedSecond = default; + + Exception ExceptionFactory(ReadOnlySpan parameter, T2 second) + { + capturedFirst = parameter.ToArray(); + capturedSecond = second; + return Exception; + } + + try + { + executeAssertion(invalidValue, additionalValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedFirst.Should().Equal(invalidValue.ToArray()); + capturedSecond.Should().Be(additionalValue); + } + } + + public static void CustomSpanException( + Span invalidValue, + T2 additionalValue, + ExecuteSpanAssertion executeAssertion + ) + { + T1[]? capturedFirst = null; + T2? capturedSecond = default; + + Exception ExceptionFactory(ReadOnlySpan parameter, T2 second) + { + capturedFirst = parameter.ToArray(); + capturedSecond = second; + return Exception; + } + + try + { + executeAssertion(invalidValue, additionalValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedFirst.Should().Equal(invalidValue.ToArray()); + capturedSecond.Should().Be(additionalValue); + } + } + + public static void CustomMemoryException( + Memory invalidValue, + T2 additionalValue, + Action, T2, ReadOnlySpanExceptionFactory> executeAssertion + ) + { + Memory capturedFirst = default; + T2? capturedSecond = default; + + Exception ExceptionFactory(ReadOnlySpan first, T2 second) + { + capturedFirst = first.ToArray(); + capturedSecond = second; + return Exception; + } + + try + { + executeAssertion(invalidValue, additionalValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedFirst.ToArray().Should().Equal(invalidValue.ToArray()); + capturedSecond.Should().Be(additionalValue); + } + } + + public static void CustomMemoryException( + ReadOnlyMemory invalidValue, + T2 additionalValue, + Action, T2, ReadOnlySpanExceptionFactory> executeAssertion + ) + { + ReadOnlyMemory capturedFirst = default; + T2? capturedSecond = default; + + Exception ExceptionFactory(ReadOnlySpan first, T2 second) + { + capturedFirst = first.ToArray(); + capturedSecond = second; + return Exception; + } + + try + { + executeAssertion(invalidValue, additionalValue, ExceptionFactory); + throw new XunitException("The assertion should have thrown a custom exception at this point."); + } + catch (ExceptionDummy exception) + { + exception.Should().BeSameAs(exception); + capturedFirst.ToArray().Should().Equal(invalidValue.ToArray()); + capturedSecond.Should().Be(additionalValue); + } + } + + public static void CustomException( + T1 first, + T2 second, + T3 third, + Action> executeAssertion + ) { T1? capturedFirst = default; T2? capturedSecond = default; @@ -115,8 +337,9 @@ public static void CustomMessage(Action executeAssertion) wh } } - private sealed class ExceptionDummy : Exception; - - public static void WriteExceptionTo(this ExceptionAssertions exceptionAssertions, ITestOutputHelper output) where T : Exception => + public static void WriteExceptionTo(this ExceptionAssertions exceptionAssertions, ITestOutputHelper output) + where T : Exception => output.WriteLine(exceptionAssertions.Which.ToString()); -} \ No newline at end of file + + private sealed class ExceptionDummy : Exception; +} From cd34ecc9c34a5caf06b2c4cad44b7e5979bd3615 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 6 Mar 2025 05:08:23 +0100 Subject: [PATCH 105/123] refactor: move Throw into own namespace Signed-off-by: Kenny Pflug --- .../SpanMustHaveLengthBenchmark.cs | 1 + .../InvalidArgumentBenchmark.cs | 3 +- .../MustHaveValueBenchmark.cs | 1 + .../MustNotBeNullReferenceBenchmark.cs | 6 +- Code/Light.GuardClauses/Check.Equals.cs | 1 + .../Check.InvalidArgument.cs | 1 + .../Check.InvalidOperation.cs | 1 + Code/Light.GuardClauses/Check.InvalidState.cs | 1 + Code/Light.GuardClauses/Check.MustBe.cs | 1 + .../Check.MustBeAbsoluteUri.cs | 1 + .../Check.MustBeEmailAddress.cs | 1 + .../Check.MustBeFileExtension.cs | 1 + .../Check.MustBeGreaterThan.cs | 1 + .../Check.MustBeGreaterThanOrEqualTo.cs | 1 + .../Check.MustBeHttpOrHttpsUrl.cs | 1 + Code/Light.GuardClauses/Check.MustBeIn.cs | 1 + .../Check.MustBeLessThan.cs | 1 + .../Check.MustBeLessThanOrEqualTo.cs | 1 + Code/Light.GuardClauses/Check.MustBeLocal.cs | 1 + .../Check.MustBeLongerThan.cs | 1 + .../Check.MustBeLongerThanOrEqualTo.cs | 1 + .../Light.GuardClauses/Check.MustBeNewLine.cs | 1 + Code/Light.GuardClauses/Check.MustBeOfType.cs | 1 + Code/Light.GuardClauses/Check.MustBeOneOf.cs | 1 + .../Check.MustBeRelativeUri.cs | 1 + .../Check.MustBeShorterThan.cs | 1 + .../Check.MustBeShorterThanOrEqualTo.cs | 1 + .../Check.MustBeSubstringOf.cs | 1 + .../Light.GuardClauses/Check.MustBeTrimmed.cs | 1 + .../Check.MustBeTrimmedAtEnd.cs | 1 + .../Check.MustBeTrimmedAtStart.cs | 1 + .../Check.MustBeUnspecified.cs | 1 + Code/Light.GuardClauses/Check.MustBeUtc.cs | 1 + .../Check.MustBeValidEnumValue.cs | 1 + Code/Light.GuardClauses/Check.MustContain.cs | 1 + Code/Light.GuardClauses/Check.MustEndWith.cs | 1 + .../Light.GuardClauses/Check.MustHaveCount.cs | 1 + .../Check.MustHaveLength.cs | 1 + .../Check.MustHaveLengthIn.cs | 1 + .../Check.MustHaveMaximumCount.cs | 1 + .../Check.MustHaveMinimumCount.cs | 1 + .../Check.MustHaveOneSchemeOf.cs | 1 + .../Check.MustHaveScheme.cs | 1 + .../Light.GuardClauses/Check.MustHaveValue.cs | 1 + Code/Light.GuardClauses/Check.MustMatch.cs | 1 + Code/Light.GuardClauses/Check.MustNotBe.cs | 1 + .../Check.MustNotBeDefault.cs | 1 + .../Check.MustNotBeEmpty.cs | 1 + .../Check.MustNotBeGreaterThan.cs | 1 + .../Check.MustNotBeGreaterThanOrEqualTo.cs | 1 + Code/Light.GuardClauses/Check.MustNotBeIn.cs | 1 + .../Check.MustNotBeLessThan.cs | 1 + .../Check.MustNotBeLessThanOrEqualTo.cs | 1 + .../Light.GuardClauses/Check.MustNotBeNull.cs | 1 + .../Check.MustNotBeNullOrEmpty.cs | 1 + .../Check.MustNotBeNullOrWhiteSpace.cs | 1 + .../Check.MustNotBeNullReference.cs | 1 + .../Check.MustNotBeOneOf.cs | 1 + .../Check.MustNotBeSameAs.cs | 1 + .../Check.MustNotBeSubstringOf.cs | 1 + .../Check.MustNotContain.cs | 1 + .../Check.MustNotEndWith.cs | 1 + .../Check.MustNotStartWith.cs | 95 +- .../Light.GuardClauses/Check.MustStartWith.cs | 1 + .../ExceptionFactory/Throw.cs | 1340 +++++++++++++++++ Code/Light.GuardClauses/Exceptions/Throw.cs | 702 --------- Code/Light.GuardClauses/SpanDelegates.cs | 16 +- 67 files changed, 1514 insertions(+), 709 deletions(-) create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.cs delete mode 100644 Code/Light.GuardClauses/Exceptions/Throw.cs diff --git a/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs b/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs index 947efbf..f49986c 100644 --- a/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text; using BenchmarkDotNet.Attributes; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses.Performance.CollectionAssertions diff --git a/Code/Light.GuardClauses.Performance/CommonAssertions/InvalidArgumentBenchmark.cs b/Code/Light.GuardClauses.Performance/CommonAssertions/InvalidArgumentBenchmark.cs index dbc6241..3e922c3 100644 --- a/Code/Light.GuardClauses.Performance/CommonAssertions/InvalidArgumentBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/CommonAssertions/InvalidArgumentBenchmark.cs @@ -1,5 +1,6 @@ using System; using BenchmarkDotNet.Attributes; +using Light.GuardClauses.ExceptionFactory; namespace Light.GuardClauses.Performance.CommonAssertions { @@ -34,7 +35,7 @@ public bool LightGuardClausesCustomException() public bool LightGuardClausesCustomExceptionManualInlining() { if (Condition) - Exceptions.Throw.CustomException(() => new ArgumentException(Message, ParameterName)); + Throw.CustomException(() => new ArgumentException(Message, ParameterName)); return Condition; } diff --git a/Code/Light.GuardClauses.Performance/CommonAssertions/MustHaveValueBenchmark.cs b/Code/Light.GuardClauses.Performance/CommonAssertions/MustHaveValueBenchmark.cs index e42ec29..1458a09 100644 --- a/Code/Light.GuardClauses.Performance/CommonAssertions/MustHaveValueBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/CommonAssertions/MustHaveValueBenchmark.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses.Performance.CommonAssertions diff --git a/Code/Light.GuardClauses.Performance/CommonAssertions/MustNotBeNullReferenceBenchmark.cs b/Code/Light.GuardClauses.Performance/CommonAssertions/MustNotBeNullReferenceBenchmark.cs index e6c2a8a..ea6aa9e 100644 --- a/Code/Light.GuardClauses.Performance/CommonAssertions/MustNotBeNullReferenceBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/CommonAssertions/MustNotBeNullReferenceBenchmark.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using Light.GuardClauses.Exceptions; +using Light.GuardClauses.ExceptionFactory; namespace Light.GuardClauses.Performance.CommonAssertions { @@ -56,7 +56,7 @@ public static T MustNotBeNullReferenceV1(this T parameter, string parameterNa return parameter; Throw.ArgumentNull(parameterName, message); - return default(T); + return default; } return parameter; @@ -71,7 +71,7 @@ public static T MustNotBeNullReferenceV2(this T parameter, string parameterNa return parameter; Throw.ArgumentNull(parameterName, message); - return default(T); + return default; } } } \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.Equals.cs b/Code/Light.GuardClauses/Check.Equals.cs index 688e191..07df57c 100644 --- a/Code/Light.GuardClauses/Check.Equals.cs +++ b/Code/Light.GuardClauses/Check.Equals.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; diff --git a/Code/Light.GuardClauses/Check.InvalidArgument.cs b/Code/Light.GuardClauses/Check.InvalidArgument.cs index cc50e69..0235254 100644 --- a/Code/Light.GuardClauses/Check.InvalidArgument.cs +++ b/Code/Light.GuardClauses/Check.InvalidArgument.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.InvalidOperation.cs b/Code/Light.GuardClauses/Check.InvalidOperation.cs index c4ff5b7..2b0e5c1 100644 --- a/Code/Light.GuardClauses/Check.InvalidOperation.cs +++ b/Code/Light.GuardClauses/Check.InvalidOperation.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.InvalidState.cs b/Code/Light.GuardClauses/Check.InvalidState.cs index 1e89445..496ec78 100644 --- a/Code/Light.GuardClauses/Check.InvalidState.cs +++ b/Code/Light.GuardClauses/Check.InvalidState.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBe.cs b/Code/Light.GuardClauses/Check.MustBe.cs index 722e499..99a7121 100644 --- a/Code/Light.GuardClauses/Check.MustBe.cs +++ b/Code/Light.GuardClauses/Check.MustBe.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs b/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs index 88415b0..b4d3f75 100644 --- a/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs +++ b/Code/Light.GuardClauses/Check.MustBeAbsoluteUri.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs index ef2b33e..8cd52c7 100644 --- a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs +++ b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs index 1b3834f..ae24e8b 100644 --- a/Code/Light.GuardClauses/Check.MustBeFileExtension.cs +++ b/Code/Light.GuardClauses/Check.MustBeFileExtension.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs index c8b6234..2e66b68 100644 --- a/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThan.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs index 90cd127..213f825 100644 --- a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs b/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs index a431469..cbcea62 100644 --- a/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs +++ b/Code/Light.GuardClauses/Check.MustBeHttpOrHttpsUrl.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeIn.cs b/Code/Light.GuardClauses/Check.MustBeIn.cs index b727d36..061c236 100644 --- a/Code/Light.GuardClauses/Check.MustBeIn.cs +++ b/Code/Light.GuardClauses/Check.MustBeIn.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeLessThan.cs b/Code/Light.GuardClauses/Check.MustBeLessThan.cs index 21faebd..7667730 100644 --- a/Code/Light.GuardClauses/Check.MustBeLessThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeLessThan.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs index 9e813df..5ac96d3 100644 --- a/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeLessThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeLocal.cs b/Code/Light.GuardClauses/Check.MustBeLocal.cs index 5a41690..6f2c31b 100644 --- a/Code/Light.GuardClauses/Check.MustBeLocal.cs +++ b/Code/Light.GuardClauses/Check.MustBeLocal.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs index c884530..b6524d8 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs index f2e98ec..63a5297 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeNewLine.cs b/Code/Light.GuardClauses/Check.MustBeNewLine.cs index c5fef10..dcba3db 100644 --- a/Code/Light.GuardClauses/Check.MustBeNewLine.cs +++ b/Code/Light.GuardClauses/Check.MustBeNewLine.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeOfType.cs b/Code/Light.GuardClauses/Check.MustBeOfType.cs index bbf9001..7e9f44c 100644 --- a/Code/Light.GuardClauses/Check.MustBeOfType.cs +++ b/Code/Light.GuardClauses/Check.MustBeOfType.cs @@ -2,6 +2,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeOneOf.cs b/Code/Light.GuardClauses/Check.MustBeOneOf.cs index 6d70417..edcf7b4 100644 --- a/Code/Light.GuardClauses/Check.MustBeOneOf.cs +++ b/Code/Light.GuardClauses/Check.MustBeOneOf.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs b/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs index 6864c3d..9eee840 100644 --- a/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs +++ b/Code/Light.GuardClauses/Check.MustBeRelativeUri.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs index f871358..7fb01e2 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using Light.GuardClauses.Exceptions; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs index 20f2de8..9354f3d 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs b/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs index 63fd7f9..81d8cda 100644 --- a/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs +++ b/Code/Light.GuardClauses/Check.MustBeSubstringOf.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeTrimmed.cs b/Code/Light.GuardClauses/Check.MustBeTrimmed.cs index c4e163e..ea9fb3c 100644 --- a/Code/Light.GuardClauses/Check.MustBeTrimmed.cs +++ b/Code/Light.GuardClauses/Check.MustBeTrimmed.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs b/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs index de90549..f37319c 100644 --- a/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs +++ b/Code/Light.GuardClauses/Check.MustBeTrimmedAtEnd.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs b/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs index 34dad8b..6096052 100644 --- a/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs +++ b/Code/Light.GuardClauses/Check.MustBeTrimmedAtStart.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustBeUnspecified.cs b/Code/Light.GuardClauses/Check.MustBeUnspecified.cs index 97810eb..d4c806d 100644 --- a/Code/Light.GuardClauses/Check.MustBeUnspecified.cs +++ b/Code/Light.GuardClauses/Check.MustBeUnspecified.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeUtc.cs b/Code/Light.GuardClauses/Check.MustBeUtc.cs index efa2369..87cbc2b 100644 --- a/Code/Light.GuardClauses/Check.MustBeUtc.cs +++ b/Code/Light.GuardClauses/Check.MustBeUtc.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs b/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs index 9672a6d..c078cb3 100644 --- a/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs +++ b/Code/Light.GuardClauses/Check.MustBeValidEnumValue.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustContain.cs b/Code/Light.GuardClauses/Check.MustContain.cs index 24b7175..7158a37 100644 --- a/Code/Light.GuardClauses/Check.MustContain.cs +++ b/Code/Light.GuardClauses/Check.MustContain.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustEndWith.cs b/Code/Light.GuardClauses/Check.MustEndWith.cs index 06dc8b2..3f15afd 100644 --- a/Code/Light.GuardClauses/Check.MustEndWith.cs +++ b/Code/Light.GuardClauses/Check.MustEndWith.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveCount.cs b/Code/Light.GuardClauses/Check.MustHaveCount.cs index 3c88fad..cf1755b 100644 --- a/Code/Light.GuardClauses/Check.MustHaveCount.cs +++ b/Code/Light.GuardClauses/Check.MustHaveCount.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveLength.cs b/Code/Light.GuardClauses/Check.MustHaveLength.cs index d912c6b..34932e8 100644 --- a/Code/Light.GuardClauses/Check.MustHaveLength.cs +++ b/Code/Light.GuardClauses/Check.MustHaveLength.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs b/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs index 340d142..c190c98 100644 --- a/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs +++ b/Code/Light.GuardClauses/Check.MustHaveLengthIn.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs b/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs index b141e55..1f37f9f 100644 --- a/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs +++ b/Code/Light.GuardClauses/Check.MustHaveMaximumCount.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs b/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs index c858def..94b101b 100644 --- a/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs +++ b/Code/Light.GuardClauses/Check.MustHaveMinimumCount.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs b/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs index 19e6b80..61c333f 100644 --- a/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs +++ b/Code/Light.GuardClauses/Check.MustHaveOneSchemeOf.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveScheme.cs b/Code/Light.GuardClauses/Check.MustHaveScheme.cs index aef3ae0..1176b03 100644 --- a/Code/Light.GuardClauses/Check.MustHaveScheme.cs +++ b/Code/Light.GuardClauses/Check.MustHaveScheme.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustHaveValue.cs b/Code/Light.GuardClauses/Check.MustHaveValue.cs index b20b452..4af6c87 100644 --- a/Code/Light.GuardClauses/Check.MustHaveValue.cs +++ b/Code/Light.GuardClauses/Check.MustHaveValue.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustMatch.cs b/Code/Light.GuardClauses/Check.MustMatch.cs index 591e357..fcf5728 100644 --- a/Code/Light.GuardClauses/Check.MustMatch.cs +++ b/Code/Light.GuardClauses/Check.MustMatch.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBe.cs b/Code/Light.GuardClauses/Check.MustNotBe.cs index 93b4ced..ab562a4 100644 --- a/Code/Light.GuardClauses/Check.MustNotBe.cs +++ b/Code/Light.GuardClauses/Check.MustNotBe.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustNotBeDefault.cs b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs index d2fade3..e5a8fcd 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeDefault.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeDefault.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs b/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs index f933994..675e769 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeEmpty.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs b/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs index 4c02346..c64ff6c 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeGreaterThan.cs @@ -1,6 +1,7 @@ using System; using JetBrains.Annotations; using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs index 26a996e..7ffde4d 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeGreaterThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeIn.cs b/Code/Light.GuardClauses/Check.MustNotBeIn.cs index 4b7a385..8cf809f 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeIn.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeIn.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs b/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs index e3f21b7..ccb762b 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeLessThan.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs index 86b0574..14211f6 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeLessThanOrEqualTo.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeNull.cs b/Code/Light.GuardClauses/Check.MustNotBeNull.cs index 544a57b..76a451f 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNull.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNull.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs index 3ff1488..7beedb7 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNullOrEmpty.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs b/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs index e93d21b..de13b50 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNullOrWhiteSpace.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs index ffd3ce3..b6b5ee5 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeNullReference.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs b/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs index f4001b0..4ad82f8 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeOneOf.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs b/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs index 9b1af7f..59e9ef4 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeSameAs.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs b/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs index b098ae8..98cc45c 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeSubstringOf.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotContain.cs b/Code/Light.GuardClauses/Check.MustNotContain.cs index d9290ff..ddadc32 100644 --- a/Code/Light.GuardClauses/Check.MustNotContain.cs +++ b/Code/Light.GuardClauses/Check.MustNotContain.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotEndWith.cs b/Code/Light.GuardClauses/Check.MustNotEndWith.cs index 549a631..380dbba 100644 --- a/Code/Light.GuardClauses/Check.MustNotEndWith.cs +++ b/Code/Light.GuardClauses/Check.MustNotEndWith.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/Check.MustNotStartWith.cs b/Code/Light.GuardClauses/Check.MustNotStartWith.cs index d4cc668..0557068 100644 --- a/Code/Light.GuardClauses/Check.MustNotStartWith.cs +++ b/Code/Light.GuardClauses/Check.MustNotStartWith.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; @@ -24,8 +25,7 @@ public static string MustNotStartWith( [NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, - [CallerArgumentExpression("parameter")] - string? parameterName = null, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null ) { @@ -93,4 +93,95 @@ public static string MustNotStartWith( } return parameter; } + + /// + /// Ensures that the span does not start with the specified value, or otherwise throws a . + /// + /// The span to be checked. + /// The other span that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when starts with . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustNotStartWith( + this ReadOnlySpan parameter, + ReadOnlySpan value, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The other span that must not start with. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustNotStartWith( + this ReadOnlySpan parameter, + ReadOnlySpan value, + ReadOnlySpansExceptionFactory exceptionFactory + ) + where T : IEquatable + { + if (parameter.StartsWith(value)) + { + Throw.CustomSpanException(exceptionFactory, parameter, value); + } + + return parameter; + } + + /// + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The other span that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// + /// Thrown when is not a valid value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustNotStartWith( + this ReadOnlySpan parameter, + ReadOnlySpan value, + StringComparison comparisonType, + ReadOnlySpansExceptionFactory exceptionFactory + ) + { + if (parameter.StartsWith(value, comparisonType)) + { + Throw.CustomSpanException(exceptionFactory, parameter, value, comparisonType); + } + + return parameter; + } } diff --git a/Code/Light.GuardClauses/Check.MustStartWith.cs b/Code/Light.GuardClauses/Check.MustStartWith.cs index ce3328e..166c903 100644 --- a/Code/Light.GuardClauses/Check.MustStartWith.cs +++ b/Code/Light.GuardClauses/Check.MustStartWith.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.Exceptions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs new file mode 100644 index 0000000..ae03bf5 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs @@ -0,0 +1,1340 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +/// +/// Provides static factory methods that throw default exceptions. +/// +public static class Throw +{ + /// + /// Throws the default , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ArgumentNull(string? parameterName = null, string? message = null) => + throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); + + /// + /// Throws the default indicating that a value is the default value of its type, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ArgumentDefault(string? parameterName = null, string? message = null) => + throw new ArgumentDefaultException( + parameterName, + message ?? $"{parameterName ?? "The value"} must not be the default value." + ); + + /// + /// Throws the default indicating that a reference cannot be downcast, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidTypeCast( + object? parameter, + Type targetType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new TypeCastException( + parameterName, + message ?? + $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"." + ); + + /// + /// Throws the default indicating that a value is not one of the constants defined in an enum, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EnumValueNotDefined( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : Enum => + throw new EnumValueNotDefinedException( + parameterName, + message ?? + $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not." + ); + + /// + /// Throws the default indicating that a GUID is empty, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyGuid(string? parameterName = null, string? message = null) => + throw new EmptyGuidException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one." + ); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidState(string? message = null) => throw new InvalidStateException(message); + + /// + /// Throws an using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Argument(string? parameterName = null, string? message = null) => + throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidEmailAddress( + string parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidEmailAddressException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"." + ); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidEmailAddress( + ReadOnlySpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidEmailAddressException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"." + ); + + /// + /// Throws the default indicating that a has no value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NullableHasNoValue(string? parameterName = null, string? message = null) => + throw new NullableHasNoValueException( + parameterName, + message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null." + ); + + /// + /// Throws the default indicating that a comparable value must not be less than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeLessThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must be less than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must not be less than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeLessThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must not be greater than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeGreaterThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must be greater than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeGreaterThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must be greater than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeGreaterThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must not be greater than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeGreaterThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a comparable value must be less than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a value is not within a specified range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeInRange( + T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a value is within a specified range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeInRange( + T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that two references point to the same object, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SameObjectReference( + T? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : class => + throw new SameObjectReferenceException( + parameterName, + message ?? + $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does." + ); + + /// + /// Throws the default indicating that a string is empty, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyString(string? parameterName = null, string? message = null) => + throw new EmptyStringException( + parameterName, + message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is." + ); + + /// + /// Throws the default indicating that a string contains only white space, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void WhiteSpaceString( + string parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new WhiteSpaceStringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a string does not match a regular expression, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotMatch( + string parameter, + Regex regex, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringDoesNotMatchException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotContain( + string parameter, + string substring, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotContain( + string parameter, + string substring, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringContains( + string parameter, + string substring, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringContains( + string parameter, + string substring, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotSubstring( + string parameter, + string other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotSubstring( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Substring( + string parameter, + string other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Substring( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotStartWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith( + ReadOnlySpan parameter, + ReadOnlySpan other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}." + ); + + /// + /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotEndWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringEndsWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotShorterThan( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotShorterThanOrEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringLengthNotEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThan( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThanOrEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringLengthNotInRange( + string parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not equal to "\n" or "\r\n". + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotNewLine(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not trimmed. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmed(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not trimmed at the start. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not trimmed at the end. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}." + ); + + /// + /// Throws the default indicating that two values are not equal, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValuesNotEqual( + T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValuesNotEqualException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that two values are equal, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValuesEqual( + T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValuesEqualException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a collection has an invalid number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}." + ); + + /// + /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidSpanLength( + in Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must have length {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidSpanLength( + in ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a collection has less than a minimum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMinimumCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}." + ); + + /// + /// Throws the default indicating that a span is not longer than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThan( + in Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not longer than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThan( + in ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not longer than and not equal to the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThanOrEqualTo( + in Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not longer than and not equal to the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThanOrEqualTo( + in ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThan( + in Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThanOrEqualTo( + in Span parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThanOrEqualTo( + in ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThan( + in ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a collection has more than a maximum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMaximumCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}." + ); + + /// + /// Throws the default indicating that a collection has no items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyCollection(string? parameterName = null, string? message = null) => + throw new EmptyCollectionException( + parameterName, + message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is." + ); + + /// + /// Throws the default indicating that a collection is not containing the specified item, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MissingItem( + IEnumerable parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new MissingItemException( + parameterName, + message ?? + new StringBuilder().AppendLine( + $"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not." + ) + .AppendCollectionContent(parameter) + .ToString() + ); + + /// + /// Throws the default indicating that a collection contains the specified item that should not be part of it, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ExistingItem( + IEnumerable parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ExistingItemException( + parameterName, + message ?? + new StringBuilder().AppendLine( + $"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does." + ) + .AppendCollectionContent(parameter) + .ToString() + ); + + /// + /// Throws the default indicating that a value is not one of a specified collection of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValueNotOneOf( + TItem parameter, + IEnumerable items, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValueIsNotOneOfException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items") + .AppendItemsWithNewLine(items) + .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") + .ToString() + ); + + /// + /// Throws the default indicating that a value is one of a specified collection of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValueIsOneOf( + TItem parameter, + IEnumerable items, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValueIsOneOfException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items") + .AppendItemsWithNewLine(items) + .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") + .ToString() + ); + + /// + /// Throws the default indicating that a URI is relative instead of absolute, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeAbsoluteUri( + Uri parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new RelativeUriException( + parameterName, + message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI is absolute instead of relative, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeRelativeUri( + Uri parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new AbsoluteUriException( + parameterName, + message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI has an unexpected scheme, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveScheme( + Uri parameter, + string scheme, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidUriSchemeException( + parameterName, + message ?? + $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI does not use one of a set of expected schemes, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveOneSchemeOf( + Uri parameter, + IEnumerable schemes, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidUriSchemeException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes") + .AppendItemsWithNewLine(schemes) + .AppendLine($"but it actually is \"{parameter}\".") + .ToString() + ); + + /// + /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeUtcDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); + + /// + /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLocalDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); + + /// + /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeUnspecifiedDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); + + /// + /// Throws the exception that is returned by . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); + + /// + /// Throws the exception that is returned by . is passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory, T parameter) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); + + /// + /// Throws the exception that is returned by . and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory, T1 first, T2 second) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + + /// + /// Throws the exception that is returned by . , , and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException( + Func exceptionFactory, + T1 first, + T2 second, + T3 third + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + + /// + /// Throws the exception that is returned by . and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + SpanExceptionFactory exceptionFactory, + Span span, + T value + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); + + /// + /// Throws the exception that is returned by . is passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpanExceptionFactory exceptionFactory, + ReadOnlySpan span + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); + + /// + /// Throws the exception that is returned by . and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpanExceptionFactory exceptionFactory, + ReadOnlySpan span, + T value + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + + /// + /// Throws the exception that is returned by . and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpansExceptionFactory exceptionFactory, + ReadOnlySpan first, + ReadOnlySpan second + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + + /// + /// Throws the exception that is returned by . , + /// , and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpansExceptionFactory exceptionFactory, + ReadOnlySpan first, + ReadOnlySpan second, + T third + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); +} diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs deleted file mode 100644 index 104c6b2..0000000 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ /dev/null @@ -1,702 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.RegularExpressions; -using JetBrains.Annotations; -using Light.GuardClauses.FrameworkExtensions; - -namespace Light.GuardClauses.Exceptions; - -/// -/// Provides static factory methods that throw default exceptions. -/// -public static class Throw -{ - /// - /// Throws the default , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ArgumentNull(string? parameterName = null, string? message = null) => - throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); - - /// - /// Throws the default indicating that a value is the default value of its type, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ArgumentDefault(string? parameterName = null, string? message = null) => - throw new ArgumentDefaultException(parameterName, message ?? $"{parameterName ?? "The value"} must not be the default value."); - - /// - /// Throws the default indicating that a reference cannot be downcast, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidTypeCast(object? parameter, Type targetType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new TypeCastException(parameterName, message ?? $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"."); - - /// - /// Throws the default indicating that a value is not one of the constants defined in an enum, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EnumValueNotDefined(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : Enum => - throw new EnumValueNotDefinedException(parameterName, message ?? $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not."); - - /// - /// Throws the default indicating that a GUID is empty, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyGuid(string? parameterName = null, string? message = null) => - throw new EmptyGuidException(parameterName, message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one."); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidState(string? message = null) => throw new InvalidStateException(message); - - /// - /// Throws an using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Argument(string? parameterName = null, string? message = null) => - throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidEmailAddress(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"."); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidEmailAddress(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"."); - - /// - /// Throws the default indicating that a has no value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NullableHasNoValue(string? parameterName = null, string? message = null) => - throw new NullableHasNoValueException(parameterName, message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null."); - - /// - /// Throws the default indicating that a comparable value must not be less than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must be less than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must not be less than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must not be greater than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must be greater than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must be greater than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must not be greater than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a comparable value must be less than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a value is not within a specified range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that a value is within a specified range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable => - throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); - - /// - /// Throws the default indicating that two references point to the same object, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SameObjectReference(T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class => - throw new SameObjectReferenceException(parameterName, message ?? $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does."); - - /// - /// Throws the default indicating that a string is empty, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyString(string? parameterName = null, string? message = null) => - throw new EmptyStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is."); - - /// - /// Throws the default indicating that a string contains only white space, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void WhiteSpaceString(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"."); - - /// - /// Throws the default indicating that a string does not match a regular expression, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotMatch(string parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringDoesNotMatchException(parameterName, message ?? $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"."); - - /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringContains(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringContains(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotSubstring(string parameter, string other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotSubstring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Substring(string parameter, string other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Substring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotShorterThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotShorterThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringLengthNotEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotLongerThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotLongerThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringLengthNotInRange(string parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a string is not equal to "\n" or "\r\n". - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotNewLine(string? parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not trimmed. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmed(string? parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not trimmed at the start. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not trimmed at the end. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(string? parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => - throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}."); - - /// - /// Throws the default indicating that two values are not equal, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesNotEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new ValuesNotEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that two values are equal, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new ValuesEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); - - /// - /// Throws the default indicating that a collection has an invalid number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}."); - - /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidSpanLength(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must have length {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidSpanLength(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a collection has less than a minimum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMinimumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}."); - - /// - /// Throws the default indicating that a span is not longer than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThan(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not longer than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThan(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThan(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThan(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}."); - - /// - /// Throws the default indicating that a collection has more than a maximum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMaximumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}."); - - /// - /// Throws the default indicating that a collection has no items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyCollection(string? parameterName = null, string? message = null) => - throw new EmptyCollectionException(parameterName, message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is."); - - /// - /// Throws the default indicating that a collection is not containing the specified item, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MissingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new MissingItemException(parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not.") - .AppendCollectionContent(parameter) - .ToString()); - - /// - /// Throws the default indicating that a collection contains the specified item that should not be part of it, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ExistingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new ExistingItemException(parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does.") - .AppendCollectionContent(parameter) - .ToString()); - - /// - /// Throws the default indicating that a value is not one of a specified collection of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValueNotOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new ValueIsNotOneOfException(parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items") - .AppendItemsWithNewLine(items) - .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") - .ToString()); - - /// - /// Throws the default indicating that a value is one of a specified collection of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValueIsOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new ValueIsOneOfException(parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items") - .AppendItemsWithNewLine(items) - .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") - .ToString()); - - /// - /// Throws the default indicating that a URI is relative instead of absolute, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeAbsoluteUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new RelativeUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"."); - - /// - /// Throws the default indicating that a URI is absolute instead of relative, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeRelativeUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new AbsoluteUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"."); - - /// - /// Throws the default indicating that a URI has an unexpected scheme, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void UriMustHaveScheme(Uri parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidUriSchemeException(parameterName, message ?? $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"."); - - /// - /// Throws the default indicating that a URI does not use one of a set of expected schemes, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void UriMustHaveOneSchemeOf(Uri parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidUriSchemeException(parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes") - .AppendItemsWithNewLine(schemes) - .AppendLine($"but it actually is \"{parameter}\".") - .ToString()); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeUtcDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLocalDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeUnspecifiedDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => - throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); - - /// - /// Throws the exception that is returned by . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); - - /// - /// Throws the exception that is returned by . is passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T parameter) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); - - /// - /// Throws the exception that is returned by . , , and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException(SpanExceptionFactory exceptionFactory, in Span span, T value) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); - - /// - /// Throws the exception that is returned by . is passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span, T value) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); -} \ No newline at end of file diff --git a/Code/Light.GuardClauses/SpanDelegates.cs b/Code/Light.GuardClauses/SpanDelegates.cs index 0d4c018..0eecc93 100644 --- a/Code/Light.GuardClauses/SpanDelegates.cs +++ b/Code/Light.GuardClauses/SpanDelegates.cs @@ -15,4 +15,18 @@ namespace Light.GuardClauses; /// /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. /// -public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); \ No newline at end of file +public delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); + +/// +/// Represents a delegate that receives two spans and produces an exception. +/// +public delegate Exception ReadOnlySpansExceptionFactory(ReadOnlySpan span1, ReadOnlySpan span2); + +/// +/// Represents a delegate that receives two spans and a value as parameters and that produces an exception. +/// +public delegate Exception ReadOnlySpansExceptionFactory( + ReadOnlySpan span1, + ReadOnlySpan span2, + T value +); From 894c33136ed71997186bf387be7c12d97d3701c9 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Thu, 6 Mar 2025 06:21:46 +0100 Subject: [PATCH 106/123] refactor: break Throw up into several files Signed-off-by: Kenny Pflug --- .../SpanMustHaveLengthBenchmark.cs | 6 +- Code/Light.GuardClauses/Check.Equals.cs | 1 - .../Check.InvalidArgument.cs | 1 - .../Check.InvalidOperation.cs | 1 - .../Check.MustBeLongerThan.cs | 6 +- .../Check.MustBeLongerThanOrEqualTo.cs | 8 +- .../Check.MustBeShorterThan.cs | 6 +- .../Check.MustBeShorterThanOrEqualTo.cs | 6 +- .../Check.MustHaveLength.cs | 6 +- .../ExceptionFactory/Throw.Argument.cs | 16 + .../ExceptionFactory/Throw.ArgumentDefault.cs | 20 + .../ExceptionFactory/Throw.ArgumentNull.cs | 16 + .../ExceptionFactory/Throw.CustomException.cs | 113 ++ .../ExceptionFactory/Throw.DateTime.cs | 61 + .../ExceptionFactory/Throw.EmptyCollection.cs | 20 + .../ExceptionFactory/Throw.EmptyGuid.cs | 20 + .../ExceptionFactory/Throw.EmptyString.cs | 20 + .../Throw.EnumValueNotDefined.cs | 27 + .../ExceptionFactory/Throw.ExistingItem.cs | 35 + .../Throw.InvalidCollectionCount.cs | 29 + .../Throw.InvalidEmailAddress.cs | 42 + .../Throw.InvalidMaximumCollectionCount.cs | 29 + .../Throw.InvalidMinimumCollectionCount.cs | 29 + .../Throw.InvalidOperation.cs | 15 + .../ExceptionFactory/Throw.InvalidState.cs | 15 + .../ExceptionFactory/Throw.InvalidTypeCast.cs | 29 + .../ExceptionFactory/Throw.MissingItem.cs | 35 + .../Throw.MustBeGreaterThan.cs | 27 + .../Throw.MustBeGreaterThanOrEqualTo.cs | 27 + .../ExceptionFactory/Throw.MustBeLessThan.cs | 26 + .../Throw.MustBeLessThanOrEqualTo.cs | 27 + .../Throw.MustNotBeGreaterThan.cs | 27 + .../Throw.MustNotBeGreaterThanOrEqualTo.cs | 27 + .../Throw.MustNotBeLessThan.cs | 27 + .../Throw.MustNotBeLessThanOrEqualTo.cs | 27 + .../Throw.NotFileExtension.cs | 34 + .../ExceptionFactory/Throw.NotNewLine.cs | 21 + .../ExceptionFactory/Throw.NotTrimmed.cs | 21 + .../ExceptionFactory/Throw.NotTrimmedAtEnd.cs | 21 + .../Throw.NotTrimmedAtStart.cs | 21 + .../Throw.NullableHasNoValue.cs | 21 + .../ExceptionFactory/Throw.Range.cs | 45 + .../Throw.SameObjectReference.cs | 26 + .../ExceptionFactory/Throw.Span.cs | 100 ++ .../ExceptionFactory/Throw.StringContains.cs | 85 ++ .../Throw.StringDoesNotEndWith.cs | 30 + .../Throw.StringDoesNotMatch.cs | 28 + .../Throw.StringDoesNotStartWith.cs | 30 + .../ExceptionFactory/Throw.StringEndsWith.cs | 30 + .../ExceptionFactory/Throw.StringLength.cs | 117 ++ .../Throw.StringStartsWith.cs | 49 + .../ExceptionFactory/Throw.Substring.cs | 85 ++ .../ExceptionFactory/Throw.Uri.cs | 84 ++ .../ExceptionFactory/Throw.ValueIsOneOf.cs | 33 + .../ExceptionFactory/Throw.ValueNotOneOf.cs | 33 + .../ExceptionFactory/Throw.ValuesEqual.cs | 46 + .../Throw.WhiteSpaceString.cs | 26 + .../ExceptionFactory/Throw.cs | 1338 +---------------- 58 files changed, 1783 insertions(+), 1368 deletions(-) create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.Argument.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentDefault.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentNull.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.CustomException.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.DateTime.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyCollection.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyGuid.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyString.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.EnumValueNotDefined.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ExistingItem.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidCollectionCount.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidEmailAddress.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMaximumCollectionCount.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMinimumCollectionCount.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidOperation.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidState.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidTypeCast.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MissingItem.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThan.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThan.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThan.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThan.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThanOrEqualTo.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NotFileExtension.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NotNewLine.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmed.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtEnd.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtStart.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.NullableHasNoValue.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.Range.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.SameObjectReference.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.Span.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringContains.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotEndWith.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotMatch.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotStartWith.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringEndsWith.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringLength.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.StringStartsWith.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.Substring.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.Uri.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ValueIsOneOf.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ValueNotOneOf.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.ValuesEqual.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.WhiteSpaceString.cs diff --git a/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs b/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs index f49986c..0398812 100644 --- a/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/CollectionAssertions/SpanMustHaveLengthBenchmark.cs @@ -48,7 +48,7 @@ public static class SpanMustHaveLengthExtensions public static Span MustHaveLengthCopyByValue(this Span parameter, int length, string parameterName = null, string message = null) { if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); + Throw.InvalidSpanLength((ReadOnlySpan) parameter, length, parameterName, message); return parameter; } @@ -56,7 +56,7 @@ public static Span MustHaveLengthCopyByValue(this Span parameter, int l public static Span MustHaveLengthInParameter(in this Span parameter, int length, string parameterName = null, string message = null) { if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); + Throw.InvalidSpanLength((ReadOnlySpan) parameter, length, parameterName, message); return parameter; } @@ -64,7 +64,7 @@ public static Span MustHaveLengthInParameter(in this Span parameter, in public static ref Span MustHaveLengthInOut(ref this Span parameter, int length, string parameterName = null, string message = null) { if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); + Throw.InvalidSpanLength((ReadOnlySpan) parameter, length, parameterName, message); return ref parameter; } } diff --git a/Code/Light.GuardClauses/Check.Equals.cs b/Code/Light.GuardClauses/Check.Equals.cs index 07df57c..05f38e6 100644 --- a/Code/Light.GuardClauses/Check.Equals.cs +++ b/Code/Light.GuardClauses/Check.Equals.cs @@ -1,7 +1,6 @@ using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.InvalidArgument.cs b/Code/Light.GuardClauses/Check.InvalidArgument.cs index 0235254..29bf0ec 100644 --- a/Code/Light.GuardClauses/Check.InvalidArgument.cs +++ b/Code/Light.GuardClauses/Check.InvalidArgument.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; -using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.InvalidOperation.cs b/Code/Light.GuardClauses/Check.InvalidOperation.cs index 2b0e5c1..e0b3b79 100644 --- a/Code/Light.GuardClauses/Check.InvalidOperation.cs +++ b/Code/Light.GuardClauses/Check.InvalidOperation.cs @@ -1,7 +1,6 @@ using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -using Light.GuardClauses.Exceptions; namespace Light.GuardClauses; diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs index b6524d8..7b6cc14 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThan.cs @@ -74,11 +74,7 @@ public static Span MustBeLongerThan( string? message = null ) { - if (parameter.Length <= length) - { - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); - } - + ((ReadOnlySpan) parameter).MustBeLongerThan(length, parameterName, message); return parameter; } diff --git a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs index 63a5297..f85231b 100644 --- a/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeLongerThanOrEqualTo.cs @@ -74,11 +74,7 @@ public static Span MustBeLongerThanOrEqualTo( string? message = null ) { - if (parameter.Length < length) - { - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - } - + ((ReadOnlySpan) parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); return parameter; } @@ -116,7 +112,7 @@ SpanExceptionFactory exceptionFactory public static ReadOnlySpan MustBeLongerThanOrEqualTo( this ReadOnlySpan parameter, int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null ) { diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs index 7fb01e2..7e47e1e 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThan.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThan.cs @@ -74,11 +74,7 @@ public static Span MustBeShorterThan( string? message = null ) { - if (parameter.Length >= length) - { - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - } - + ((ReadOnlySpan) parameter).MustBeShorterThan(length, parameterName, message); return parameter; } diff --git a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs index 9354f3d..dc9903f 100644 --- a/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs +++ b/Code/Light.GuardClauses/Check.MustBeShorterThanOrEqualTo.cs @@ -74,11 +74,7 @@ public static Span MustBeShorterThanOrEqualTo( string? message = null ) { - if (parameter.Length > length) - { - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); - } - + ((ReadOnlySpan) parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); return parameter; } diff --git a/Code/Light.GuardClauses/Check.MustHaveLength.cs b/Code/Light.GuardClauses/Check.MustHaveLength.cs index 34932e8..a7d8d4f 100644 --- a/Code/Light.GuardClauses/Check.MustHaveLength.cs +++ b/Code/Light.GuardClauses/Check.MustHaveLength.cs @@ -74,11 +74,7 @@ public static Span MustHaveLength( string? message = null ) { - if (parameter.Length != length) - { - Throw.InvalidSpanLength(parameter, length, parameterName, message); - } - + ((ReadOnlySpan) parameter).MustHaveLength(length, parameterName, message); return parameter; } diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.Argument.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.Argument.cs new file mode 100644 index 0000000..b72caec --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.Argument.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws an using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Argument(string? parameterName = null, string? message = null) => + throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentDefault.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentDefault.cs new file mode 100644 index 0000000..7a63b47 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentDefault.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value is the default value of its + /// type, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ArgumentDefault(string? parameterName = null, string? message = null) => + throw new ArgumentDefaultException( + parameterName, + message ?? $"{parameterName ?? "The value"} must not be the default value." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentNull.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentNull.cs new file mode 100644 index 0000000..0a35f34 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ArgumentNull.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ArgumentNull(string? parameterName = null, string? message = null) => + throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.CustomException.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.CustomException.cs new file mode 100644 index 0000000..48d97cf --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.CustomException.cs @@ -0,0 +1,113 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the exception that is returned by . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); + + /// + /// Throws the exception that is returned by . is + /// passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory, T parameter) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); + + /// + /// Throws the exception that is returned by . and + /// are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException(Func exceptionFactory, T1 first, T2 second) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + + /// + /// Throws the exception that is returned by . , + /// , and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomException( + Func exceptionFactory, + T1 first, + T2 second, + T3 third + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + + /// + /// Throws the exception that is returned by . and + /// are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + SpanExceptionFactory exceptionFactory, + Span span, + T value + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); + + /// + /// Throws the exception that is returned by . is + /// passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpanExceptionFactory exceptionFactory, + ReadOnlySpan span + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); + + /// + /// Throws the exception that is returned by . and + /// are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpanExceptionFactory exceptionFactory, + ReadOnlySpan span, + T value + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + + /// + /// Throws the exception that is returned by . and + /// are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpansExceptionFactory exceptionFactory, + ReadOnlySpan first, + ReadOnlySpan second + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + + /// + /// Throws the exception that is returned by . , + /// , and are passed to . + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void CustomSpanException( + ReadOnlySpansExceptionFactory exceptionFactory, + ReadOnlySpan first, + ReadOnlySpan second, + T third + ) => + throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.DateTime.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.DateTime.cs new file mode 100644 index 0000000..865b60c --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.DateTime.cs @@ -0,0 +1,61 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeUtcDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); + + /// + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLocalDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); + + /// + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeUnspecifiedDateTime( + DateTime parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidDateTimeException( + parameterName, + message ?? + $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyCollection.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyCollection.cs new file mode 100644 index 0000000..fc3eb72 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyCollection.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection has no items, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyCollection(string? parameterName = null, string? message = null) => + throw new EmptyCollectionException( + parameterName, + message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyGuid.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyGuid.cs new file mode 100644 index 0000000..1a85dee --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyGuid.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a GUID is empty, using the optional + /// parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyGuid(string? parameterName = null, string? message = null) => + throw new EmptyGuidException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyString.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyString.cs new file mode 100644 index 0000000..03ccb67 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.EmptyString.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is empty, using the optional + /// parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyString(string? parameterName = null, string? message = null) => + throw new EmptyStringException( + parameterName, + message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is." + ); +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.EnumValueNotDefined.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.EnumValueNotDefined.cs new file mode 100644 index 0000000..b484837 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.EnumValueNotDefined.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value is not one of the + /// constants defined in an enum, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EnumValueNotDefined( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : Enum => + throw new EnumValueNotDefinedException( + parameterName, + message ?? + $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not." + ); +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ExistingItem.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ExistingItem.cs new file mode 100644 index 0000000..376a8f9 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ExistingItem.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection contains the specified item + /// that should not be part of it, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ExistingItem( + IEnumerable parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ExistingItemException( + parameterName, + message ?? + new StringBuilder() + .AppendLine( + $"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does." + ) + .AppendCollectionContent(parameter) + .ToString() + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidCollectionCount.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidCollectionCount.cs new file mode 100644 index 0000000..d67f05c --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidCollectionCount.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection has an invalid + /// number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidEmailAddress.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidEmailAddress.cs new file mode 100644 index 0000000..ebf4c44 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidEmailAddress.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidEmailAddress( + string parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidEmailAddressException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"." + ); + + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidEmailAddress( + ReadOnlySpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidEmailAddressException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMaximumCollectionCount.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMaximumCollectionCount.cs new file mode 100644 index 0000000..85730d7 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMaximumCollectionCount.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection has more than a + /// maximum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMaximumCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMinimumCollectionCount.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMinimumCollectionCount.cs new file mode 100644 index 0000000..4914ad1 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidMinimumCollectionCount.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection has less than a + /// minimum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMinimumCollectionCount( + IEnumerable parameter, + int count, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidOperation.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidOperation.cs new file mode 100644 index 0000000..0452941 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidOperation.cs @@ -0,0 +1,15 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidState.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidState.cs new file mode 100644 index 0000000..cbba7f9 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidState.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws an using the optional message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidState(string? message = null) => throw new InvalidStateException(message); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidTypeCast.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidTypeCast.cs new file mode 100644 index 0000000..4831b18 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.InvalidTypeCast.cs @@ -0,0 +1,29 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a reference cannot be downcast, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidTypeCast( + object? parameter, + Type targetType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new TypeCastException( + parameterName, + message ?? + $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MissingItem.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MissingItem.cs new file mode 100644 index 0000000..0d4b4cb --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MissingItem.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a collection is not containing the + /// specified item, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MissingItem( + IEnumerable parameter, + TItem item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new MissingItemException( + parameterName, + message ?? + new StringBuilder() + .AppendLine( + $"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not." + ) + .AppendCollectionContent(parameter) + .ToString() + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThan.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThan.cs new file mode 100644 index 0000000..cec5dcb --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThan.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must be greater + /// than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeGreaterThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrEqualTo.cs new file mode 100644 index 0000000..ce70b4e --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrEqualTo.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must be greater + /// than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeGreaterThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThan.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThan.cs new file mode 100644 index 0000000..9d913ed --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThan.cs @@ -0,0 +1,26 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must be less + /// than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrEqualTo.cs new file mode 100644 index 0000000..7afb410 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrEqualTo.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must be less + /// than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThan.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThan.cs new file mode 100644 index 0000000..743df30 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThan.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must not be + /// greater than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeGreaterThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThanOrEqualTo.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThanOrEqualTo.cs new file mode 100644 index 0000000..82df482 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeGreaterThanOrEqualTo.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must not be + /// greater than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeGreaterThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThan.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThan.cs new file mode 100644 index 0000000..e1f30f7 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThan.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must not be + /// less than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeLessThan( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThanOrEqualTo.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThanOrEqualTo.cs new file mode 100644 index 0000000..bb53462 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeLessThanOrEqualTo.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a comparable value must not be + /// less than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeLessThanOrEqualTo( + T parameter, + T boundary, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NotFileExtension.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotFileExtension.cs new file mode 100644 index 0000000..1cc1b81 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotFileExtension.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a valid file extension. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NotNewLine.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotNewLine.cs new file mode 100644 index 0000000..e871706 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotNewLine.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not equal to "\n" or "\r\n". + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotNewLine(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmed.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmed.cs new file mode 100644 index 0000000..a1f91e7 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmed.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not trimmed. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmed(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtEnd.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtEnd.cs new file mode 100644 index 0000000..ba7875a --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtEnd.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not trimmed at the end. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtStart.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtStart.cs new file mode 100644 index 0000000..047fe01 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NotTrimmedAtStart.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not trimmed at the start. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => + throw new StringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.NullableHasNoValue.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.NullableHasNoValue.cs new file mode 100644 index 0000000..f477ef4 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.NullableHasNoValue.cs @@ -0,0 +1,21 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a has + /// no value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NullableHasNoValue(string? parameterName = null, string? message = null) => + throw new NullableHasNoValueException( + parameterName, + message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.Range.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.Range.cs new file mode 100644 index 0000000..a4a14ef --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.Range.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value is not within a specified + /// range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeInRange( + T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." + ); + + /// + /// Throws the default indicating that a value is within a specified + /// range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeInRange( + T parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : IComparable => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.SameObjectReference.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.SameObjectReference.cs new file mode 100644 index 0000000..8db1ebe --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.SameObjectReference.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that two references point to the same + /// object, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SameObjectReference( + T? parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : class => + throw new SameObjectReferenceException( + parameterName, + message ?? + $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.Span.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.Span.cs new file mode 100644 index 0000000..a3c04bf --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.Span.cs @@ -0,0 +1,100 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a span has an invalid length, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidSpanLength( + ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not longer than the + /// specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThan( + ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not longer than and + /// not equal to the specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeLongerThanOrEqualTo( + ReadOnlySpan parameter, + int length, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the + /// specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThan( + ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a span is not shorter than the + /// specified length. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SpanMustBeShorterThanOrEqualTo( + ReadOnlySpan parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidCollectionCountException( + parameterName, + message ?? + $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringContains.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringContains.cs new file mode 100644 index 0000000..e072d3b --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringContains.cs @@ -0,0 +1,85 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotContain( + string parameter, + string substring, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotContain( + string parameter, + string substring, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringContains( + string parameter, + string substring, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringContains( + string parameter, + string substring, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotEndWith.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotEndWith.cs new file mode 100644 index 0000000..6f0cece --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotEndWith.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string does not end with another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotEndWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotMatch.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotMatch.cs new file mode 100644 index 0000000..61fc5eb --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotMatch.cs @@ -0,0 +1,28 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string does not match a regular + /// expression, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotMatch( + string parameter, + Regex regex, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringDoesNotMatchException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotStartWith.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotStartWith.cs new file mode 100644 index 0000000..e51e802 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringDoesNotStartWith.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string does not start with another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotStartWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringEndsWith.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringEndsWith.cs new file mode 100644 index 0000000..476f035 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringEndsWith.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string ends with another one, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringEndsWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringLength.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringLength.cs new file mode 100644 index 0000000..d392362 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringLength.cs @@ -0,0 +1,117 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not shorter than the given + /// length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotShorterThan( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not shorter or equal to the + /// given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotShorterThanOrEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string has a different length than the + /// specified one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringLengthNotEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not longer than the given + /// length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThan( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string is not longer than or equal to + /// the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThanOrEqualTo( + string parameter, + int length, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." + ); + + /// + /// Throws the default indicating that a string's length is not in within the + /// given range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringLengthNotInRange( + string parameter, + Range range, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new StringLengthException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.StringStartsWith.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringStartsWith.cs new file mode 100644 index 0000000..ad19dc6 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.StringStartsWith.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith( + ReadOnlySpan parameter, + ReadOnlySpan other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.Substring.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.Substring.cs new file mode 100644 index 0000000..0d4f93d --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.Substring.cs @@ -0,0 +1,85 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotSubstring( + string parameter, + string other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotSubstring( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Substring( + string parameter, + string other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Substring( + string parameter, + string other, + StringComparison comparisonType, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new SubstringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.Uri.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.Uri.cs new file mode 100644 index 0000000..4f7a804 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.Uri.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a URI is relative instead of absolute, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeAbsoluteUri( + Uri parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new RelativeUriException( + parameterName, + message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI is absolute instead of relative, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeRelativeUri( + Uri parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new AbsoluteUriException( + parameterName, + message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI has an unexpected scheme, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveScheme( + Uri parameter, + string scheme, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidUriSchemeException( + parameterName, + message ?? + $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"." + ); + + /// + /// Throws the default indicating that a URI does not use one of a set of + /// expected schemes, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveOneSchemeOf( + Uri parameter, + IEnumerable schemes, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new InvalidUriSchemeException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes") + .AppendItemsWithNewLine(schemes) + .AppendLine($"but it actually is \"{parameter}\".") + .ToString() + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueIsOneOf.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueIsOneOf.cs new file mode 100644 index 0000000..65809d1 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueIsOneOf.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value is one of a specified collection + /// of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValueIsOneOf( + TItem parameter, + IEnumerable items, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValueIsOneOfException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items") + .AppendItemsWithNewLine(items) + .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") + .ToString() + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueNotOneOf.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueNotOneOf.cs new file mode 100644 index 0000000..746b457 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValueNotOneOf.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value is not one of a specified + /// collection of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValueNotOneOf( + TItem parameter, + IEnumerable items, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValueIsNotOneOfException( + parameterName, + message ?? + new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items") + .AppendItemsWithNewLine(items) + .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") + .ToString() + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.ValuesEqual.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValuesEqual.cs new file mode 100644 index 0000000..05157e6 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.ValuesEqual.cs @@ -0,0 +1,46 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; +using Light.GuardClauses.FrameworkExtensions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that two values are not equal, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValuesNotEqual( + T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValuesNotEqualException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); + + /// + /// Throws the default indicating that two values are equal, using the optional + /// parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ValuesEqual( + T parameter, + T other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ValuesEqualException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.WhiteSpaceString.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.WhiteSpaceString.cs new file mode 100644 index 0000000..bae38b2 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.WhiteSpaceString.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.Exceptions; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a string contains only white space, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void WhiteSpaceString( + string parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new WhiteSpaceStringException( + parameterName, + message ?? + $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"." + ); +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs index ae03bf5..98a4046 100644 --- a/Code/Light.GuardClauses/ExceptionFactory/Throw.cs +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs @@ -1,1340 +1,6 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.RegularExpressions; -using JetBrains.Annotations; -using Light.GuardClauses.Exceptions; -using Light.GuardClauses.FrameworkExtensions; - -namespace Light.GuardClauses.ExceptionFactory; +namespace Light.GuardClauses.ExceptionFactory; /// /// Provides static factory methods that throw default exceptions. /// -public static class Throw -{ - /// - /// Throws the default , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ArgumentNull(string? parameterName = null, string? message = null) => - throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); - - /// - /// Throws the default indicating that a value is the default value of its type, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ArgumentDefault(string? parameterName = null, string? message = null) => - throw new ArgumentDefaultException( - parameterName, - message ?? $"{parameterName ?? "The value"} must not be the default value." - ); - - /// - /// Throws the default indicating that a reference cannot be downcast, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidTypeCast( - object? parameter, - Type targetType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new TypeCastException( - parameterName, - message ?? - $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"." - ); - - /// - /// Throws the default indicating that a value is not one of the constants defined in an enum, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EnumValueNotDefined( - T parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : Enum => - throw new EnumValueNotDefinedException( - parameterName, - message ?? - $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not." - ); - - /// - /// Throws the default indicating that a GUID is empty, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyGuid(string? parameterName = null, string? message = null) => - throw new EmptyGuidException( - parameterName, - message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one." - ); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidState(string? message = null) => throw new InvalidStateException(message); - - /// - /// Throws an using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Argument(string? parameterName = null, string? message = null) => - throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidEmailAddress( - string parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidEmailAddressException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"." - ); - - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidEmailAddress( - ReadOnlySpan parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidEmailAddressException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"." - ); - - /// - /// Throws the default indicating that a has no value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NullableHasNoValue(string? parameterName = null, string? message = null) => - throw new NullableHasNoValueException( - parameterName, - message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null." - ); - - /// - /// Throws the default indicating that a comparable value must not be less than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeLessThan( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must be less than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThan( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must not be less than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeLessThanOrEqualTo( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must not be greater than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeGreaterThanOrEqualTo( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must be greater than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeGreaterThanOrEqualTo( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must be greater than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeGreaterThan( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must not be greater than the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeGreaterThan( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a comparable value must be less than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThanOrEqualTo( - T parameter, - T boundary, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a value is not within a specified range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeInRange( - T parameter, - Range range, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that a value is within a specified range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeInRange( - T parameter, - Range range, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : IComparable => - throw new ArgumentOutOfRangeException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}." - ); - - /// - /// Throws the default indicating that two references point to the same object, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SameObjectReference( - T? parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) where T : class => - throw new SameObjectReferenceException( - parameterName, - message ?? - $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does." - ); - - /// - /// Throws the default indicating that a string is empty, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyString(string? parameterName = null, string? message = null) => - throw new EmptyStringException( - parameterName, - message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is." - ); - - /// - /// Throws the default indicating that a string contains only white space, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void WhiteSpaceString( - string parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new WhiteSpaceStringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"." - ); - - /// - /// Throws the default indicating that a string does not match a regular expression, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotMatch( - string parameter, - Regex regex, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringDoesNotMatchException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"." - ); - - /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotContain( - string parameter, - string substring, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotContain( - string parameter, - string substring, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringContains( - string parameter, - string substring, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringContains( - string parameter, - string substring, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotSubstring( - string parameter, - string other, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotSubstring( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Substring( - string parameter, - string other, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Substring( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotStartWith( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringStartsWith( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringStartsWith( - ReadOnlySpan parameter, - ReadOnlySpan other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}." - ); - - /// - /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotEndWith( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringEndsWith( - string parameter, - string other, - StringComparison comparisonType, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new SubstringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotShorterThan( - string parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotShorterThanOrEqualTo( - string parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringLengthNotEqualTo( - string parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotLongerThan( - string parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringNotLongerThanOrEqualTo( - string parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringLengthNotInRange( - string parameter, - Range range, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new StringLengthException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a string is not equal to "\n" or "\r\n". - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotNewLine(string? parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not trimmed. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmed(string? parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not trimmed at the start. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not trimmed at the end. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(string? parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => - throw new StringException( - parameterName, - message ?? - $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}." - ); - - /// - /// Throws the default indicating that two values are not equal, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesNotEqual( - T parameter, - T other, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new ValuesNotEqualException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that two values are equal, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesEqual( - T parameter, - T other, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new ValuesEqualException( - parameterName, - message ?? - $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}." - ); - - /// - /// Throws the default indicating that a collection has an invalid number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidCollectionCount( - IEnumerable parameter, - int count, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}." - ); - - /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidSpanLength( - in Span parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must have length {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidSpanLength( - in ReadOnlySpan parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a collection has less than a minimum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMinimumCollectionCount( - IEnumerable parameter, - int count, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}." - ); - - /// - /// Throws the default indicating that a span is not longer than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThan( - in Span parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not longer than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThan( - in ReadOnlySpan parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo( - in Span parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo( - in ReadOnlySpan parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThan( - in Span parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo( - in Span parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo( - in ReadOnlySpan parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a span is not shorter than the specified length. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void SpanMustBeShorterThan( - in ReadOnlySpan parameter, - int length, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}." - ); - - /// - /// Throws the default indicating that a collection has more than a maximum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMaximumCollectionCount( - IEnumerable parameter, - int count, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidCollectionCountException( - parameterName, - message ?? - $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}." - ); - - /// - /// Throws the default indicating that a collection has no items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EmptyCollection(string? parameterName = null, string? message = null) => - throw new EmptyCollectionException( - parameterName, - message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is." - ); - - /// - /// Throws the default indicating that a collection is not containing the specified item, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MissingItem( - IEnumerable parameter, - TItem item, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new MissingItemException( - parameterName, - message ?? - new StringBuilder().AppendLine( - $"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not." - ) - .AppendCollectionContent(parameter) - .ToString() - ); - - /// - /// Throws the default indicating that a collection contains the specified item that should not be part of it, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ExistingItem( - IEnumerable parameter, - TItem item, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new ExistingItemException( - parameterName, - message ?? - new StringBuilder().AppendLine( - $"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does." - ) - .AppendCollectionContent(parameter) - .ToString() - ); - - /// - /// Throws the default indicating that a value is not one of a specified collection of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValueNotOneOf( - TItem parameter, - IEnumerable items, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new ValueIsNotOneOfException( - parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items") - .AppendItemsWithNewLine(items) - .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") - .ToString() - ); - - /// - /// Throws the default indicating that a value is one of a specified collection of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValueIsOneOf( - TItem parameter, - IEnumerable items, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new ValueIsOneOfException( - parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items") - .AppendItemsWithNewLine(items) - .AppendLine($"but it actually is {parameter.ToStringOrNull()}.") - .ToString() - ); - - /// - /// Throws the default indicating that a URI is relative instead of absolute, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeAbsoluteUri( - Uri parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new RelativeUriException( - parameterName, - message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"." - ); - - /// - /// Throws the default indicating that a URI is absolute instead of relative, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeRelativeUri( - Uri parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new AbsoluteUriException( - parameterName, - message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"." - ); - - /// - /// Throws the default indicating that a URI has an unexpected scheme, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void UriMustHaveScheme( - Uri parameter, - string scheme, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidUriSchemeException( - parameterName, - message ?? - $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"." - ); - - /// - /// Throws the default indicating that a URI does not use one of a set of expected schemes, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void UriMustHaveOneSchemeOf( - Uri parameter, - IEnumerable schemes, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidUriSchemeException( - parameterName, - message ?? - new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes") - .AppendItemsWithNewLine(schemes) - .AppendLine($"but it actually is \"{parameter}\".") - .ToString() - ); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeUtcDateTime( - DateTime parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidDateTimeException( - parameterName, - message ?? - $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." - ); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLocalDateTime( - DateTime parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidDateTimeException( - parameterName, - message ?? - $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." - ); - - /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeUnspecifiedDateTime( - DateTime parameter, - [CallerArgumentExpression("parameter")] string? parameterName = null, - string? message = null - ) => - throw new InvalidDateTimeException( - parameterName, - message ?? - $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"." - ); - - /// - /// Throws the exception that is returned by . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); - - /// - /// Throws the exception that is returned by . is passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T parameter) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); - - /// - /// Throws the exception that is returned by . , , and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomException( - Func exceptionFactory, - T1 first, - T2 second, - T3 third - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException( - SpanExceptionFactory exceptionFactory, - Span span, - T value - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); - - /// - /// Throws the exception that is returned by . is passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException( - ReadOnlySpanExceptionFactory exceptionFactory, - ReadOnlySpan span - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException( - ReadOnlySpanExceptionFactory exceptionFactory, - ReadOnlySpan span, - T value - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); - - /// - /// Throws the exception that is returned by . and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException( - ReadOnlySpansExceptionFactory exceptionFactory, - ReadOnlySpan first, - ReadOnlySpan second - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); - - /// - /// Throws the exception that is returned by . , - /// , and are passed to . - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CustomSpanException( - ReadOnlySpansExceptionFactory exceptionFactory, - ReadOnlySpan first, - ReadOnlySpan second, - T third - ) => - throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); -} +public static partial class Throw; From f2d157fd2b8ea04e75038ecc9021131a666bda2b Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 06:05:28 +0100 Subject: [PATCH 107/123] feat: add IsApproximately for all types implementing INumber Signed-off-by: Kenny Pflug --- .../CommonAssertions/IsApproximatelyTests.cs | 58 ++++++++++++++++++- .../Check.IsApproximately.cs | 24 +++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs index c0abf2d..afbf5af 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs @@ -38,4 +38,60 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp [InlineData(5.0f, 15.0001f, 10.0f, false)] public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); -} \ No newline at end of file + +#if NET8_0 + [Theory] + [InlineData(1.1, 1.3, 0.5, true)] + [InlineData(100.55, 100.555, 0.00001, false)] + [InlineData(5.0, 14.999999, 10.0, true)] + [InlineData(5.0, 15.0, 10.0, false)] + [InlineData(5.0, 15.0001, 10.0, false)] + public static void GenericDoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) => + first.IsApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(1.1f, 1.3f, 0.5f, true)] + [InlineData(100.55f, 100.555f, 0.00001f, false)] + [InlineData(5.0f, 14.999999f, 10.0f, true)] + [InlineData(5.0f, 15.0f, 10.0f, false)] + [InlineData(5.0f, 15.0001f, 10.0f, false)] + public static void GenericFloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => + first.IsApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(5, 10, 10, true)] + [InlineData(5, 15, 10, false)] + [InlineData(-5, 5, 12, true)] + [InlineData(-100, 100, 199, false)] + [InlineData(42, 42, 1, true)] + public static void GenericIntWithCustomTolerance(int first, int second, int tolerance, bool expected) => + first.IsApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(5L, 10L, 10L, true)] + [InlineData(5L, 15L, 10L, false)] + [InlineData(-5L, 5L, 12L, true)] + [InlineData(-100L, 100L, 199L, false)] + [InlineData(42L, 42L, 1L, true)] + public static void GenericLongWithCustomTolerance(long first, long second, long tolerance, bool expected) => + first.IsApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [MemberData(nameof(DecimalTestData))] + public static void GenericDecimalWithCustomTolerance( + decimal first, + decimal second, + decimal tolerance, + bool expected + ) => + first.IsApproximately(second, tolerance).Should().Be(expected); + + public static TheoryData DecimalTestData() => new () + { + { 1.1m, 1.3m, 0.5m, true }, + { 100.55m, 100.555m, 0.00001m, false }, + { 5.0m, 14.999999m, 10.0m, true }, + { 5.0m, 15.0m, 10.0m, false }, + }; +#endif +} diff --git a/Code/Light.GuardClauses/Check.IsApproximately.cs b/Code/Light.GuardClauses/Check.IsApproximately.cs index 698450e..6ee6172 100644 --- a/Code/Light.GuardClauses/Check.IsApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsApproximately.cs @@ -1,5 +1,8 @@ using System; using System.Runtime.CompilerServices; +#if NET8_0 +using System.Numerics; +#endif namespace Light.GuardClauses; @@ -35,8 +38,8 @@ public static bool IsApproximately(this double value, double other) => /// /// Checks if the specified value is approximately the same as the other value, using the given tolerance. /// - /// The first value to compare. - /// The second value to compare. + /// The first value to be compared. + /// The second value to be compared. /// The tolerance indicating how much the two values may differ from each other. /// /// True if and are equal or if their absolute difference @@ -58,4 +61,21 @@ public static bool IsApproximately(this float value, float other, float toleranc [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) < 0.0001f; + +#if NET8_0 + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// The type that implements the interface. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this T value, T other, T tolerance) where T : INumber => + T.Abs(value - other) < tolerance; +#endif } From 7aeddc84173ba1aca5159c71bfddd12418f69fb8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 07:41:51 +0100 Subject: [PATCH 108/123] feat: add MustBeApproximately Signed-off-by: Kenny Pflug --- .../MustBeApproximatelyTests.cs | 236 ++++++++++++++ .../Check.MustBeApproximately.cs | 297 ++++++++++++++++++ .../Throw.MustBeApproximately.cs | 28 ++ 3 files changed, 561 insertions(+) create mode 100644 Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeApproximately.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeApproximately.cs diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs new file mode 100644 index 0000000..3204655 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs @@ -0,0 +1,236 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustBeApproximatelyTests +{ + [Theory] + [InlineData(5.1, 5.0, 0.2)] + [InlineData(10.3, 10.3, 0.01)] + [InlineData(3.14159, 3.14, 0.002)] + [InlineData(-42.0, -42.0001, 0.001)] + public static void ValuesApproximatelyEqual_Double(double value, double other, double tolerance) => + value.MustBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.1f, 5.0f, 0.2f)] + [InlineData(10.3f, 10.3f, 0.01f)] + [InlineData(3.14159f, 3.14f, 0.002f)] + [InlineData(-42.0f, -42.0001f, 0.001f)] + public static void ValuesApproximatelyEqual_Float(float value, float other, float tolerance) => + value.MustBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.0, 5.3, 0.1)] + [InlineData(100.0, 99.8, 0.1)] + [InlineData(-20.0, -20.2, 0.1)] + [InlineData(0.0001, 0.0002, 0.00005)] + public static void ValuesNotApproximatelyEqual_Double(double value, double other, double tolerance) + { + var act = () => value.MustBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Theory] + [InlineData(5.0f, 5.3f, 0.1f)] + [InlineData(100.0f, 99.8f, 0.1f)] + [InlineData(-20.0f, -20.2f, 0.1f)] + [InlineData(0.0001f, 0.0002f, 0.00005f)] + public static void ValuesNotApproximatelyEqual_Float(float value, float other, float tolerance) + { + var act = () => value.MustBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void DefaultTolerance_Double() + { + // Should pass - difference is 0.00005 which is less than default tolerance 0.0001 + const double value = 1.00005; + value.MustBeApproximately(1.0).Should().Be(value); + + // Should throw - difference is 0.0002 which is greater than default tolerance 0.0001 + Action act = () => 1.0002.MustBeApproximately(1.0, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void DefaultTolerance_Float() + { + // Should pass - difference is 0.00005f which is less than default tolerance 0.0001f + const float value = 1.00005f; + value.MustBeApproximately(1.0f).Should().Be(value); + + // Should throw - difference is 0.0002f which is greater than default tolerance 0.0001f + Action act = () => 1.0002f.MustBeApproximately(1.0f, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void CustomException_Double() => + Test.CustomException( + 5.0, + 5.3, + (x, y, exceptionFactory) => x.MustBeApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Double() => + Test.CustomException( + 5.0, + 5.5, + 0.1, + (x, y, z, exceptionFactory) => x.MustBeApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void CustomException_Float() => + Test.CustomException( + 5.0f, + 5.3f, + (x, y, exceptionFactory) => x.MustBeApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Float() => + Test.CustomException( + 5.0f, + 5.5f, + 0.1f, + (x, y, z, exceptionFactory) => x.MustBeApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Double() => + 5.0.MustBeApproximately(5.05, 0.1, (_, _, _) => null).Should().Be(5.0); + + [Fact] + public static void NoCustomExceptionThrown_Float() => + 5.0f.MustBeApproximately(5.05f, 0.1f, (_, _, _) => null).Should().Be(5.0f); + + [Fact] + public static void CustomMessage_Double() => + Test.CustomMessage( + message => 100.0.MustBeApproximately(101.0, 0.5, message: message) + ); + + [Fact] + public static void CustomMessage_Float() => + Test.CustomMessage( + message => 100.0f.MustBeApproximately(101.0f, 0.5f, message: message) + ); + + [Fact] + public static void CallerArgumentExpression_Double() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustBeApproximately(3.0); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Double() + { + const double pi = 3.14159; + + var act = () => pi.MustBeApproximately(3.0, 0.1); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + + [Fact] + public static void CallerArgumentExpression_Float() + { + const float seventyEightO1 = 78.1f; + + var act = () => seventyEightO1.MustBeApproximately(3.0f); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Float() + { + const float pi = 3.14159f; + + var act = () => pi.MustBeApproximately(3.0f, 0.1f); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + +#if NET8_0 + [Theory] + [InlineData(5.1, 5.0, 0.2)] + [InlineData(10.3, 10.3, 0.01)] + [InlineData(3.14159, 3.14, 0.002)] + [InlineData(-42.0, -42.0001, 0.001)] + public static void ValuesApproximatelyEqual_Generic(double value, double other, double tolerance) => + value.MustBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.0, 5.3, 0.1)] + [InlineData(100.0, 99.8, 0.1)] + [InlineData(-20.0, -20.2, 0.1)] + [InlineData(0.0001, 0.0002, 0.00005)] + public static void ValuesNotApproximatelyEqual_Generic(double value, double other, double tolerance) + { + var act = () => value.MustBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void CustomExceptionWithTolerance_Generic() => + Test.CustomException( + 5.0, + 5.3, + 0.2, + (x, y, t, exceptionFactory) => x.MustBeApproximately(y, t, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Generic() => + 5.0.MustBeApproximately(5.05, 0.1, (_, _, _) => null).Should().Be(5.0); + + [Fact] + public static void CustomMessage_Generic() => + Test.CustomMessage( + message => 100.0.MustBeApproximately(101.0, 0.5, message: message) + ); + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Generic() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustBeApproximately(3.0, 10.0); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } +#endif +} diff --git a/Code/Light.GuardClauses/Check.MustBeApproximately.cs b/Code/Light.GuardClauses/Check.MustBeApproximately.cs new file mode 100644 index 0000000..77bdf05 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeApproximately.cs @@ -0,0 +1,297 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; +#if NET8_0 +using System.Numerics; +#endif + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeApproximately( + this double parameter, + double other, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) => + parameter.MustBeApproximately(other, 0.0001, parameterName, message); + + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeApproximately( + this double parameter, + double other, + Func exceptionFactory + ) + { + if (!parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeApproximately( + this double parameter, + double other, + double tolerance, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeApproximately( + this double parameter, + double other, + double tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// not less than 0.0001f. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately( + this float parameter, + float other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustBeApproximately(other, 0.0001f, parameterName, message); + + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately( + this float parameter, + float other, + Func exceptionFactory + ) + { + if (!parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately( + this float parameter, + float other, + float tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately( + this float parameter, + float other, + float tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + +#if NET8_0 + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The type that implements the interface. + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeApproximately( + this T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// The type that implements the interface. + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeApproximately( + this T parameter, + T other, + T tolerance, + Func exceptionFactory + ) where T : INumber + { + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } +#endif +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeApproximately.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeApproximately.cs new file mode 100644 index 0000000..e4ba742 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeApproximately.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value must be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeApproximately( + T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}." + ); +} From 4e99de6997b794c654e534b794676158786df048 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 08:01:44 +0100 Subject: [PATCH 109/123] build: upgrade Light.GuardClauses.Performance to .NET 8 Signed-off-by: Kenny Pflug --- .../Light.GuardClauses.Performance.csproj | 2 +- .../Light.GuardClauses.Performance/Program.cs | 23 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj b/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj index 7ffe52c..cbf1418 100644 --- a/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj +++ b/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/Code/Light.GuardClauses.Performance/Program.cs b/Code/Light.GuardClauses.Performance/Program.cs index 6bdd825..b17928a 100644 --- a/Code/Light.GuardClauses.Performance/Program.cs +++ b/Code/Light.GuardClauses.Performance/Program.cs @@ -4,18 +4,17 @@ using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; -namespace Light.GuardClauses.Performance +namespace Light.GuardClauses.Performance; + +public static class Program { - public static class Program - { - private static IConfig DefaultConfiguration => - DefaultConfig - .Instance - .AddJob(Job.Default.WithRuntime(CoreRuntime.Core70)) - .AddJob(Job.Default.WithRuntime(ClrRuntime.Net48)) - .AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig())); + private static IConfig DefaultConfiguration => + DefaultConfig + .Instance + .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80)) + .AddJob(Job.Default.WithRuntime(ClrRuntime.Net48)) + .AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig())); - public static void Main(string[] arguments) => - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(arguments, DefaultConfiguration); - } + public static void Main(string[] arguments) => + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(arguments, DefaultConfiguration); } \ No newline at end of file From b13929909d311c7c0a17a94a0f6dc8afba525793 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 08:02:02 +0100 Subject: [PATCH 110/123] performance: add MustBeApproximatelyBenchmark Signed-off-by: Kenny Pflug --- .../MustBeApproximatelyBenchmark.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Code/Light.GuardClauses.Performance/ComparableAssertions/MustBeApproximatelyBenchmark.cs diff --git a/Code/Light.GuardClauses.Performance/ComparableAssertions/MustBeApproximatelyBenchmark.cs b/Code/Light.GuardClauses.Performance/ComparableAssertions/MustBeApproximatelyBenchmark.cs new file mode 100644 index 0000000..57364eb --- /dev/null +++ b/Code/Light.GuardClauses.Performance/ComparableAssertions/MustBeApproximatelyBenchmark.cs @@ -0,0 +1,25 @@ +using System; +using BenchmarkDotNet.Attributes; + +namespace Light.GuardClauses.Performance.ComparableAssertions; + +[MemoryDiagnoser] +// ReSharper disable once ClassCanBeSealed.Global -- Benchmark.NET derives from this class with dynamically created code +public class MustBeApproximatelyBenchmark +{ + [Benchmark] + public double MustBeApproximately() => 5.100001.MustBeApproximately(5.100000, 0.0001); + + [Benchmark(Baseline = true)] + public double Imperative() => Imperative(5.100001, 5.100000, 0.0001); + + private static double Imperative(double parameter, double other, double tolerance) + { + if (Math.Abs(parameter - other) > tolerance) + { + throw new ArgumentOutOfRangeException(nameof(parameter), $"Value is not approximately {other}"); + } + + return parameter; + } +} From 66be15f9f94a73813b7f05256faea3655632482c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 09:39:51 +0100 Subject: [PATCH 111/123] feat: add MustNotBeApproximately Signed-off-by: Kenny Pflug --- .../MustNotBeApproximatelyBenchmark.cs | 25 ++ .../MustNotBeApproximatelyTests.cs | 238 ++++++++++++++ .../Check.MustNotBeApproximately.cs | 297 ++++++++++++++++++ .../Throw.MustNotBeApproximately.cs | 28 ++ 4 files changed, 588 insertions(+) create mode 100644 Code/Light.GuardClauses.Performance/ComparableAssertions/MustNotBeApproximatelyBenchmark.cs create mode 100644 Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs create mode 100644 Code/Light.GuardClauses/Check.MustNotBeApproximately.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeApproximately.cs diff --git a/Code/Light.GuardClauses.Performance/ComparableAssertions/MustNotBeApproximatelyBenchmark.cs b/Code/Light.GuardClauses.Performance/ComparableAssertions/MustNotBeApproximatelyBenchmark.cs new file mode 100644 index 0000000..a65d4b8 --- /dev/null +++ b/Code/Light.GuardClauses.Performance/ComparableAssertions/MustNotBeApproximatelyBenchmark.cs @@ -0,0 +1,25 @@ +using System; +using BenchmarkDotNet.Attributes; + +namespace Light.GuardClauses.Performance.ComparableAssertions; + +[MemoryDiagnoser] +// ReSharper disable once ClassCanBeSealed.Global -- Benchmark.NET derives from this class with dynamically created code +public class MustNotBeApproximatelyBenchmark +{ + [Benchmark] + public double MustNotBeApproximately() => 5.2.MustNotBeApproximately(5.1, 0.5); + + [Benchmark(Baseline = true)] + public double Imperative() => Imperative(5.2, 5.1, 0.5); + + private static double Imperative(double parameter, double other, double tolerance) + { + if (Math.Abs(parameter - other) < tolerance) + { + throw new ArgumentOutOfRangeException(nameof(parameter), $"Value is approximately {other}"); + } + + return parameter; + } +} \ No newline at end of file diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs new file mode 100644 index 0000000..da9e7ea --- /dev/null +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs @@ -0,0 +1,238 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustNotBeApproximatelyTests +{ + [Theory] + [InlineData(5.3, 5.0, 0.2)] + [InlineData(10.4, 10.3, 0.01)] + [InlineData(3.15, 3.14, 0.001)] + [InlineData(-42.002, -42.0001, 0.001)] + public static void ValuesNotApproximatelyEqual_Double(double value, double other, double tolerance) => + value.MustNotBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.3f, 5.0f, 0.2f)] + [InlineData(10.4f, 10.3f, 0.01f)] + [InlineData(3.15f, 3.14f, 0.001f)] + [InlineData(-42.002f, -42.0001f, 0.001f)] + public static void ValuesNotApproximatelyEqual_Float(float value, float other, float tolerance) => + value.MustNotBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.0, 5.05, 0.1)] + [InlineData(100.0, 99.95, 0.1)] + [InlineData(-20.0, -20.05, 0.1)] + [InlineData(0.0001, 0.00015, 0.0001)] + public static void ValuesApproximatelyEqual_Double(double value, double other, double tolerance) + { + var act = () => value.MustNotBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Theory] + [InlineData(5.0f, 5.05f, 0.1f)] + [InlineData(100.0f, 99.95f, 0.1f)] + [InlineData(-20.0f, -20.05f, 0.1f)] + [InlineData(0.0001f, 0.00015f, 0.0001f)] + public static void ValuesApproximatelyEqual_Float(float value, float other, float tolerance) + { + var act = () => value.MustNotBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void DefaultTolerance_Double() + { + // Should throw - difference is 0.00005 which is less than default tolerance 0.0001 + const double value = 1.00005; + Action act = () => value.MustNotBeApproximately(1.0, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + + // Should pass - difference is 0.0002 which is greater than default tolerance 0.0001 + const double value2 = 1.0002; + value2.MustNotBeApproximately(1.0).Should().Be(value2); + } + + [Fact] + public static void DefaultTolerance_Float() + { + // Should throw - difference is 0.00005f which is less than default tolerance 0.0001f + const float value = 1.00005f; + Action act = () => value.MustNotBeApproximately(1.0f, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + + // Should pass - difference is 0.0002f which is greater than default tolerance 0.0001f + const float value2 = 1.0002f; + value2.MustNotBeApproximately(1.0f).Should().Be(value2); + } + + [Fact] + public static void CustomException_Double() => + Test.CustomException( + 5.0, + 5.0000000001, + (x, y, exceptionFactory) => x.MustNotBeApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Double() => + Test.CustomException( + 5.0, + 5.05, + 0.1, + (x, y, z, exceptionFactory) => x.MustNotBeApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void CustomException_Float() => + Test.CustomException( + 5.0f, + 5.000001f, + (x, y, exceptionFactory) => x.MustNotBeApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Float() => + Test.CustomException( + 5.0f, + 5.05f, + 0.1f, + (x, y, z, exceptionFactory) => x.MustNotBeApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Double() => + 5.2.MustNotBeApproximately(5.0, 0.1, (_, _, _) => null).Should().Be(5.2); + + [Fact] + public static void NoCustomExceptionThrown_Float() => + 5.2f.MustNotBeApproximately(5.0f, 0.1f, (_, _, _) => null).Should().Be(5.2f); + + [Fact] + public static void CustomMessage_Double() => + Test.CustomMessage( + message => 100.0.MustNotBeApproximately(100.05, 0.1, message: message) + ); + + [Fact] + public static void CustomMessage_Float() => + Test.CustomMessage( + message => 100.0f.MustNotBeApproximately(100.05f, 0.1f, message: message) + ); + + [Fact] + public static void CallerArgumentExpression_Double() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustNotBeApproximately(78.099999); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Double() + { + const double pi = 3.14159; + + var act = () => pi.MustNotBeApproximately(3.14, 0.01); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + + [Fact] + public static void CallerArgumentExpression_Float() + { + const float seventyEightO1 = 78.1f; + + var act = () => seventyEightO1.MustNotBeApproximately(78.100005f); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Float() + { + const float pi = 3.14159f; + + var act = () => pi.MustNotBeApproximately(3.14f, 0.01f); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + +#if NET8_0 + [Theory] + [InlineData(5.3, 5.0, 0.2)] + [InlineData(10.4, 10.3, 0.01)] + [InlineData(3.15, 3.14, 0.001)] + [InlineData(-42.002, -42.0001, 0.001)] + public static void ValuesNotApproximatelyEqual_Generic(double value, double other, double tolerance) => + value.MustNotBeApproximately(other, tolerance).Should().Be(value); + + [Theory] + [InlineData(5.0, 5.05, 0.1)] + [InlineData(100.0, 99.95, 0.1)] + [InlineData(-20.0, -20.05, 0.1)] + [InlineData(0.0001, 0.00015, 0.0001)] + public static void ValuesApproximatelyEqual_Generic(double value, double other, double tolerance) + { + var act = () => value.MustNotBeApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void CustomExceptionWithTolerance_Generic() => + Test.CustomException( + 5.0, + 5.05, + 0.1, + (x, y, t, exceptionFactory) => x.MustNotBeApproximately(y, t, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Generic() => + 5.2.MustNotBeApproximately(5.0, 0.1, (_, _, _) => null).Should().Be(5.2); + + [Fact] + public static void CustomMessage_Generic() => + Test.CustomMessage( + message => 100.0.MustNotBeApproximately(100.05, 0.1, message: message) + ); + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Generic() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustNotBeApproximately(78.0, 0.2); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } +#endif +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs b/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs new file mode 100644 index 0000000..909ffe0 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs @@ -0,0 +1,297 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; +#if NET8_0 +using System.Numerics; +#endif + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately( + this double parameter, + double other, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) => + parameter.MustNotBeApproximately(other, 0.0001, parameterName, message); + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately( + this double parameter, + double other, + Func exceptionFactory + ) + { + if (parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately( + this double parameter, + double other, + double tolerance, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately( + this double parameter, + double other, + double tolerance, + Func exceptionFactory + ) + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001f. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeApproximately( + this float parameter, + float other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustNotBeApproximately(other, 0.0001f, parameterName, message); + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeApproximately( + this float parameter, + float other, + Func exceptionFactory + ) + { + if (parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeApproximately( + this float parameter, + float other, + float tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeApproximately( + this float parameter, + float other, + float tolerance, + Func exceptionFactory + ) + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + +#if NET8_0 + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The type that implements the interface. + /// + /// Thrown when the absolute difference between and is + /// less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBeApproximately( + this T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// The type that implements the interface. + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBeApproximately( + this T parameter, + T other, + T tolerance, + Func exceptionFactory + ) where T : INumber + { + if (parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } +#endif +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeApproximately.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeApproximately.cs new file mode 100644 index 0000000..3b8adfa --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeApproximately.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value must not be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeApproximately( + T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}." + ); +} \ No newline at end of file From 7d8f0d99e072078f4951e9864257a84a1b2f6651 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 09:49:35 +0100 Subject: [PATCH 112/123] chore: add comment to clarify index usage in IsFileExtension method Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Check.IsFileExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Light.GuardClauses/Check.IsFileExtension.cs b/Code/Light.GuardClauses/Check.IsFileExtension.cs index d9c76cc..4e81452 100644 --- a/Code/Light.GuardClauses/Check.IsFileExtension.cs +++ b/Code/Light.GuardClauses/Check.IsFileExtension.cs @@ -62,6 +62,7 @@ public static bool IsFileExtension([NotNullWhen(true)] this string? value) => [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsFileExtension(this ReadOnlySpan value) { + // ReSharper disable once UseIndexFromEndExpression -- cannot use index from end expression in .NET Standard 2.0 if (value.Length <= 1 || value[0] != '.' || value[value.Length - 1] == '.') { return false; From 38a924941befab180ae7adc565da46cbab07a113 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 10:17:09 +0100 Subject: [PATCH 113/123] fix: IsApproximately now uses <= operator instead of the < operator Signed-off-by: Kenny Pflug --- .../CommonAssertions/IsApproximatelyTests.cs | 20 ++++++++++++------- .../Check.IsApproximately.cs | 10 +++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs index afbf5af..70db5a8 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs @@ -17,7 +17,8 @@ public static void DoubleWithDefaultTolerance(double first, double second, bool [InlineData(1.1, 1.3, 0.5, true)] [InlineData(100.55, 100.555, 0.00001, false)] [InlineData(5.0, 14.999999, 10.0, true)] - [InlineData(5.0, 15.0, 10.0, false)] + [InlineData(5.0, 15.0, 10.0, true)] + [InlineData(4.9998, 15.0, 10.0, false)] [InlineData(5.0, 15.0001, 10.0, false)] public static void DoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); @@ -34,7 +35,8 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp [InlineData(1.1f, 1.3f, 0.5f, true)] [InlineData(100.55f, 100.555f, 0.00001f, false)] [InlineData(5.0f, 14.999999f, 10.0f, true)] - [InlineData(5.0f, 15.0f, 10.0f, false)] + [InlineData(5.0f, 15.0f, 10.0f, true)] + [InlineData(4.99f, 15.0f, 10.0f, false)] [InlineData(5.0f, 15.0001f, 10.0f, false)] public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); @@ -44,7 +46,8 @@ public static void FloatWithCustomTolerance(float first, float second, float tol [InlineData(1.1, 1.3, 0.5, true)] [InlineData(100.55, 100.555, 0.00001, false)] [InlineData(5.0, 14.999999, 10.0, true)] - [InlineData(5.0, 15.0, 10.0, false)] + [InlineData(5.0, 15.0, 10.0, true)] + [InlineData(5.0, 15.000001, 10.0, false)] [InlineData(5.0, 15.0001, 10.0, false)] public static void GenericDoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); @@ -53,14 +56,16 @@ public static void GenericDoubleWithCustomTolerance(double first, double second, [InlineData(1.1f, 1.3f, 0.5f, true)] [InlineData(100.55f, 100.555f, 0.00001f, false)] [InlineData(5.0f, 14.999999f, 10.0f, true)] - [InlineData(5.0f, 15.0f, 10.0f, false)] + [InlineData(5.0f, 15.0f, 10.0f, true)] + [InlineData(5.0f, 15.01f, 10.0f, false)] [InlineData(5.0f, 15.0001f, 10.0f, false)] public static void GenericFloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); [Theory] [InlineData(5, 10, 10, true)] - [InlineData(5, 15, 10, false)] + [InlineData(5, 15, 10, true)] + [InlineData(4, 15, 10, false)] [InlineData(-5, 5, 12, true)] [InlineData(-100, 100, 199, false)] [InlineData(42, 42, 1, true)] @@ -69,7 +74,8 @@ public static void GenericIntWithCustomTolerance(int first, int second, int tole [Theory] [InlineData(5L, 10L, 10L, true)] - [InlineData(5L, 15L, 10L, false)] + [InlineData(5L, 15L, 10L, true)] + [InlineData(5L, 16L, 10L, false)] [InlineData(-5L, 5L, 12L, true)] [InlineData(-100L, 100L, 199L, false)] [InlineData(42L, 42L, 1L, true)] @@ -91,7 +97,7 @@ bool expected { 1.1m, 1.3m, 0.5m, true }, { 100.55m, 100.555m, 0.00001m, false }, { 5.0m, 14.999999m, 10.0m, true }, - { 5.0m, 15.0m, 10.0m, false }, + { 5.0m, 15.0m, 9.99m, false }, }; #endif } diff --git a/Code/Light.GuardClauses/Check.IsApproximately.cs b/Code/Light.GuardClauses/Check.IsApproximately.cs index 6ee6172..02ef101 100644 --- a/Code/Light.GuardClauses/Check.IsApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsApproximately.cs @@ -20,7 +20,7 @@ public static partial class Check /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this double value, double other, double tolerance) => - Math.Abs(value - other) < tolerance; + Math.Abs(value - other) <= tolerance; /// /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. @@ -33,7 +33,7 @@ public static bool IsApproximately(this double value, double other, double toler /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this double value, double other) => - Math.Abs(value - other) < 0.0001; + Math.Abs(value - other) <= 0.0001; /// /// Checks if the specified value is approximately the same as the other value, using the given tolerance. @@ -47,7 +47,7 @@ public static bool IsApproximately(this double value, double other) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this float value, float other, float tolerance) => - Math.Abs(value - other) < tolerance; + Math.Abs(value - other) <= tolerance; /// /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. @@ -60,7 +60,7 @@ public static bool IsApproximately(this float value, float other, float toleranc /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this float value, float other) => - Math.Abs(value - other) < 0.0001f; + Math.Abs(value - other) <= 0.0001f; #if NET8_0 /// @@ -76,6 +76,6 @@ public static bool IsApproximately(this float value, float other) => /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsApproximately(this T value, T other, T tolerance) where T : INumber => - T.Abs(value - other) < tolerance; + T.Abs(value - other) <= tolerance; #endif } From 4c5db07c86cfe4c46a4bdfa6526cf61ee737cf23 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 10:17:31 +0100 Subject: [PATCH 114/123] feat: add IsGreaterThanOrApproximately Signed-off-by: Kenny Pflug --- .../IsGreaterThanOrApproximatelyTests.cs | 57 ++++++++++++++++++- .../Check.IsGreaterThanOrApproximately.cs | 20 +++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs index d7d59d5..96ccedc 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs @@ -36,4 +36,59 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp [InlineData(1.0f, 2.0f, 0.1f, false)] public static void FloatWIthCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); -} \ No newline at end of file + +#if NET8_0 + [Theory] + [InlineData(15.91, 15.9, 0.1, true)] + [InlineData(24.449, 24.45, 0.0001, false)] + [InlineData(-3.12, -3.2, 0.001, true)] + [InlineData(2.369, 2.37, 0.0005, false)] + [InlineData(15.0, 14.0, 0.1, true)] // Greater than case + [InlineData(14.95, 15.0, 0.1, true)] // Approximately equal case + public static void GenericDoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) => + first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(2.0f, 1.0f, 0.1f, true)] + [InlineData(1.0f, 1.0f, 0.1f, true)] + [InlineData(1.0f, 1.1f, 0.01f, false)] + [InlineData(1.0f, 2.0f, 0.1f, false)] + [InlineData(2.1f, 2.0f, 0.01f, true)] // Greater than case + public static void GenericFloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => + first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(10, 5, 1, true)] // Greater than case + [InlineData(5, 5, 1, true)] // Equal case + [InlineData(5, 6, 1, true)] // Approximately equal case + [InlineData(5, 7, 1, false)] // Not greater than or approximately equal case + public static void GenericIntWithCustomTolerance(int first, int second, int tolerance, bool expected) => + first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(10L, 5L, 1L, true)] // Greater than case + [InlineData(5L, 5L, 1L, true)] // Equal case + [InlineData(5L, 6L, 1L, true)] // Approximately equal case + [InlineData(5L, 7L, 1L, false)] // Not greater than or approximately equal case + public static void GenericLongWithCustomTolerance(long first, long second, long tolerance, bool expected) => + first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [MemberData(nameof(DecimalTestData))] + public static void GenericDecimalWithCustomTolerance( + decimal first, + decimal second, + decimal tolerance, + bool expected + ) => + first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); + + public static TheoryData DecimalTestData() => new () + { + { 1.3m, 1.1m, 0.1m, true }, // Greater than case + { 1.1m, 1.1m, 0.1m, true }, // Equal case + { 1.0m, 1.1m, 0.2m, true }, // Approximately equal case + { 1.0m, 1.3m, 0.1m, false }, // Not greater than or approximately equal case + }; +#endif +} diff --git a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs index caca671..487540b 100644 --- a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs @@ -1,4 +1,7 @@ using System.Runtime.CompilerServices; +#if NET8_0 +using System.Numerics; +#endif namespace Light.GuardClauses; @@ -57,4 +60,21 @@ public static bool IsGreaterThanOrApproximately(this float value, float other, f [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); + +#if NET8_0 + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// The type that implements the interface. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this T value, T other, T tolerance) where T : INumber => + value > other || value.IsApproximately(other, tolerance); +#endif } From 041530476d7fe69033c09e55d79a3cd941d8800c Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 10:24:22 +0100 Subject: [PATCH 115/123] feat: add IsLessThanOrApproximately Signed-off-by: Kenny Pflug --- .../IsLessThanOrApproximatelyTests.cs | 52 +++++++++++++++++++ .../Check.IsLessThanOrApproximately.cs | 20 +++++++ 2 files changed, 72 insertions(+) diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs index c710ec2..6e774b8 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs @@ -36,4 +36,56 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp [InlineData(0f, -1f, 0.9f, false)] public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + +#if NET8_0 + [Theory] + [InlineData(13.25, 13.5, 0.1, true)] // Less than case + [InlineData(13.5, 13.5, 0.1, true)] // Equal case + [InlineData(13.55, 13.5, 0.1, true)] // Approximately equal case + [InlineData(13.7, 13.5, 0.1, false)] // Not less than or approximately equal case + public static void GenericDoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) => + first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(13.25f, 13.5f, 0.1f, true)] // Less than case + [InlineData(13.5f, 13.5f, 0.1f, true)] // Equal case + [InlineData(13.55f, 13.5f, 0.1f, true)] // Approximately equal case + [InlineData(13.7f, 13.5f, 0.1f, false)] // Not less than or approximately equal case + public static void GenericFloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => + first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(5, 10, 1, true)] // Less than case + [InlineData(5, 5, 1, true)] // Equal case + [InlineData(6, 5, 1, true)] // Approximately equal case + [InlineData(7, 5, 1, false)] // Not less than or approximately equal case + public static void GenericIntWithCustomTolerance(int first, int second, int tolerance, bool expected) => + first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [InlineData(5L, 10L, 1L, true)] // Less than case + [InlineData(5L, 5L, 1L, true)] // Equal case + [InlineData(6L, 5L, 1L, true)] // Approximately equal case + [InlineData(7L, 5L, 1L, false)] // Not less than or approximately equal case + public static void GenericLongWithCustomTolerance(long first, long second, long tolerance, bool expected) => + first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + + [Theory] + [MemberData(nameof(DecimalTestData))] + public static void GenericDecimalWithCustomTolerance( + decimal first, + decimal second, + decimal tolerance, + bool expected + ) => + first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); + + public static TheoryData DecimalTestData() => new () + { + { 1.0m, 1.2m, 0.1m, true }, // Less than case + { 1.1m, 1.1m, 0.1m, true }, // Equal case + { 1.2m, 1.1m, 0.1m, true }, // Approximately equal case + { 1.3m, 1.1m, 0.1m, false }, // Not less than or approximately equal case + }; +#endif } \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs index c24c1a0..89b109b 100644 --- a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs @@ -1,4 +1,7 @@ using System.Runtime.CompilerServices; +#if NET8_0 +using System.Numerics; +#endif namespace Light.GuardClauses; @@ -57,4 +60,21 @@ public static bool IsLessThanOrApproximately(this float value, float other, floa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); + +#if NET8_0 + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// The type that implements the interface. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this T value, T other, T tolerance) where T : INumber => + value < other || value.IsApproximately(other, tolerance); +#endif } From 444ca1a78238fbd52ecf3d0f07d710114a530de9 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 11:03:31 +0100 Subject: [PATCH 116/123] feat: add MustBeGreaterThanOrApproximately Signed-off-by: Kenny Pflug --- .../MustBeGreaterThanOrApproximatelyTests.cs | 295 ++++++++++++++++++ .../Check.MustBeGreaterThanOrApproximately.cs | 292 +++++++++++++++++ .../Throw.MustBeGreaterThanOrApproximately.cs | 28 ++ 3 files changed, 615 insertions(+) create mode 100644 Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrApproximately.cs diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs new file mode 100644 index 0000000..ff19f60 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs @@ -0,0 +1,295 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class CheckMustBeGreaterThanOrApproximatelyTests +{ + [Theory] + [InlineData(17.4, 17.3)] + [InlineData(19.9999999, 20.0)] + [InlineData(-5.49998, -5.5)] + [InlineData(0.0001, 0.0001)] + public static void EqualOrGreater_Double(double first, double second) => + first.MustBeGreaterThanOrApproximately(second).Should().Be(first); + + [Theory] + [InlineData(15.91, 15.9, 0.1)] + [InlineData(24.49999, 24.45, 0.0001)] + [InlineData(-3.12, -3.2, 0.001)] + [InlineData(2.369, 2.37, 0.05)] + public static void EqualOrGreaterWithTolerance_Double(double first, double second, double tolerance) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(100.225f, 100.2f)] + [InlineData(-5.9f, -5.900005f)] + [InlineData(0f, -0.02f)] + [InlineData(-0.00001f, 0f)] + public static void EqualOrGreater_Float(float first, float second) => + first.MustBeGreaterThanOrApproximately(second).Should().Be(first); + + [Theory] + [InlineData(2.0f, 1.0f, 0.1f)] + [InlineData(1.0f, 1.0f, 0.1f)] + [InlineData(1.01f, 1.1f, 0.1f)] + [InlineData(1.0f, 2.0f, 1.0f)] + public static void EqualOrGreaterWithTolerance_Float(float first, float second, float tolerance) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(5.0, 5.3, 0.1)] + [InlineData(100.0, 100.5, 0.1)] + [InlineData(-20.0, -19.8, 0.1)] + [InlineData(0.0001, 0.0003, 0.00005)] + public static void NotGreaterThanOrApproximately_Double(double value, double other, double tolerance) + { + var act = () => value.MustBeGreaterThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Theory] + [InlineData(5.0f, 5.3f, 0.1f)] + [InlineData(100.0f, 100.5f, 0.1f)] + [InlineData(-20.0f, -19.8f, 0.1f)] + [InlineData(0.0001f, 0.0003f, 0.00005f)] + public static void NotGreaterThanOrApproximately_Float(float value, float other, float tolerance) + { + var act = () => value.MustBeGreaterThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void DefaultTolerance_Double() + { + // Should pass - difference is 0.00005 which is less than default tolerance 0.0001 + const double value = 1.00005; + value.MustBeGreaterThanOrApproximately(1.0).Should().Be(value); + + // Should throw - difference is 0.0002 which is greater than default tolerance 0.0001 + Action act = () => 0.9998.MustBeGreaterThanOrApproximately(1.0, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void DefaultTolerance_Float() + { + // Should pass - difference is 0.00005f which is less than default tolerance 0.0001f + const float value = 1.00005f; + value.MustBeGreaterThanOrApproximately(1.0f).Should().Be(value); + + // Should throw - difference is 0.0002f which is greater than default tolerance 0.0001f + Action act = () => 0.9998f.MustBeGreaterThanOrApproximately(1.0f, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void CustomException_Double() => + Test.CustomException( + 5.0, + 5.3, + (x, y, exceptionFactory) => x.MustBeGreaterThanOrApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Double() => + Test.CustomException( + 5.0, + 5.5, + 0.1, + (x, y, z, exceptionFactory) => x.MustBeGreaterThanOrApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void CustomException_Float() => + Test.CustomException( + 5.0f, + 5.3f, + (x, y, exceptionFactory) => x.MustBeGreaterThanOrApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Float() => + Test.CustomException( + 5.0f, + 5.5f, + 0.1f, + (x, y, z, exceptionFactory) => x.MustBeGreaterThanOrApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Double() => + 5.2.MustBeGreaterThanOrApproximately(5.1, (_, _) => null).Should().Be(5.2); + + [Fact] + public static void NoCustomExceptionThrownWithTolerance_Double() => + 5.2.MustBeGreaterThanOrApproximately(5.0, 0.1, (_, _, _) => null).Should().Be(5.2); + + [Fact] + public static void NoCustomExceptionThrown_Float() => + 5.2f.MustBeGreaterThanOrApproximately(5.0f, (_, _) => null).Should().Be(5.2f); + + [Fact] + public static void NoCustomExceptionThrownWithTolerance_Float() => + 5.2f.MustBeGreaterThanOrApproximately(5.0f, 0.1f, (_, _, _) => null).Should().Be(5.2f); + + [Fact] + public static void CustomMessage_Double() => + Test.CustomMessage( + message => 100.0.MustBeGreaterThanOrApproximately(101.0, 0.5, message: message) + ); + + [Fact] + public static void CustomMessage_Float() => + Test.CustomMessage( + message => 100.0f.MustBeGreaterThanOrApproximately(101.0f, 0.5f, message: message) + ); + + [Fact] + public static void CallerArgumentExpression_Double() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustBeGreaterThanOrApproximately(79.0); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Double() + { + const double pi = 3.14159; + + var act = () => pi.MustBeGreaterThanOrApproximately(3.5, 0.1); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + + [Fact] + public static void CallerArgumentExpression_Float() + { + const float seventyEightO1 = 78.1f; + + var act = () => seventyEightO1.MustBeGreaterThanOrApproximately(79.0f); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Float() + { + const float pi = 3.14159f; + + var act = () => pi.MustBeGreaterThanOrApproximately(3.5f, 0.1f); + + act.Should().Throw() + .WithParameterName(nameof(pi)); + } + +#if NET8_0 + [Theory] + [InlineData(15.91, 15.9, 0.1)] + [InlineData(24.4999, 24.45, 0.0001)] + [InlineData(-3.12, -3.2, 0.001)] + [InlineData(2.369, 2.37, 0.05)] + [InlineData(15.0, 14.0, 0.1)] // Greater than case + [InlineData(14.95, 15.0, 0.1)] // Approximately equal case + public static void EqualOrGreaterWithCustomTolerance_GenericDouble(double first, double second, double tolerance) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(10, 5, 1)] // Greater than case + [InlineData(5, 5, 1)] // Equal case + [InlineData(5, 6, 1)] // Approximately equal case + [InlineData(5, 7, 2)] // Not greater than or approximately equal case + public static void EqualOrGreaterWithCustomTolerance_GenericInt32(int first, int second, int tolerance) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(10L, 5L, 1L)] // Greater than case + [InlineData(5L, 5L, 1L)] // Equal case + [InlineData(5L, 6L, 1L)] // Approximately equal case + [InlineData(4L, 7L, 3L)] // Not greater than or approximately equal case + public static void EqualOrGreaterWithCustomTolerance_GenericInt64(long first, long second, long tolerance) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [MemberData(nameof(DecimalTestData))] + public static void GenericDecimalWithCustomTolerance( + decimal first, + decimal second, + decimal tolerance + ) => + first.MustBeGreaterThanOrApproximately(second, tolerance).Should().Be(first); + + public static TheoryData DecimalTestData() => new () + { + { 1.3m, 1.1m, 0.1m }, // Greater than case + { 1.1m, 1.1m, 0.1m }, // Equal case + { 1.0m, 1.1m, 0.2m }, // Approximately equal case + { 1.292m, 1.3m, 0.1m }, // Not greater than or approximately equal case + }; + + [Theory] + [InlineData(5.0, 5.3, 0.1)] + [InlineData(100.0, 100.5, 0.1)] + [InlineData(-20.0, -19.8, 0.1)] + [InlineData(0.0001, 0.0003, 0.00005)] + public static void NotGreaterThanOrApproximately_Generic(double value, double other, double tolerance) + { + var act = () => value.MustBeGreaterThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void CustomExceptionWithTolerance_Generic() => + Test.CustomException( + 5.0, + 5.5, + 0.1, + (x, y, t, exceptionFactory) => x.MustBeGreaterThanOrApproximately(y, t, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Generic() => + 5.2.MustBeGreaterThanOrApproximately(5.0, 0.1, (_, _, _) => null).Should().Be(5.2); + + [Fact] + public static void CustomMessage_Generic() => + Test.CustomMessage( + message => 100.0.MustBeGreaterThanOrApproximately(101.0, 0.5, message: message) + ); + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Generic() + { + const double seventyEightO1 = 78.1; + + var act = () => seventyEightO1.MustBeGreaterThanOrApproximately(79.0, 0.5); + + act.Should().Throw() + .WithParameterName(nameof(seventyEightO1)); + } +#endif +} diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs new file mode 100644 index 0000000..a0cd395 --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs @@ -0,0 +1,292 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; +#if NET8_0 +using System.Numerics; +#endif + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeGreaterThanOrApproximately( + this double parameter, + double other, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) => + parameter.MustBeGreaterThanOrApproximately(other, 0.0001, parameterName, message); + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeGreaterThanOrApproximately( + this double parameter, + double other, + Func exceptionFactory + ) + { + if (!parameter.IsGreaterThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeGreaterThanOrApproximately( + this double parameter, + double other, + double tolerance, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeGreaterThanOrApproximately( + this double parameter, + double other, + double tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeGreaterThanOrApproximately( + this float parameter, + float other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustBeGreaterThanOrApproximately(other, 0.0001f, parameterName, message); + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeGreaterThanOrApproximately( + this float parameter, + float other, + Func exceptionFactory + ) + { + if (!parameter.IsGreaterThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeGreaterThanOrApproximately( + this float parameter, + float other, + float tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeGreaterThanOrApproximately( + this float parameter, + float other, + float tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + +#if NET8_0 + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The type that implements the interface. + /// + /// Thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeGreaterThanOrApproximately( + this T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is not greater than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeGreaterThanOrApproximately( + this T parameter, + T other, + T tolerance, + Func exceptionFactory + ) where T : INumber + { + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } +#endif +} diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrApproximately.cs new file mode 100644 index 0000000..a67a571 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeGreaterThanOrApproximately.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value must be greater than or approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeGreaterThanOrApproximately( + T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}." + ); +} From b02763a25ea7ef067c57ea26250b330325cad104 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sat, 8 Mar 2025 11:28:18 +0100 Subject: [PATCH 117/123] feat: add MustBeLessThanOrApproximately Signed-off-by: Kenny Pflug --- .../MustBeLessThanOrApproximatelyTests.cs | 295 ++++++++++++++++++ .../Check.MustBeLessThanOrApproximately.cs | 292 +++++++++++++++++ .../Throw.MustBeLessThanOrApproximately.cs | 28 ++ 3 files changed, 615 insertions(+) create mode 100644 Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs create mode 100644 Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs create mode 100644 Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrApproximately.cs diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs new file mode 100644 index 0000000..9eace40 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs @@ -0,0 +1,295 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustBeLessThanOrApproximatelyTests +{ + [Theory] + [InlineData(17.3, 17.4)] + [InlineData(19.9999999, 20.0)] + [InlineData(-5.5, -5.49998)] + [InlineData(0.0001, 0.0001)] + public static void EqualOrLess_Double(double first, double second) => + first.MustBeLessThanOrApproximately(second).Should().Be(first); + + [Theory] + [InlineData(15.9, 15.91, 0.1)] + [InlineData(24.45, 24.49999, 0.0001)] + [InlineData(-3.2, -3.12, 0.001)] + [InlineData(2.37, 2.369, 0.05)] + public static void EqualOrLessWithTolerance_Double(double first, double second, double tolerance) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(100.2f, 100.225f)] + [InlineData(-5.900005f, -5.9f)] + [InlineData(-0.02f, 0f)] + [InlineData(0f, 0.00001f)] + public static void EqualOrLess_Float(float first, float second) => + first.MustBeLessThanOrApproximately(second).Should().Be(first); + + [Theory] + [InlineData(1.0f, 2.0f, 0.1f)] + [InlineData(1.0f, 1.0f, 0.1f)] + [InlineData(1.1f, 1.01f, 0.1f)] + [InlineData(1.0f, 2.0f, 1.0f)] + public static void EqualOrLessWithTolerance_Float(float first, float second, float tolerance) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(5.3, 5.0, 0.1)] + [InlineData(100.5, 100.0, 0.1)] + [InlineData(-19.8, -20.0, 0.1)] + [InlineData(0.0003, 0.0001, 0.00005)] + public static void NotLessThanOrApproximately_Double(double value, double other, double tolerance) + { + var act = () => value.MustBeLessThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Theory] + [InlineData(5.3f, 5.0f, 0.1f)] + [InlineData(100.5f, 100.0f, 0.1f)] + [InlineData(-19.8f, -20.0f, 0.1f)] + [InlineData(0.0003f, 0.0001f, 0.00005f)] + public static void NotLessThanOrApproximately_Float(float value, float other, float tolerance) + { + var act = () => value.MustBeLessThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void DefaultTolerance_Double() + { + // Should pass - difference is 0.00005 which is less than default tolerance 0.0001 + const double value = 1.0; + value.MustBeLessThanOrApproximately(1.00005).Should().Be(value); + + // Should throw - difference is 0.0002 which is greater than default tolerance 0.0001 + Action act = () => 1.0002.MustBeLessThanOrApproximately(1.0, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void DefaultTolerance_Float() + { + // Should pass - difference is 0.00005f which is less than default tolerance 0.0001f + const float value = 1.0f; + value.MustBeLessThanOrApproximately(1.00005f).Should().Be(value); + + // Should throw - difference is 0.0002f which is greater than default tolerance 0.0001f + Action act = () => 1.0002f.MustBeLessThanOrApproximately(1.0f, "parameter"); + act.Should().Throw() + .WithParameterName("parameter"); + } + + [Fact] + public static void CustomException_Double() => + Test.CustomException( + 5.3, + 5.0, + (x, y, exceptionFactory) => x.MustBeLessThanOrApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Double() => + Test.CustomException( + 5.5, + 5.0, + 0.1, + (x, y, z, exceptionFactory) => x.MustBeLessThanOrApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void CustomException_Float() => + Test.CustomException( + 5.3f, + 5.0f, + (x, y, exceptionFactory) => x.MustBeLessThanOrApproximately(y, exceptionFactory) + ); + + [Fact] + public static void CustomExceptionWithTolerance_Float() => + Test.CustomException( + 5.5f, + 5.0f, + 0.1f, + (x, y, z, exceptionFactory) => x.MustBeLessThanOrApproximately(y, z, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Double() => + 5.1.MustBeLessThanOrApproximately(5.2, (_, _) => null).Should().Be(5.1); + + [Fact] + public static void NoCustomExceptionThrownWithTolerance_Double() => + 5.0.MustBeLessThanOrApproximately(5.2, 0.1, (_, _, _) => null).Should().Be(5.0); + + [Fact] + public static void NoCustomExceptionThrown_Float() => + 5.0f.MustBeLessThanOrApproximately(5.2f, (_, _) => null).Should().Be(5.0f); + + [Fact] + public static void NoCustomExceptionThrownWithTolerance_Float() => + 5.0f.MustBeLessThanOrApproximately(5.2f, 0.1f, (_, _, _) => null).Should().Be(5.0f); + + [Fact] + public static void CustomMessage_Double() => + Test.CustomMessage( + message => 101.0.MustBeLessThanOrApproximately(100.0, 0.5, message: message) + ); + + [Fact] + public static void CustomMessage_Float() => + Test.CustomMessage( + message => 101.0f.MustBeLessThanOrApproximately(100.0f, 0.5f, message: message) + ); + + [Fact] + public static void CallerArgumentExpression_Double() + { + const double seventyNinePoint0 = 79.0; + + var act = () => seventyNinePoint0.MustBeLessThanOrApproximately(78.1); + + act.Should().Throw() + .WithParameterName(nameof(seventyNinePoint0)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Double() + { + const double threePointFive = 3.5; + + var act = () => threePointFive.MustBeLessThanOrApproximately(3.14159, 0.1); + + act.Should().Throw() + .WithParameterName(nameof(threePointFive)); + } + + [Fact] + public static void CallerArgumentExpression_Float() + { + const float seventyNinePoint0 = 79.0f; + + var act = () => seventyNinePoint0.MustBeLessThanOrApproximately(78.1f); + + act.Should().Throw() + .WithParameterName(nameof(seventyNinePoint0)); + } + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Float() + { + const float threePointFive = 3.5f; + + var act = () => threePointFive.MustBeLessThanOrApproximately(3.14159f, 0.1f); + + act.Should().Throw() + .WithParameterName(nameof(threePointFive)); + } + +#if NET8_0 + [Theory] + [InlineData(15.9, 15.91, 0.1)] + [InlineData(24.45, 24.4999, 0.0001)] + [InlineData(-3.2, -3.12, 0.001)] + [InlineData(2.37, 2.369, 0.05)] + [InlineData(14.0, 15.0, 0.1)] // Less than case + [InlineData(15.0, 14.95, 0.1)] // Approximately equal case + public static void EqualOrLessWithCustomTolerance_GenericDouble(double first, double second, double tolerance) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(5, 10, 1)] // Less than case + [InlineData(5, 5, 1)] // Equal case + [InlineData(6, 5, 1)] // Approximately equal case + [InlineData(7, 5, 2)] // Not less than or approximately equal case + public static void EqualOrLessWithCustomTolerance_GenericInt32(int first, int second, int tolerance) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [InlineData(5L, 10L, 1L)] // Less than case + [InlineData(5L, 5L, 1L)] // Equal case + [InlineData(6L, 5L, 1L)] // Approximately equal case + [InlineData(7L, 4L, 3L)] // Not less than or approximately equal case + public static void EqualOrLessWithCustomTolerance_GenericInt64(long first, long second, long tolerance) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + [Theory] + [MemberData(nameof(DecimalTestData))] + public static void GenericDecimalWithCustomTolerance( + decimal first, + decimal second, + decimal tolerance + ) => + first.MustBeLessThanOrApproximately(second, tolerance).Should().Be(first); + + public static TheoryData DecimalTestData() => new () + { + { 1.1m, 1.3m, 0.1m }, // Less than case + { 1.1m, 1.1m, 0.1m }, // Equal case + { 1.1m, 1.0m, 0.2m }, // Approximately equal case + { 1.3m, 1.292m, 0.1m }, // Not less than or approximately equal case + }; + + [Theory] + [InlineData(5.3, 5.0, 0.1)] + [InlineData(100.5, 100.0, 0.1)] + [InlineData(-19.8, -20.0, 0.1)] + [InlineData(0.0003, 0.0001, 0.00005)] + public static void NotLessThanOrApproximately_Generic(double value, double other, double tolerance) + { + var act = () => value.MustBeLessThanOrApproximately(other, tolerance, nameof(value)); + + var exceptionAssertion = act.Should().Throw().Which; + exceptionAssertion.Message.Should().Contain( + $"{nameof(value)} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}." + ); + exceptionAssertion.ParamName.Should().BeSameAs(nameof(value)); + } + + [Fact] + public static void CustomExceptionWithTolerance_Generic() => + Test.CustomException( + 5.5, + 5.0, + 0.1, + (x, y, t, exceptionFactory) => x.MustBeLessThanOrApproximately(y, t, exceptionFactory) + ); + + [Fact] + public static void NoCustomExceptionThrown_Generic() => + 5.0.MustBeLessThanOrApproximately(5.2, 0.1, (_, _, _) => null).Should().Be(5.0); + + [Fact] + public static void CustomMessage_Generic() => + Test.CustomMessage( + message => 101.0.MustBeLessThanOrApproximately(100.0, 0.5, message: message) + ); + + [Fact] + public static void CallerArgumentExpressionWithTolerance_Generic() + { + const double seventyNinePoint0 = 79.0; + + var act = () => seventyNinePoint0.MustBeLessThanOrApproximately(78.1, 0.5); + + act.Should().Throw() + .WithParameterName(nameof(seventyNinePoint0)); + } +#endif +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs new file mode 100644 index 0000000..f48bf5d --- /dev/null +++ b/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs @@ -0,0 +1,292 @@ +using System; +using System.Runtime.CompilerServices; +using Light.GuardClauses.ExceptionFactory; +#if NET8_0 +using System.Numerics; +#endif + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeLessThanOrApproximately( + this double parameter, + double other, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) => + parameter.MustBeLessThanOrApproximately(other, 0.0001, parameterName, message); + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeLessThanOrApproximately( + this double parameter, + double other, + Func exceptionFactory + ) + { + if (!parameter.IsLessThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeLessThanOrApproximately( + this double parameter, + double other, + double tolerance, + [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeLessThanOrApproximately( + this double parameter, + double other, + double tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeLessThanOrApproximately( + this float parameter, + float other, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + parameter.MustBeLessThanOrApproximately(other, 0.0001f, parameterName, message); + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeLessThanOrApproximately( + this float parameter, + float other, + Func exceptionFactory + ) + { + if (!parameter.IsLessThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeLessThanOrApproximately( + this float parameter, + float other, + float tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeLessThanOrApproximately( + this float parameter, + float other, + float tolerance, + Func exceptionFactory + ) + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } + +#if NET8_0 + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The type that implements the interface. + /// + /// Thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeLessThanOrApproximately( + this T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeLessThanOrApproximately( + this T parameter, + T other, + T tolerance, + Func exceptionFactory + ) where T : INumber + { + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + + return parameter; + } +#endif +} \ No newline at end of file diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrApproximately.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrApproximately.cs new file mode 100644 index 0000000..40827d8 --- /dev/null +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.MustBeLessThanOrApproximately.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Light.GuardClauses.ExceptionFactory; + +public static partial class Throw +{ + /// + /// Throws the default indicating that a value must be less than or approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThanOrApproximately( + T parameter, + T other, + T tolerance, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? + $"{parameterName ?? "The value"} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}." + ); +} \ No newline at end of file From 34efb3d0539c867fd0735d0dcca3b712d360652a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 19:11:47 +0100 Subject: [PATCH 118/123] refactor: move SpanDelegates to Light.GuardClauses.ExceptionFactory namespace Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs | 1 + Code/Light.GuardClauses.Tests/Test.cs | 1 + Code/Light.GuardClauses/{ => ExceptionFactory}/SpanDelegates.cs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) rename Code/Light.GuardClauses/{ => ExceptionFactory}/SpanDelegates.cs (96%) diff --git a/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs b/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs index 32536e1..725c9ec 100644 --- a/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs +++ b/Code/Light.GuardClauses.Tests/ExecuteReadOnlySpanAssertion.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using Light.GuardClauses.ExceptionFactory; namespace Light.GuardClauses.Tests; diff --git a/Code/Light.GuardClauses.Tests/Test.cs b/Code/Light.GuardClauses.Tests/Test.cs index 42922f5..d2956ea 100644 --- a/Code/Light.GuardClauses.Tests/Test.cs +++ b/Code/Light.GuardClauses.Tests/Test.cs @@ -3,6 +3,7 @@ using System; using FluentAssertions; using FluentAssertions.Specialized; +using Light.GuardClauses.ExceptionFactory; using Xunit.Abstractions; using Xunit.Sdk; diff --git a/Code/Light.GuardClauses/SpanDelegates.cs b/Code/Light.GuardClauses/ExceptionFactory/SpanDelegates.cs similarity index 96% rename from Code/Light.GuardClauses/SpanDelegates.cs rename to Code/Light.GuardClauses/ExceptionFactory/SpanDelegates.cs index 0eecc93..2abcf03 100644 --- a/Code/Light.GuardClauses/SpanDelegates.cs +++ b/Code/Light.GuardClauses/ExceptionFactory/SpanDelegates.cs @@ -1,6 +1,6 @@ using System; -namespace Light.GuardClauses; +namespace Light.GuardClauses.ExceptionFactory; /// /// Represents a delegate that receives a span and a value as parameters and that produces an exception. From 3fa99bd867cb614b6137d7e5ebbadb42b2f57c51 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 19:33:27 +0100 Subject: [PATCH 119/123] chore: update version to 13.0.0 Signed-off-by: Kenny Pflug --- Code/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Version.props b/Code/Version.props index 1914f2f..83928e4 100644 --- a/Code/Version.props +++ b/Code/Version.props @@ -1,5 +1,5 @@  - 12.0.0 + 13.0.0 \ No newline at end of file From 4efdc7bcc8a1c1b6191863c90e7c9481c67e9767 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 19:33:47 +0100 Subject: [PATCH 120/123] chore: update package release notes and copyright for 13.0.0 release Signed-off-by: Kenny Pflug --- .../Light.GuardClauses.csproj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Code/Light.GuardClauses/Light.GuardClauses.csproj b/Code/Light.GuardClauses/Light.GuardClauses.csproj index 2d7d6dc..0f55b64 100644 --- a/Code/Light.GuardClauses/Light.GuardClauses.csproj +++ b/Code/Light.GuardClauses/Light.GuardClauses.csproj @@ -9,7 +9,7 @@ Kenny Pflug enable true - Copyright © Kenny Pflug 2016, 2024 + Copyright © Kenny Pflug 2016, 2025 12 true true @@ -26,18 +26,21 @@ true README.md -Light.GuardClauses 12.0.0 +Light.GuardClauses 13.0.0 -------------------------------- -- Added support for NotNullAttribute -- Added Range.InclusiveBetween and Range.ExclusiveBetween to create Range instances even more easily -- breaking: String assertions that use a StringComparison parameter now throw an ArgumentException instead of forwarding an invalid value to the exception factory +- new assertions: IsApproximately<T>, IsLessThanOrApproximately<T>, IsGreaterThanOrApproximately<T>, MustBeApproximately, MustNotBeApproximately, MustBeLessThanOrApproximately, MustBeGreaterThanOrApproximately, IsEmptyOrWhiteSpace, IsFileExtension +- email regex is now precompiled on .NET 8 and newer, the regex is compiled at runtime on .NET Standard 2.0 and 2.1 +- breaking: Throw class is now located in new namespace Light.GuardClauses.ExceptionFactory +- breaking: Throw members regarding spans now only support ReadOnlySpan<T>, in keywords were removed +- breaking: IsApproximately now uses less-than-or-equal-to operator (<=) instead of less-than operator (<) +- breaking: Email regex is less strict and support additional patterns like domains with more than 3 letters (e.g. .info or .travel) - - + + From dbaac0a4937fd18bbefe9365af63f99906c7f025 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 19:33:12 +0100 Subject: [PATCH 121/123] docs: update readme for 13.0.0 release Signed-off-by: Kenny Pflug --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index dc3ed06..64c403f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ **A lightweight .NET library for expressive Guard Clauses.** [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/LICENSE) -[![NuGet](https://img.shields.io/badge/NuGet-12.0.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) -[![Source Code](https://img.shields.io/badge/Source%20Code-12.0.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) +[![NuGet](https://img.shields.io/badge/NuGet-13.0.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) +[![Source Code](https://img.shields.io/badge/Source%20Code-13.0.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) [![Documentation](https://img.shields.io/badge/Docs-Wiki-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/wiki) [![Documentation](https://img.shields.io/badge/Docs-Changelog-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/releases) @@ -85,6 +85,8 @@ In addition to assertions that throw exceptions (all these start with `Must`), * - `string.IsNullOrWhitespace()` - `collection.IsNullOrEmpty()` - `enum.IsValidEnumValue()` +- `string.IsFileExtension()` +- `span.IsEmptyOrWhiteSpace()` You can use these in your branching logic to easily check if an assertion is true or false. @@ -94,9 +96,9 @@ Every assertion is well-documented - explore them using IntelliSense or check ou Since version 4, **Light.GuardClauses** is optimized for performance (measured in .NET 4.8 and .NET 6). With the incredible help of [@redknightlois](https://github.com/redknightlois) and the awesome tool [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet), most assertions are as fast as your imperative code would be. -**Light.GuardClauses** has support for [.NET analyzers / FxCopAnalyzers](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) with the `ValidatedNotNullAttribute`. Analyzers will know when an assertion validated that a parameters is not null and consequently, CA1062 will not be raised. +**Light.GuardClauses** has support for [.NET analyzers / FxCopAnalyzers](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) with the `ValidatedNotNullAttribute` and the `NotNullAttribute`. Analyzers will know when an assertion validated that a parameters is not null and consequently, CA1062 will not be raised. -Furthermore, **Light.GuardClauses** has support for ReSharper since version 4.x. Via [Contract Annotations](https://www.jetbrains.com/help/resharper/Contract_Annotations.html), R# knows when assertions do not return a null value and thus removes squiggly lines indicating a possible `NullReferenceException`. Since version 10.1.0, Light.GuardClauses also annotates assertions that do not iterate over an `IEnumerable` with ReSharper's `NoEnumerationAttribute` - ReSharper will then not indicate a "Possible multiple enumeration" (thanks to [cdonnellytx](https://github.com/cdonnellytx) for this contribution). +Furthermore, **Light.GuardClauses** has support for ReSharper since version 4.x. Via [Contract Annotations](https://www.jetbrains.com/help/resharper/Contract_Annotations.html), ReSharper knows when assertions do not return a null value and thus removes squiggly lines indicating a possible `NullReferenceException`. Since version 10.1.0, Light.GuardClauses also annotates assertions that do not iterate over an `IEnumerable` with ReSharper's `NoEnumerationAttribute` - ReSharper will then not indicate a "Possible multiple enumeration" (thanks to [cdonnellytx](https://github.com/cdonnellytx) for this contribution). Since version 11, Light.GuardClauses supports Native AOT. @@ -106,7 +108,7 @@ And, of course, the functional correctness of **Light.GuardClauses** is covered ## Supported Platforms -**Light.GuardClauses** is built against [.NET Standard 2.0 and 2.1](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), thus it can be used in frameworks like .NET 6, .NET 7, .NET Framework 4.6.1 or newer, Unity, Mono, or UWP. +**Light.GuardClauses** is built against [.NET 8, .NET Standard 2.0 and 2.1](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), thus it can be used with a large variaty of target frameworks like .NET 5 or newer .NET Framework 4.6.1 or newer, Unity, Mono, or UWP. ## How to Install @@ -114,7 +116,7 @@ Light.GuardClauses is available as a [NuGet package](https://www.nuget.org/packa - **dotnet CLI**: `dotnet add package Light.GuardClauses` - **Visual Studio Package Manager Console**: `Install-Package Light.GuardClauses` -- **Package Reference in csproj**: `` +- **Package Reference in csproj**: `` Also, you can incorporate Light.GuardClauses as a **single source file** where the API is changed to `internal`. This is especially interesting for framework / library developers that do not want to have a dependency on the Light.GuardClauses DLL. You can grab the default .NET Standard 2.0 version in [Light.GuardClauses.SingleFile.cs](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) or you can use the [Light.GuardClauses.SourceCodeTransformation](https://github.com/feO2x/Light.GuardClauses/tree/master/Code/Light.GuardClauses.SourceCodeTransformation) project to create your custom file. You can learn more about it [here](https://github.com/feO2x/Light.GuardClauses/wiki/Including-Light.GuardClauses-as-source-code). From fafc7becb23d47251c4a1ecaa80aa9a406429ffd Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 20:12:52 +0100 Subject: [PATCH 122/123] feat: update SourceFileMerger for 13.0.0 release Signed-off-by: Kenny Pflug --- .../Light.GuardClauses.Source.csproj | 6 +- ...ardClauses.SourceCodeTransformation.csproj | 2 +- .../SourceFileMerger.cs | 268 ++++++++++++++---- Code/Light.GuardClauses/Check.cs | 3 +- .../ExceptionFactory/Throw.cs | 3 +- 5 files changed, 213 insertions(+), 69 deletions(-) diff --git a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj index 229eeab..0f9b090 100644 --- a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj +++ b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj @@ -5,13 +5,13 @@ 12.0 Kenny Pflug Kenny Pflug - Copyright © Kenny Pflug 2016 - 2023 + Copyright © Kenny Pflug 2016, 2025 true - - + + \ No newline at end of file diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj index f9959ae..371827b 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj +++ b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj @@ -13,7 +13,7 @@ - + diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs index 6ceb25d..a5af7e0 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs +++ b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs @@ -24,17 +24,20 @@ public static void CreateSingleSourceFile(SourceFileMergeOptions options) { Console.WriteLine("Appending version header..."); stringBuilder.AppendLine("/* ------------------------------") - .AppendLine($" Light.GuardClauses {typeof(SourceFileMerger).Assembly.GetName().Version!.ToString(3)}") + .AppendLine( + $" Light.GuardClauses {typeof(SourceFileMerger).Assembly.GetName().Version!.ToString(3)}" + ) .AppendLine(" ------------------------------") .AppendLine(); } Console.WriteLine("Creating default file layout..."); stringBuilder.AppendLineIf(!options.IncludeVersionComment, "/*") - .AppendLine($@"License information for Light.GuardClauses + .AppendLine( + $@"License information for Light.GuardClauses The MIT License (MIT) -Copyright (c) 2016, 2024 Kenny Pflug mailto:kenny.pflug@live.de +Copyright (c) 2016, 2025 Kenny Pflug mailto:kenny.pflug@live.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal @@ -68,6 +71,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Text; using System.Text.RegularExpressions; {(options.IncludeJetBrainsAnnotationsUsing ? "using JetBrains.Annotations;" + Environment.NewLine : string.Empty)}using {options.BaseNamespace}.Exceptions; +using {options.BaseNamespace}.ExceptionFactory; using {options.BaseNamespace}.FrameworkExtensions; {(options.IncludeJetBrainsAnnotationsUsing ? "using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute;" : "")} @@ -82,12 +86,18 @@ namespace {options.BaseNamespace}.Exceptions {{ }} +namespace {options.BaseNamespace}.ExceptionFactory +{{ +}} + namespace {options.BaseNamespace}.FrameworkExtensions {{ -}}"); +}}" + ); if (options.IncludeJetBrainsAnnotations) { - stringBuilder.AppendLine().AppendLine(@"/* + stringBuilder.AppendLine().AppendLine( + @"/* License information for JetBrains.Annotations MIT License @@ -113,12 +123,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace JetBrains.Annotations { -}"); +}" + ); } if (options.IncludeCodeAnalysisNullableAttributes) { - stringBuilder.AppendLine().AppendLine(@" + stringBuilder.AppendLine().AppendLine( + @" namespace System.Diagnostics.CodeAnalysis { /// @@ -293,12 +305,14 @@ public NotNullWhenAttribute(bool returnValue) ReturnValue = returnValue; } } -}"); +}" + ); } if (options.IncludeCallerArgumentExpressionAttribute) { - stringBuilder.AppendLine().AppendLine(@" + stringBuilder.AppendLine().AppendLine( + @" namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] @@ -311,10 +325,11 @@ public CallerArgumentExpressionAttribute(string parameterName) public string ParameterName { get; } } -}"); +}" + ); } - var csharpParseOptions = new CSharpParseOptions(LanguageVersion.CSharp10); + var csharpParseOptions = new CSharpParseOptions(LanguageVersion.CSharp12); var targetSyntaxTree = CSharpSyntaxTree.ParseText(stringBuilder.ToString(), csharpParseOptions); var targetRoot = (CompilationUnitSyntax) targetSyntaxTree.GetRoot(); @@ -323,63 +338,131 @@ public CallerArgumentExpressionAttribute(string parameterName) .OfType() .ToList(); var defaultNamespace = namespaces.First(@namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}"); - var exceptionsNamespace = namespaces.First(@namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}.Exceptions"); - var extensionsNamespace = namespaces.First(@namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}.FrameworkExtensions"); - var jetBrainsNamespace = namespaces.FirstOrDefault(@namespace => @namespace.Name.ToString() == "JetBrains.Annotations"); + var exceptionsNamespace = + namespaces.First(@namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}.Exceptions"); + var extensionsNamespace = namespaces.First( + @namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}.FrameworkExtensions" + ); + var exceptionFactoryNamespace = namespaces.First( + @namespace => @namespace.Name.ToString() == $"{options.BaseNamespace}.ExceptionFactory" + ); + var jetBrainsNamespace = + namespaces.FirstOrDefault(@namespace => @namespace.Name.ToString() == "JetBrains.Annotations"); var replacedNodes = new Dictionary { [defaultNamespace] = defaultNamespace, [exceptionsNamespace] = exceptionsNamespace, - [extensionsNamespace] = extensionsNamespace + [extensionsNamespace] = extensionsNamespace, + [exceptionFactoryNamespace] = exceptionFactoryNamespace, }; if (options.IncludeJetBrainsAnnotations && jetBrainsNamespace != null) + { replacedNodes.Add(jetBrainsNamespace, jetBrainsNamespace); + } var allSourceFiles = new DirectoryInfo(options.SourceFolder).GetFiles("*.cs", SearchOption.AllDirectories) - .Where(f => !f.FullName.Contains("obj") && - !f.FullName.Contains("bin")) + .Where( + f => !f.FullName.Contains("obj") && + !f.FullName.Contains("bin") + ) .ToDictionary(f => f.Name); - // Start with Check.CommonAssertions before all other files to prepare the Check class - Console.WriteLine("Merging CommonAssertions of the Check class..."); - var currentFile = allSourceFiles["Check.CommonAssertions.cs"]; - + // Start with Check.cs before all other files to prepare the Check class + Console.WriteLine("Preparing Check class..."); + var currentFile = allSourceFiles["Check.cs"]; var sourceSyntaxTree = CSharpSyntaxTree.ParseText(currentFile.ReadContent(), csharpParseOptions); var checkClassDeclaration = (ClassDeclarationSyntax) sourceSyntaxTree.GetRoot() .DescendantNodes() - .First(node => node.IsKind(SyntaxKind.ClassDeclaration)); - checkClassDeclaration = checkClassDeclaration.WithModifiers(checkClassDeclaration.Modifiers.Remove(checkClassDeclaration.Modifiers.First(token => token.IsKind(SyntaxKind.PartialKeyword)))); + .First( + node => node.IsKind( + SyntaxKind.ClassDeclaration + ) + ); + checkClassDeclaration = checkClassDeclaration.WithModifiers( + checkClassDeclaration.Modifiers.Remove( + checkClassDeclaration.Modifiers.First(token => token.IsKind(SyntaxKind.PartialKeyword)) + ) + ); + + // Do the same thing for the Throw class + Console.WriteLine("Preparing Throw class..."); + currentFile = allSourceFiles["Throw.cs"]; + sourceSyntaxTree = CSharpSyntaxTree.ParseText(currentFile.ReadContent(), csharpParseOptions); + var throwClassDeclaration = (ClassDeclarationSyntax) sourceSyntaxTree.GetRoot() + .DescendantNodes() + .First( + node => node.IsKind( + SyntaxKind.ClassDeclaration + ) + ); + throwClassDeclaration = throwClassDeclaration.WithModifiers( + throwClassDeclaration.Modifiers.Remove( + throwClassDeclaration.Modifiers.First(token => token.IsKind(SyntaxKind.PartialKeyword)) + ) + ); // Process all other files Console.WriteLine("Merging remaining files..."); foreach (var fileName in allSourceFiles.Keys) { + if (fileName == "SpanDelegates.cs") + { + + } + if (!CheckIfFileShouldBeProcessed(options, fileName)) + { continue; + } currentFile = allSourceFiles[fileName]; sourceSyntaxTree = CSharpSyntaxTree.ParseText(currentFile.ReadContent(), csharpParseOptions); - var originalNamespace = DetermineOriginalNamespace(options, - defaultNamespace, - currentFile, - extensionsNamespace, - exceptionsNamespace, - jetBrainsNamespace); + var originalNamespace = DetermineOriginalNamespace( + options, + defaultNamespace, + currentFile, + extensionsNamespace, + exceptionsNamespace, + exceptionFactoryNamespace, + jetBrainsNamespace + ); // If the file contains assertions, add it to the existing Check class declaration if (originalNamespace == defaultNamespace && currentFile.Name.StartsWith("Check.")) { var classDeclaration = (ClassDeclarationSyntax) sourceSyntaxTree.GetRoot() .DescendantNodes() - .First(node => node.IsKind(SyntaxKind.ClassDeclaration)); - checkClassDeclaration = checkClassDeclaration.WithMembers(checkClassDeclaration.Members.AddRange(classDeclaration.Members)); + .First( + node => node.IsKind( + SyntaxKind.ClassDeclaration + ) + ); + checkClassDeclaration = + checkClassDeclaration.WithMembers(checkClassDeclaration.Members.AddRange(classDeclaration.Members)); + continue; + } + + // Do something similar for the Throw class + if (originalNamespace == exceptionFactoryNamespace && currentFile.Name.StartsWith("Throw.")) + { + var classDeclaration = (ClassDeclarationSyntax) sourceSyntaxTree.GetRoot() + .DescendantNodes() + .First( + node => node.IsKind( + SyntaxKind.ClassDeclaration + ) + ); + throwClassDeclaration = + throwClassDeclaration.WithMembers(throwClassDeclaration.Members.AddRange(classDeclaration.Members)); continue; } // Else just get the members of the first namespace and add them to the corresponding one var sourceCompilationUnit = (CompilationUnitSyntax) sourceSyntaxTree.GetRoot(); if (sourceCompilationUnit.Members.IsNullOrEmpty()) + { continue; + } var membersToAdd = ((FileScopedNamespaceDeclarationSyntax) sourceCompilationUnit.Members[0]).Members; @@ -387,15 +470,25 @@ public CallerArgumentExpressionAttribute(string parameterName) replacedNodes[originalNamespace] = currentlyEditedNamespace .WithMembers( - currentlyEditedNamespace.Members.AddRange(membersToAdd)); + currentlyEditedNamespace.Members.AddRange(membersToAdd) + ); } // After the Check class declaration is finished, insert it into the default namespace var currentDefaultNamespace = replacedNodes[defaultNamespace]; - replacedNodes[defaultNamespace] = currentDefaultNamespace.WithMembers(currentDefaultNamespace.Members.Insert(0, checkClassDeclaration)); + replacedNodes[defaultNamespace] = + currentDefaultNamespace.WithMembers(currentDefaultNamespace.Members.Insert(0, checkClassDeclaration)); + + // Do the same thing for the Throw class + var currentExceptionFactoryNamespace = replacedNodes[exceptionFactoryNamespace]; + replacedNodes[exceptionFactoryNamespace] = + currentExceptionFactoryNamespace.WithMembers( + currentExceptionFactoryNamespace.Members.Insert(0, throwClassDeclaration) + ); // Update the target compilation unit - targetRoot = targetRoot.ReplaceNodes(replacedNodes.Keys, (originalNode, _) => replacedNodes[originalNode]).NormalizeWhitespace(); + targetRoot = targetRoot.ReplaceNodes(replacedNodes.Keys, (originalNode, _) => replacedNodes[originalNode]) + .NormalizeWhitespace(); // Make types internal if necessary if (options.ChangePublicTypesToInternalTypes) @@ -403,38 +496,57 @@ public CallerArgumentExpressionAttribute(string parameterName) Console.WriteLine("Types are changed from public to internal..."); var changedTypeDeclarations = new Dictionary(); - foreach (var typeDeclaration in targetRoot.DescendantNodes().Where(node => node.IsKind(SyntaxKind.ClassDeclaration) || - node.IsKind(SyntaxKind.StructDeclaration) || - node.IsKind(SyntaxKind.EnumDeclaration) || - node.IsKind(SyntaxKind.DelegateDeclaration))) + foreach (var typeDeclaration in targetRoot.DescendantNodes().Where( + node => node.IsKind(SyntaxKind.ClassDeclaration) || + node.IsKind(SyntaxKind.StructDeclaration) || + node.IsKind(SyntaxKind.EnumDeclaration) || + node.IsKind(SyntaxKind.DelegateDeclaration) + )) { if (typeDeclaration is BaseTypeDeclarationSyntax typeDeclarationSyntax) { var publicModifier = typeDeclarationSyntax.Modifiers[0]; var adjustedModifiers = typeDeclarationSyntax.Modifiers .RemoveAt(0) - .Insert(0, Token(SyntaxKind.InternalKeyword).WithTriviaFrom(publicModifier)); + .Insert( + 0, + Token(SyntaxKind.InternalKeyword) + .WithTriviaFrom(publicModifier) + ); if (typeDeclarationSyntax is ClassDeclarationSyntax classDeclaration) + { changedTypeDeclarations[classDeclaration] = classDeclaration.WithModifiers(adjustedModifiers); + } else if (typeDeclarationSyntax is StructDeclarationSyntax structDeclaration) + { changedTypeDeclarations[structDeclaration] = structDeclaration.WithModifiers(adjustedModifiers); + } else if (typeDeclarationSyntax is EnumDeclarationSyntax enumDeclaration) + { changedTypeDeclarations[enumDeclaration] = enumDeclaration.WithModifiers(adjustedModifiers); + } } else if (typeDeclaration is DelegateDeclarationSyntax delegateDeclaration) { var publicModifier = delegateDeclaration.Modifiers[0]; var adjustedModifiers = delegateDeclaration.Modifiers .RemoveAt(0) - .Insert(0, Token(SyntaxKind.InternalKeyword).WithTriviaFrom(publicModifier)); + .Insert( + 0, + Token(SyntaxKind.InternalKeyword) + .WithTriviaFrom(publicModifier) + ); changedTypeDeclarations[delegateDeclaration] = delegateDeclaration.WithModifiers(adjustedModifiers); } } - targetRoot = targetRoot.ReplaceNodes(changedTypeDeclarations.Keys, (originalNode, _) => changedTypeDeclarations[originalNode]); + targetRoot = targetRoot.ReplaceNodes( + changedTypeDeclarations.Keys, + (originalNode, _) => changedTypeDeclarations[originalNode] + ); } // Remove assertion overloads that incorporate an exception factory if necessary @@ -443,23 +555,49 @@ public CallerArgumentExpressionAttribute(string parameterName) Console.WriteLine("Removing overloads with exception factory..."); var checkClass = (ClassDeclarationSyntax) targetRoot.DescendantNodes() - .First(node => node.IsKind(SyntaxKind.ClassDeclaration) && - node is ClassDeclarationSyntax { Identifier.Text: "Check" }); + .First( + node => node.IsKind(SyntaxKind.ClassDeclaration) && + node is ClassDeclarationSyntax + { + Identifier.Text: "Check", + } + ); var membersWithoutExceptionFactory = checkClass.Members - .Where(member => member is not MethodDeclarationSyntax method || method.ParameterList.Parameters.All(parameter => parameter.Identifier.Text != "exceptionFactory")); - targetRoot = targetRoot.ReplaceNode(checkClass, checkClass.WithMembers(new SyntaxList(membersWithoutExceptionFactory))); + .Where( + member => member is not MethodDeclarationSyntax method || + method.ParameterList.Parameters.All( + parameter => parameter.Identifier.Text != "exceptionFactory" + ) + ); + targetRoot = targetRoot.ReplaceNode( + checkClass, + checkClass.WithMembers(new (membersWithoutExceptionFactory)) + ); // Remove members from Throw class that use exception factories var throwClass = (ClassDeclarationSyntax) targetRoot.DescendantNodes() - .First(node => node.IsKind(SyntaxKind.ClassDeclaration) && - node is ClassDeclarationSyntax { Identifier.Text: "Throw" }); + .First( + node => node.IsKind(SyntaxKind.ClassDeclaration) && + node is ClassDeclarationSyntax + { + Identifier.Text: "Throw", + } + ); membersWithoutExceptionFactory = throwClass.Members - .Where(member => member is not MethodDeclarationSyntax method || method.ParameterList.Parameters.All(parameter => parameter.Identifier.Text != "exceptionFactory")); - targetRoot = targetRoot.ReplaceNode(throwClass, throwClass.WithMembers(new SyntaxList(membersWithoutExceptionFactory))); + .Where( + member => member is not MethodDeclarationSyntax method || + method.ParameterList.Parameters.All( + parameter => parameter.Identifier.Text != "exceptionFactory" + ) + ); + targetRoot = targetRoot.ReplaceNode( + throwClass, + throwClass.WithMembers(new (membersWithoutExceptionFactory)) + ); } var targetFileContent = targetRoot.ToFullString(); @@ -473,35 +611,39 @@ public CallerArgumentExpressionAttribute(string parameterName) } private static bool CheckIfFileShouldBeProcessed(SourceFileMergeOptions options, string fileName) => - fileName != "Check.CommonAssertions.cs" && + fileName != "Check.cs" && + fileName != "Throw.cs" && fileName != "CallerArgumentExpressionAttribute.cs" && (fileName != "ReSharperAnnotations.cs" || options.IncludeJetBrainsAnnotations) && (fileName != "ValidatedNotNullAttribute.cs" || options.IncludeValidatedNotNullAttribute); - private static NamespaceDeclarationSyntax DetermineOriginalNamespace(SourceFileMergeOptions options, - NamespaceDeclarationSyntax defaultNamespace, - FileInfo currentFile, - NamespaceDeclarationSyntax extensionsNamespace, - NamespaceDeclarationSyntax exceptionsNamespace, - NamespaceDeclarationSyntax? jetBrainsNamespace) + private static NamespaceDeclarationSyntax DetermineOriginalNamespace( + SourceFileMergeOptions options, + NamespaceDeclarationSyntax defaultNamespace, + FileInfo currentFile, + NamespaceDeclarationSyntax extensionsNamespace, + NamespaceDeclarationSyntax exceptionsNamespace, + NamespaceDeclarationSyntax exceptionFactoryNamespace, + NamespaceDeclarationSyntax? jetBrainsNamespace + ) { var originalNamespace = defaultNamespace; switch (currentFile.Directory?.Name) { - case "FrameworkExtensions": - originalNamespace = extensionsNamespace; - break; - case "Exceptions": - originalNamespace = exceptionsNamespace; - break; + case "FrameworkExtensions": originalNamespace = extensionsNamespace; break; + case "Exceptions": originalNamespace = exceptionsNamespace; break; + case "ExceptionFactory": originalNamespace = exceptionFactoryNamespace; break; default: { if (options.IncludeJetBrainsAnnotations && currentFile.Name == "ReSharperAnnotations.cs") + { originalNamespace = jetBrainsNamespace!; + } + break; } } return originalNamespace; } -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses/Check.cs b/Code/Light.GuardClauses/Check.cs index ac14024..1edb58e 100644 --- a/Code/Light.GuardClauses/Check.cs +++ b/Code/Light.GuardClauses/Check.cs @@ -3,4 +3,5 @@ /// /// The class provides access to all assertions of Light.GuardClauses. /// -public static partial class Check; +// ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation +public static partial class Check { } diff --git a/Code/Light.GuardClauses/ExceptionFactory/Throw.cs b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs index 98a4046..0ff2aab 100644 --- a/Code/Light.GuardClauses/ExceptionFactory/Throw.cs +++ b/Code/Light.GuardClauses/ExceptionFactory/Throw.cs @@ -3,4 +3,5 @@ /// /// Provides static factory methods that throw default exceptions. /// -public static partial class Throw; +// ReSharper disable once RedundantTypeDeclarationBody - requried for the Source Code Transformation +public static partial class Throw { } From 66a804effb6546fefbb38e820a885a32e534bb3a Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Sun, 9 Mar 2025 20:14:22 +0100 Subject: [PATCH 123/123] chore: update single source file for 13.0.0 release Signed-off-by: Kenny Pflug --- Light.GuardClauses.SingleFile.cs | 7054 +++++++++++++++++++----------- 1 file changed, 4396 insertions(+), 2658 deletions(-) diff --git a/Light.GuardClauses.SingleFile.cs b/Light.GuardClauses.SingleFile.cs index 55dea4f..d984d0b 100644 --- a/Light.GuardClauses.SingleFile.cs +++ b/Light.GuardClauses.SingleFile.cs @@ -1,11 +1,11 @@ /* ------------------------------ - Light.GuardClauses 12.0.0 + Light.GuardClauses 13.0.0 ------------------------------ License information for Light.GuardClauses The MIT License (MIT) -Copyright (c) 2016, 2024 Kenny Pflug mailto:kenny.pflug@live.de +Copyright (c) 2016, 2025 Kenny Pflug mailto:kenny.pflug@live.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Text.RegularExpressions; using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using Light.GuardClauses.ExceptionFactory; using Light.GuardClauses.FrameworkExtensions; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; @@ -48,1183 +49,1298 @@ namespace Light.GuardClauses /// /// The class provides access to all assertions of Light.GuardClauses. /// + // ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation internal static class Check { /// - /// Ensures that the specified object reference is not null, or otherwise throws an . + /// Ensures that the specified URI is an absolute one, or otherwise throws a . /// - /// The object reference to be checked. + /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not an absolute URI. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - return parameter; - } + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) + { + Throw.MustBeAbsoluteUri(parameter, parameterName, message); + } - /// - /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. - /// - /// The reference to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : class - { - if (parameter is null) - Throw.CustomException(exceptionFactory); return parameter; } /// - /// Ensures that the specified parameter is not the default value, or otherwise throws an - /// for reference types, or an for value types. + /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and null. - /// Thrown when is a value type and the default value. + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when is not an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (default(T)is null) + if (parameter is null || parameter.IsAbsoluteUri == false) { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - return parameter; + Throw.CustomException(exceptionFactory, parameter); } - if (EqualityComparer.Default.Equals(parameter, default !)) - Throw.ArgumentDefault(parameterName, message); - // If we end up here, we have a value type which cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; -#pragma warning restore CS8777 } /// - /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. + /// Checks if the given is one of the specified . /// - /// The value to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is the default value. + /// The item to be checked. + /// The collection that might contain the . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static bool IsOneOf(this TItem item, [NotNull][ValidatedNotNull] IEnumerable items) { - if (default(T)is null) + if (items is ICollection collection) { - if (parameter is null) - Throw.CustomException(exceptionFactory); - return parameter; + return collection.Contains(item); } - if (EqualityComparer.Default.Equals(parameter, default !)) - Throw.CustomException(exceptionFactory); - // If we end up here, we have a value type which cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + if (items is string @string && item is char character) + { + return @string.IndexOf(character) != -1; + } - return parameter; -#pragma warning restore CS8777 + return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); } /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. + /// Ensures that the collection does not contain the specified item, or otherwise throws an . /// - /// The value to be checked for null. + /// The collection to be checked. + /// The item that must not be part of the collection. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and is null. + /// Thrown when contains . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (default(T) != null) + if (parameter is ICollection collection) { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + if (collection.Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); + } return parameter; -#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + } + if (parameter.MustNotBeNull(parameterName, message).Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); } - if (parameter is null) - Throw.ArgumentNull(parameterName, message); return parameter; } /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. + /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. /// - /// The value to be checked for null. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is a reference type and is null. + /// The collection to be checked. + /// The item that must not be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (default(T) != null) + if (parameter is ICollection collection) { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + if (collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } return parameter; -#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + } + if (parameter is null || parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); } - if (parameter is null) - Throw.CustomException(exceptionFactory); return parameter; } /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws a . + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// - /// The value to be cast. + /// The string to be checked. + /// The string that must not be part of . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when cannot be cast to . - /// Thrown when is null. + /// Thrown when contains . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message)is T castValue) - return castValue; - Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); - return default; + if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + { + Throw.StringContains(parameter, value, parameterName, message); + } + + return parameter; } /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. /// - /// The value to be cast. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when cannot be cast to . + /// The string to be checked. + /// The string that must not be part of . + /// The delegate that creates your custom exception (optional). and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { - if (parameter is T castValue) - return castValue; - Throw.CustomException(exceptionFactory, parameter); - return default; + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.Contains(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; } /// - /// Checks if the specified value is a valid enum value of its type. This is true when the specified value - /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked - /// with the . - /// - /// The type of the enum. - /// The enum value to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidEnumValue(this T parameter) - where T : struct, Enum => EnumInfo.IsValidEnumValue(parameter); - /// - /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// - /// The type of the enum. - /// The value to be checked. + /// The string to be checked. + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid enum value. + /// Thrown when contains . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : struct, Enum + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!EnumInfo.IsValidEnumValue(parameter)) - Throw.EnumValueNotDefined(parameter, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) + { + Throw.StringContains(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. /// - /// The type of the enum. - /// The value to be checked. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is no valid enum value, or when is no enum type. + /// The string to be checked. + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) - where T : struct, Enum + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { - if (!EnumInfo.IsValidEnumValue(parameter)) - Throw.CustomException(exceptionFactory, parameter); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } /// - /// Checks if the specified GUID is an empty one. - /// - /// The GUID to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; - /// - /// Ensures that the specified GUID is not empty, or otherwise throws an . + /// Checks if the specified string is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty string will return true. /// - /// The GUID to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty GUID. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid MustNotBeEmpty(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter == Guid.Empty) - Throw.EmptyGuid(parameterName, message); - return parameter; - } - - /// - /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. - /// - /// The GUID to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is an empty GUID. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) - { - if (parameter == Guid.Empty) - Throw.CustomException(exceptionFactory); - return parameter; - } - - /// - /// Checks if the specified is true and throws an in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the (optional). - /// Thrown when is true. + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the end, else false. + /// An empty string will result in true. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidOperation(bool condition, string? message = null) - { - if (condition) - Throw.InvalidOperation(message); - } - + public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); /// - /// Checks if the specified is true and throws an in this case. + /// Checks if the specified character span is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty span will return true. /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the . - /// Thrown when is true. + /// The character span to be checked. + /// + /// True if the is trimmed at the end, else false. + /// An empty span will result in true. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidState(bool condition, string? message = null) - { - if (condition) - Throw.InvalidState(message); - } - + public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); /// - /// Checks if the specified is true and throws an in this case. + /// Ensures that the collection has the specified number of items, or otherwise throws an . /// - /// The condition to be checked. The exception is thrown when it is true. + /// The collection to be checked. + /// The number of items the collection must have. /// The name of the parameter (optional). - /// The message that will be passed to the (optional). - /// Thrown when is true. + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not have the specified number of items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (condition) - Throw.Argument(parameterName, message); - } + if (parameter!.Count(parameterName, message) != count) + { + Throw.InvalidCollectionCount(parameter, count, parameterName, message); + } - /// - /// Checks if the specified is true and throws your custom exception in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, Func exceptionFactory) - { - if (condition) - Throw.CustomException(exceptionFactory); + return parameter; } /// - /// Checks if the specified is true and throws your custom exception in this case. + /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. /// - /// The condition to be checked. The exception is thrown when it is true. - /// The value that is checked in the . This value is passed to the . - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is true. + /// The collection to be checked. + /// The number of items the collection must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (condition) - Throw.CustomException(exceptionFactory, parameter); + if (parameter is null || parameter.Count() != count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + + return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . + /// Ensures that the collection is not null or empty, or otherwise throws an . /// - /// The nullable to be checked. + /// The collection to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no value. + /// Thrown when has no items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : struct + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (!parameter.HasValue) - Throw.NullableHasNoValue(parameterName, message); - return parameter.Value; + if (parameter.Count(parameterName, message) == 0) + { + Throw.EmptyCollection(parameterName, message); + } + + return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. + /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. /// - /// The nullable to be checked. + /// The collection to be checked. /// The delegate that creates your custom exception. - /// Thrown when has no value. + /// Thrown when has no items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : struct + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (!parameter.HasValue) - Throw.CustomException(exceptionFactory); - return parameter.Value; + if (parameter is null || parameter.Count() == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } /// - /// Checks if and point to the same object. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - // ReSharper disable StringLiteralTypo - [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] - // ReSharper restore StringLiteralTypo - public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) - where T : class => ReferenceEquals(parameter, other); - /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws a . + /// Ensures that the specified string is not null or empty, or otherwise throws an or . /// - /// The first reference to be checked. - /// The second reference to be checked. + /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when both and point to the same object. + /// Thrown when is an empty string. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (ReferenceEquals(parameter, other)) - Throw.SameObjectReference(parameter, parameterName, message); + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + if (parameter.Length == 0) + { + Throw.EmptyString(parameterName, message); + } + return parameter; } /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws your custom exception. + /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. /// - /// The first reference to be checked. - /// The second reference to be checked. + /// The string to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Thrown when both and point to the same object. + /// Your custom exception thrown when is an empty string or null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) - where T : class + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (ReferenceEquals(parameter, other)) + if (parameter.IsNullOrEmpty()) + { Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// Ensures that the URI has one of the specified schemes, or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. + /// Thrown when the scheme is not equal to one of the specified schemes. + /// Thrown when is relative and thus has no scheme. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] + public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!EqualityComparer.Default.Equals(parameter, other)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); + // ReSharper disable PossibleMultipleEnumeration + parameter.MustBeAbsoluteUri(parameterName, message); + if (schemes is ICollection collection) + { + if (!collection.Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + } + + return parameter; + } + + if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + } + return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. + /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are not equal. + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, or when is a relative URI, or when is null. + /// Thrown when is null. + /// The type of the collection containing the schemes. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (!EqualityComparer.Default.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other); - return parameter; - } - - /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . + if (parameter is null || !parameter.IsAbsoluteUri) + { + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + if (schemes is ICollection collection) + { + if (!collection.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + return parameter; + } + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (schemes is null || !schemes.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes!); + } + + return parameter; + } + + /// + /// Ensures that the specified is not less than the given value, or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. - /// Thrown when is null. + /// Thrown when the specified is less than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + { + Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are not equal, or when is null. + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + if (parameter is null || parameter.CompareTo(other) < 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + return parameter; } /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . + /// Ensures that the collection has at most the specified number of items, or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. + /// The collection to be checked. + /// The number of items the collection should have at most. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. + /// Thrown when does not contain at most the specified number of items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (EqualityComparer.Default.Equals(parameter, other)) - Throw.ValuesEqual(parameter, other, parameterName, message); + if (parameter.Count(parameterName, message) > count) + { + Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); + } + return parameter; } /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. + /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are equal. + /// The collection to be checked. + /// The number of items the collection should have at most. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (EqualityComparer.Default.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other); + if (parameter is null || parameter.Count() > count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + return parameter; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . + /// Checks if the specified character is a digit. /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDigit(this char character) => char.IsDigit(character); + /// + /// Checks if the specified is true and throws an in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the . + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidState(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidState(message); + } + } + + /// + /// Checks if the specified GUID is an empty one. + /// + /// The GUID to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; + /// + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws an . + /// + /// The email address that will be validated. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. - /// Thrown when is null. + /// Thrown when is no valid email address. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) - Throw.ValuesEqual(parameter, other, parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are equal, or when is null. + /// The email address that will be validated. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || equalityComparer.Equals(parameter, other)) - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + if (!parameter.IsEmailAddress()) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws an . /// - /// The first value to be compared. - /// The second value to be compared. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid email address. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other, double tolerance) => Math.Abs(value - other) < tolerance; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other) => Math.Abs(value - other) < 0.0001; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) + { + Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); + } + + return parameter; + } + /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// + /// The string to be checked. + /// The other string that must not contain . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other, float tolerance) => Math.Abs(value - other) < tolerance; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + { + Throw.Substring(parameter, value, parameterName, message); + } + + return parameter; + } + /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) < 0.0001f; - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => value > other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other) => value > other || value.IsApproximately(other); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => value > other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// + /// The string to be checked. + /// The other string that must not contain . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.Contains(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - /// The date time to be checked. + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when contains . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Utc) - Throw.MustBeUtcDateTime(parameter, parameterName, message); + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) != -1) + { + Throw.Substring(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { - if (parameter.Kind != DateTimeKind.Utc) - Throw.CustomException(exceptionFactory, parameter); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The date time to be checked. + /// The value to be checked. + /// The value that should be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.Kind != DateTimeKind.Local) - Throw.MustBeLocalDateTime(parameter, parameterName, message); - return parameter; - } - + public static double MustBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) + public static double MustBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter.Kind != DateTimeKind.Local) - Throw.CustomException(exceptionFactory, parameter); + if (!parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . /// - /// The date time to be checked. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + public static double MustBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (parameter.Kind != DateTimeKind.Unspecified) - Throw.CustomException(exceptionFactory, parameter); + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the collection has the specified number of items, or otherwise throws an . + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The collection to be checked. - /// The number of items the collection must have. + /// The value to be checked. + /// The value that should be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified number of items. - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is + /// not less than 0.0001f. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static float MustBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001f, parameterName, message); + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately(this float parameter, float other, Func exceptionFactory) { - if (parameter!.Count(parameterName, message) != count) - Throw.InvalidCollectionCount(parameter, count, parameterName, message); + if (!parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified number of items, or when is null. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static float MustBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || parameter.Count() != count) - Throw.CustomException(exceptionFactory, parameter, count); + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Checks if the specified collection is null or empty. + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// True if the collection is null or empty, else false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; - /// - /// Ensures that the collection is not null or empty, or otherwise throws an . - /// - /// The collection to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no items. - /// Thrown when is null. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static float MustBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (parameter.Count(parameterName, message) == 0) - Throw.EmptyCollection(parameterName, message); + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The collection to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) - where TCollection : class, IEnumerable + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not end with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || parameter.Count() == 0) - Throw.CustomException(exceptionFactory, parameter); + if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws a . + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when is null. + /// The string to be checked. + /// The other string must end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter is ICollection collection) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) { - if (!collection.Contains(item)) - Throw.MissingItem(parameter, item, parameterName, message); - return parameter; + Throw.CustomException(exceptionFactory, parameter, value!); } - if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.MissingItem(parameter, item, parameterName, message); return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain , or when is null. + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter is ICollection collection) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) { - if (!collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } - if (parameter is null || !parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws an . + /// Checks if the string contains the specified value using the given comparison type. /// - /// The collection to be checked. - /// The item that must not be part of the collection. + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if contains , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("string:null => halt; value:null => halt")] + public static bool Contains(// ReSharper disable once RedundantNullableFlowAttribute -- Caller might have NRTs turned off + [NotNull][ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; + /// + /// Ensures that and do not point to the same object instance, or otherwise + /// throws a . + /// + /// The first reference to be checked. + /// The second reference to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when is null. + /// Thrown when both and point to the same object. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class { - if (parameter is ICollection collection) + if (ReferenceEquals(parameter, other)) { - if (collection.Contains(item)) - Throw.ExistingItem(parameter, item, parameterName, message); - return parameter; + Throw.SameObjectReference(parameter, parameterName, message); } - if (parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.ExistingItem(parameter, item, parameterName, message); return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. + /// Ensures that and do not point to the same object instance, or otherwise + /// throws your custom exception. /// - /// The collection to be checked. - /// The item that must not be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . + /// The first reference to be checked. + /// The second reference to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Thrown when both and point to the same object. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) + where T : class { - if (parameter is ICollection collection) + if (ReferenceEquals(parameter, other)) { - if (collection.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); - return parameter; + Throw.CustomException(exceptionFactory, parameter); } - if (parameter is null || parameter.Contains(item)) - Throw.CustomException(exceptionFactory, parameter, item); return parameter; } /// - /// Checks if the given is one of the specified . + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The item to be checked. - /// The collection that might contain the . - /// Thrown when is null. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static bool IsOneOf(this TItem item, [NotNull, ValidatedNotNull] IEnumerable items) + public static double MustNotBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001, parameterName, message); + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (items is ICollection collection) - return collection.Contains(item); - if (items is string @string && item is char character) - return @string.IndexOf(character) != -1; - return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); + if (parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; } /// - /// Ensures that the value is one of the specified items, or otherwise throws a . + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . /// /// The value to be checked. - /// The items that should contain the value. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to one of the specified . - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueNotOneOf(parameter, items, parameterName, message); + if (parameter.IsApproximately(other, tolerance)) + { + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. /// /// The value to be checked. - /// The items that should contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to one of the specified , or when is null. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || !parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); + if (parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// /// The value to be checked. - /// The items that must not contain the value. + /// The value that should not be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to one of the specified . - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001f. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - // ReSharper disable PossibleMultipleEnumeration - if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) - Throw.ValueIsOneOf(parameter, items, parameterName, message); - return parameter; - // ReSharper restore PossibleMultipleEnumeration - } - + public static float MustNotBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// /// The value to be checked. - /// The items that must not contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is equal to one of the specified , or when is null. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + public static float MustNotBeApproximately(this float parameter, float other, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || parameter.IsOneOf(items)) - Throw.CustomException(exceptionFactory, parameter, items!); + if (parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws an . + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection should have at least. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at least the specified number of items. - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static float MustNotBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) < count) - Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); - return parameter; - } + if (parameter.IsApproximately(other, tolerance)) + { + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + } - /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection should have at least. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() < count) - Throw.CustomException(exceptionFactory, parameter, count); return parameter; } /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws an . + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at most the specified number of items. - /// Thrown when is null. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static float MustNotBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (parameter.Count(parameterName, message) > count) - Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); - return parameter; - } + if (parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } - /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. - /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable - { - if (parameter is null || parameter.Count() > count) - Throw.CustomException(exceptionFactory, parameter, count); return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws an . + /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . /// - /// The span to be checked. - /// The length that the span must have. + /// The nullable to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified length. + /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : struct { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); - return parameter; + if (!parameter.HasValue) + { + Throw.NullableHasNoValue(parameterName, message); + } + + return parameter.Value; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. + /// The nullable to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : struct { - if (parameter.Length != length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; + if (!parameter.HasValue) + { + Throw.CustomException(exceptionFactory); + } + + return parameter.Value; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the string is longer than the specified length, or otherwise throws a . /// - /// The span to be checked. - /// The length that the span must have. + /// The string to be checked. + /// The length that the string must be longer than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Your custom exception thrown when does not have the specified length. + /// Thrown when has a length shorter than or equal to . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThan([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length != length) - Throw.InvalidSpanLength(parameter, length, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).Length <= length) + { + Throw.StringNotLongerThan(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the string is longer than the specified length, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The length that the span must have. + /// The string to be checked. + /// The length that the string must be longer than. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. + /// Your custom exception thrown when is null or when it has a length shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (parameter.Length != length) - Throw.CustomSpanException(exceptionFactory, parameter, length); + if (parameter is null || parameter.Length <= length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + return parameter; } @@ -1239,8 +1355,7 @@ public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length <= length) - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + ((ReadOnlySpan)parameter).MustBeLongerThan(length, parameterName, message); return parameter; } @@ -1255,7 +1370,10 @@ public static Span MustBeLongerThan(this Span parameter, int length, [C public static Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { if (parameter.Length <= length) + { Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } @@ -1271,7 +1389,10 @@ public static Span MustBeLongerThan(this Span parameter, int length, Sp public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.Length <= length) + { Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + } + return parameter; } @@ -1286,2639 +1407,3968 @@ public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { if (parameter.Length <= length) + { Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Ensures that the specified object reference is not null, or otherwise throws an . /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. + /// The object reference to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . + /// The reference to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : class { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Checks if the specified is true and throws an in this case. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. + /// The condition to be checked. The exception is thrown when it is true. /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . + /// The message that will be passed to the (optional). + /// Thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) { - if (parameter.Length < length) - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - return parameter; + if (condition) + { + Throw.Argument(parameterName, message); + } } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the specified is true and throws your custom exception in this case. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . + /// The condition to be checked. The exception is thrown when it is true. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, Func exceptionFactory) { - if (parameter.Length < length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; + if (condition) + { + Throw.CustomException(exceptionFactory); + } } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// Checks if the specified is true and throws your custom exception in this case. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . + /// The condition to be checked. The exception is thrown when it is true. + /// The value that is checked in the . This value is passed to the . + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); - return parameter; + if (condition) + { + Throw.CustomException(exceptionFactory, parameter); + } } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Checks if the specified character is a letter or digit. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); - return parameter; - } - + public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// Checks if the given is equal to the specified or if it implements it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); + /// + /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified + /// to compare the types. + /// + /// , + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// The equality comparer used to compare the interface types. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); + /// + /// Checks if the specified string is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty string will return true. When null is passed, + /// you can control the return value with which will + /// return true by default. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed, else false. An empty string will result in true. + /// You can control the return value with when the + /// is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); + /// + /// Checks if the specified character span is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// True if the is trimmed, else false. An empty span will result in true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace() && !parameter[parameter.Length - 1].IsWhiteSpace(); + /// + /// Ensures that the string is not null and trimmed, or otherwise throws a . + /// Empty strings are regarded as trimmed. + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not trimmed, i.e. they start or end with white space characters. + /// Empty strings are regarded as trimmed. + /// + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length >= length) - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) + { + Throw.NotTrimmed(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// - /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.Length >= length) - Throw.CustomSpanException(exceptionFactory, parameter, length); + if (parameter is null || !parameter.AsSpan().IsTrimmed()) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the string does not start with the specified value, or otherwise throws a . /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// Thrown when starts with . + /// Thrown when or is null. + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter.Length > length) - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter.Length > length) - Throw.CustomSpanException(exceptionFactory, parameter, length); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the span does not start with the specified value, or otherwise throws a . /// /// The span to be checked. - /// The length value that the span must be shorter than or equal to. + /// The other span that must not start with. + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// Thrown when starts with . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length > length) - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + if (parameter.StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. /// /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The other span that must not start with. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, ReadOnlySpansExceptionFactory exceptionFactory) + where T : IEquatable { - if (parameter.Length > length) - Throw.CustomSpanException(exceptionFactory, parameter, length); + if (parameter.StartsWith(value)) + { + Throw.CustomSpanException(exceptionFactory, parameter, value); + } + return parameter; } /// - /// Checks if the two specified types are equivalent. This is true when both types are equal or - /// when one type is a constructed generic type and the other type is the corresponding generic type definition. + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. /// - /// The first type to be checked. - /// The other type to be checked. - /// - /// True if both types are null, or if both are equal, or if one type - /// is a constructed generic type and the other one is the corresponding generic type definition, else false. - /// + /// The span to be checked. + /// The other span that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// + /// Thrown when is not a valid value. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || !(type is null) && !(other is null) && (type == other || type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)); - private static bool CheckTypeEquivalency(Type type, Type other) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, ReadOnlySpansExceptionFactory exceptionFactory) { - if (type.IsConstructedGenericType) - return type.GetGenericTypeDefinition() == other; - return other.GetGenericTypeDefinition() == type; + if (parameter.StartsWith(value, comparisonType)) + { + Throw.CustomSpanException(exceptionFactory, parameter, value, comparisonType); + } + + return parameter; } /// - /// Checks if the type implements the specified interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// - /// The type to be checked. - /// The interface type that should implement. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type interfaceType// ReSharper restore RedundantNullableFlowAttribute - ) + /// The string to be checked. + /// The other string that must contain . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) + if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) { - if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) - return true; + Throw.NotSubstring(parameter, value, parameterName, message); } - return false; + return parameter; } /// - /// Checks if the type implements the specified interface type. This overload uses the specified - /// to compare the interface types. + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. /// - /// The type to be checked. - /// The interface type that should implement. - /// The equality comparer used to compare the interface types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type interfaceType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer// ReSharper restore RedundantNullableFlowAttribute - ) - { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - typeComparer.MustNotBeNull(); - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) + /// The string to be checked. + /// The other string that must contain . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !value.Contains(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) == -1) + { + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "http" or "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) + { + Throw.UriMustHaveOneSchemeOf(parameter, ["https", "http"], parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http" or "https", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + { + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).Length > length) + { + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length greater than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + { + if (parameter is null || parameter.Length > length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is longer than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); + return parameter; + } + + /// + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + { + if (parameter.Length > length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is longer than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.Length > length) + { + Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + { + if (parameter.Length > length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . + /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains only white space. + /// Thrown when is an empty string. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + parameter.MustNotBeNullOrEmpty(parameterName, message); + foreach (var character in parameter) + { + if (!character.IsWhiteSpace()) + { + return parameter; + } + } + + Throw.WhiteSpaceString(parameter, parameterName, message); + return null; + } + + /// + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null, empty, or contains only white space. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] + public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + { + if (parameter.IsNullOrWhiteSpace()) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Checks if the two specified types are equivalent. This is true when both types are equal or + /// when one type is a constructed generic type and the other type is the corresponding generic type definition. + /// + /// The first type to be checked. + /// The other type to be checked. + /// + /// True if both types are null, or if both are equal, or if one type + /// is a constructed generic type and the other one is the corresponding generic type definition, else false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || (type is not null && other is not null && (type == other || (type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)))); + private static bool CheckTypeEquivalency(Type type, Type other) + { + if (type.IsConstructedGenericType) + { + return type.GetGenericTypeDefinition() == other; + } + + return other.GetGenericTypeDefinition() == type; + } + + /// + /// Checks if the value is within the specified range. + /// + /// The comparable to be checked. + /// The range where must be in-between. + /// True if the parameter is within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => range.IsValueWithinRange(parameter); + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + { + Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } + + /// + /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); + /// + /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified + /// to compare the types. + /// + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] + public static bool InheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); + /// + /// Ensures that the specified uses , or otherwise throws an . + /// + /// The date time to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.Kind != DateTimeKind.Unspecified) + { + Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified uses , or otherwise throws your custom exception. + /// + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + { + if (parameter.Kind != DateTimeKind.Unspecified) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Checks if the specified string is null, empty, or contains only white space. + /// + /// The string to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); + /// + /// Ensures that the specified uses , or otherwise throws an . + /// + /// The date time to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.Kind != DateTimeKind.Local) + { + Throw.MustBeLocalDateTime(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified uses , or otherwise throws your custom exception. + /// + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) + { + if (parameter.Kind != DateTimeKind.Local) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the value is one of the specified items, or otherwise throws a . + /// + /// The value to be checked. + /// The items that should contain the value. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to one of the specified . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf(this TItem parameter, // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + // ReSharper disable PossibleMultipleEnumeration + if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + { + Throw.ValueNotOneOf(parameter, items, parameterName, message); + } + + return parameter; + // ReSharper restore PossibleMultipleEnumeration + } + + /// + /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. + /// + /// The value to be checked. + /// The items that should contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to one of the specified , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || !parameter.IsOneOf(items)) + { + Throw.CustomException(exceptionFactory, parameter, items!); + } + + return parameter; + } + + /// + /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are not equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when and are not equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBe(this T parameter, T other, Func exceptionFactory) + { + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are not equal. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when and are not equal, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!string.Equals(parameter, other, comparisonType)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + { + if (!string.Equals(parameter, other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.Equals(other, comparisonType)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + { + if (!parameter.Equals(other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + + return parameter; + } + + /// + /// Checks if the specified is true and throws an in this case. + /// + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the (optional). + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidOperation(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidOperation(message); + } + } + + /// + /// Ensures that the string matches the specified regular expression, or otherwise throws a . + /// + /// The string to be checked. + /// The regular expression used for pattern matching. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not match the specified regular expression. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] + public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) + { + Throw.StringDoesNotMatch(parameter, regex, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The regular expression used for pattern matching. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not match the specified regular expression, + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || regex is null || !regex.IsMatch(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, regex!); + } + + return parameter; + } + + /// + /// Ensures that can be cast to and returns the cast value, or otherwise throws a . + /// + /// The value to be cast. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when cannot be cast to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message)is T castValue) + return castValue; + Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); + return default; + } + + /// + /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. + /// + /// The value to be cast. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when cannot be cast to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + { + if (parameter is T castValue) + return castValue; + Throw.CustomException(exceptionFactory, parameter); + return default; + } + + /// + /// Checks if the specified value is a valid enum value of its type. This is true when the specified value + /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked + /// with the . + /// + /// The type of the enum. + /// The enum value to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsValidEnumValue(this T parameter) + where T : struct, Enum => EnumInfo.IsValidEnumValue(parameter); + /// + /// Checks if and point to the same object. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable StringLiteralTypo + [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] + // ReSharper restore StringLiteralTypo + public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) + where T : class => ReferenceEquals(parameter, other); + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other, double tolerance) => Math.Abs(value - other) <= tolerance; + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other) => Math.Abs(value - other) <= 0.0001; + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other, float tolerance) => Math.Abs(value - other) <= tolerance; + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) <= 0.0001f; + /// + /// Ensures that is within the specified range, or otherwise throws an . + /// + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not within . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) { - if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) - return true; + Throw.MustBeInRange(parameter, range, parameterName, message); } - return false; + return parameter; } /// - /// Checks if the given is equal to the specified or if it implements it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Ensures that is within the specified range, or otherwise throws your custom exception. /// - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// Thrown when or is null. + /// The parameter to be checked. + /// The range where must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || !range.IsValueWithinRange(parameter)) + { + Throw.CustomException(exceptionFactory, parameter!, range); + } + + return parameter; + } + /// - /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified - /// to compare the types. + /// Checks if the specified string is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty string will return true. /// - /// , - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// The equality comparer used to compare the interface types. - /// Thrown when or is null. + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the start, else false. + /// An empty string will result in true. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); + public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); /// - /// Checks if the specified type derives from the other type. Internally, this method uses - /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Checks if the specified character span is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty span will return true. /// - /// The type info to be checked. - /// The base class that should derive from. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass) - // ReSharper restore RedundantNullableFlowAttribute + /// The character span to be checked. + /// + /// True if the is trimmed at the start, else false. + /// An empty span will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace(); + /// + /// Ensures that the has the specified scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than the specified one. + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - baseClass.MustNotBeNull(nameof(baseClass)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) + if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) { - if (currentBaseType.IsEquivalentTypeTo(baseClass)) - return true; - currentBaseType = currentBaseType.BaseType; + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); } - return false; + return parameter; + } + + /// + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + { + if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. and are passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + { + if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) + { + Throw.CustomException(exceptionFactory, parameter, scheme); + } + + return parameter; + } + + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); + /// + /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "http". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); + /// + /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); + /// + /// Ensures that the string starts with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not start with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; } /// - /// Checks if the specified type derives from the other type. This overload uses the specified - /// to compare the types. + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. /// - /// The type info to be checked. - /// The base class that should derive from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) - // ReSharper restore RedundantNullableFlowAttribute + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - baseClass.MustNotBeNull(nameof(baseClass)); - typeComparer.MustNotBeNull(nameof(typeComparer)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) { - if (typeComparer.Equals(currentBaseType, baseClass)) - return true; - currentBaseType = currentBaseType.BaseType; + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } - return false; + return parameter; } /// - /// Checks if the given is equal to the specified or if it derives from it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); + public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => value > other || value.IsApproximately(other, tolerance); /// - /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified - /// to compare the types. + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); + public static bool IsGreaterThanOrApproximately(this double value, double other) => value > other || value.IsApproximately(other); /// - /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// Thrown when or is null. + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType) => // ReSharper restore RedundantNullableFlowAttribute - baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); + public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => value > other || value.IsApproximately(other, tolerance); /// - /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified - /// to compare the types. + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute - baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); + public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions - /// are regarded as equal. + /// Ensures that is not within the specified range, or otherwise throws an . /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is within . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => // ReSharper restore RedundantNullableFlowAttribute - type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + { + Throw.MustNotBeInRange(parameter, range, parameterName, message); + } + + return parameter; + } + /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// This overload uses the specified to compare the types. + /// Ensures that is not within the specified range, or otherwise throws your custom exception. /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when or is null. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - [NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => // ReSharper restore RedundantNullableFlowAttribute - typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || range.IsValueWithinRange(parameter)) + { + Throw.CustomException(exceptionFactory, parameter!, range); + } + + return parameter; + } + /// - /// Checks if the given is a generic type that has open generic parameters, - /// but is no generic type definition. + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . /// - /// The type to be checked. - /// Thrown when is null. + /// The string to be checked. + /// The length that the string must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has a length shorter than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOpenConstructedGenericType([NotNull, ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).Length < length) + { + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + } + + return parameter; + } + /// - /// Checks if the specified string is null or empty. + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The string to be checked. + /// The string to be checked. + /// The length that the string must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + { + if (parameter is null || parameter.Length < length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// - /// Ensures that the specified string is not null or empty, or otherwise throws an or . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// - /// The string to be checked. + /// The span to be checked. + /// The value that the span must be longer than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty string. - /// Thrown when is null. + /// Thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null) - Throw.ArgumentNull(parameterName, message); - if (parameter.Length == 0) - Throw.EmptyString(parameterName, message); + ((ReadOnlySpan)parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); return parameter; } /// - /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an empty string or null. + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter.IsNullOrEmpty()) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length < length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + + /// + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + { + if (parameter.Length < length) + { + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + } + return parameter; } /// - /// Checks if the specified string is null, empty, or contains only white space. + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The string to be checked. + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + { + if (parameter.Length < length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + + return parameter; + } + /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . + /// Ensures that the string is a valid file extension, or otherwise throws an . /// /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains only white space. - /// Thrown when is an empty string. + /// Thrown when is not a valid file extension. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - parameter.MustNotBeNullOrEmpty(parameterName, message); - foreach (var character in parameter) + if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) { - if (!character.IsWhiteSpace()) - return parameter; + Throw.NotFileExtension(parameter, parameterName, message); } - Throw.WhiteSpaceString(parameter, parameterName, message); - return null; + return parameter; } /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. + /// Ensures that the string is a valid file extension, or otherwise throws your custom exception. /// /// The string to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null, empty, or contains only white space. + /// Your custom exception thrown when is null or not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] - public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.IsNullOrWhiteSpace()) + if (parameter is null || !parameter.IsFileExtension()) + { Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Checks if the specified character is a white space character. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); - /// - /// Checks if the specified character is a letter. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetter(this char character) => char.IsLetter(character); - /// - /// Checks if the specified character is a letter or digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); - /// - /// Checks if the specified character is a digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsDigit(this char character) => char.IsDigit(character); - /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the character span is a valid file extension, or otherwise throws a . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The character span to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. + /// The original character span. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeFileExtension(this Span parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!string.Equals(parameter, other, comparisonType)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); + ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the character span is a valid file extension, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + /// The character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character span. + /// Your custom exception thrown when is not a valid file extension. + public static Span MustBeFileExtension(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (!string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + ((ReadOnlySpan)parameter).MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the character memory is a valid file extension, or otherwise throws a . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The character memory to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. + /// The original character memory. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Memory MustBeFileExtension(this Memory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.Equals(other, comparisonType)) - Throw.ValuesNotEqual(parameter, other, parameterName, message); + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the character memory is a valid file extension, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + /// The character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character memory. + /// Your custom exception thrown when is not a valid file extension. + public static Memory MustBeFileExtension(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (!parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The read-only character memory to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// The original read-only character memory. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (string.Equals(parameter, other, comparisonType)) - Throw.ValuesEqual(parameter, other, parameterName, message); + parameter.Span.MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + /// The read-only character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character memory. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + parameter.Span.MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// Ensures that the read-only character span is a valid file extension, or otherwise throws a . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The read-only character span to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// The original read-only character span. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.Equals(other, comparisonType)) - Throw.ValuesEqual(parameter, other, parameterName, message); + if (!parameter.IsFileExtension()) + { + Throw.NotFileExtension(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that the read-only character span is a valid file extension, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + /// The read-only character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character span. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + if (!parameter.IsFileExtension()) + { + Throw.CustomSpanException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the string matches the specified regular expression, or otherwise throws a . + /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . /// - /// The string to be checked. - /// The regular expression used for pattern matching. + /// The first value to be compared. + /// The other value to be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not match the specified regular expression. - /// Thrown when or is null. + /// Thrown when and are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] - public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) - Throw.StringDoesNotMatch(parameter, regex, parameterName, message); - return parameter; - } + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } - /// - /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. - /// - /// The string to be checked. - /// The regular expression used for pattern matching. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not match the specified regular expression, - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || regex is null || !regex.IsMatch(parameter)) - Throw.CustomException(exceptionFactory, parameter, regex!); return parameter; } /// - /// Checks if the specified strings are equal, using the given comparison rules. + /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. /// - /// The first string to compare. - /// The second string to compare. - /// One of the enumeration values that specifies the rules for the comparison. - /// True if the two strings are considered equal, else false. - /// Thrown when is no valid enum value. + /// The first value to be compared. + /// The other value to be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when and are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) + public static T MustNotBe(this T parameter, T other, Func exceptionFactory) { - if ((int)comparisonType < 6) - return string.Equals(@string, value, (StringComparison)comparisonType); - if (comparisonType == StringComparisonType.OrdinalIgnoreWhiteSpace) - return @string.EqualsOrdinalIgnoreWhiteSpace(value); - if (comparisonType == StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace) - return @string.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(value); - Throw.EnumValueNotDefined(comparisonType, nameof(comparisonType)); - return false; + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; } /// - /// Ensures that the string contains the specified substring, or otherwise throws a . + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . /// - /// The string to be checked. - /// The substring that must be part of . + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. + /// Thrown when and are equal. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) { - Throw.StringDoesNotContain(parameter, value, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The substring that must be part of . - /// The delegate that creates you custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when and are equal, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !parameter.Contains(value)) + if (equalityComparer is null || equalityComparer.Equals(parameter, other)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws a . + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . /// - /// The string to be checked. - /// The substring that must be part of . - /// One of the enumeration values that specifies the rules for the search. + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// Thrown when is equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) + if (string.Equals(parameter, other, comparisonType)) { - Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. /// - /// The string to be checked. - /// The substring that must be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates you custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + if (string.Equals(parameter, other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . /// - /// The string to be checked. - /// The string that must not be part of . + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. + /// Thrown when is equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + if (parameter.Equals(other, comparisonType)) { - Throw.StringContains(parameter, value, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. /// - /// The string to be checked. - /// The string that must not be part of . - /// The delegate that creates your custom exception (optional). and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.Contains(value)) - Throw.CustomException(exceptionFactory, parameter, value!); + if (parameter.Equals(other, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + } + return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// Ensures that the collection has at least the specified number of items, or otherwise throws an . /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. + /// The collection to be checked. + /// The number of items the collection should have at least. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// Thrown when does not contain at least the specified number of items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) - Throw.StringContains(parameter, value, comparisonType, parameterName, message); + if (parameter.Count(parameterName, message) < count) + { + Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); + } + return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + if (parameter is null || parameter.Count() < count) + { + Throw.CustomException(exceptionFactory, parameter, count); + } + return parameter; } /// - /// Checks if the string contains the specified value using the given comparison type. - /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if contains , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("string:null => halt; value:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - public static bool Contains([NotNull, ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; - /// - /// Checks if the string is a substring of the other string. - /// - /// The string to be checked. - /// The other string. - /// True if is a substring of , else false. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); - // ReSharper restore RedundantNullableFlowAttribute - /// - /// Checks if the string is a substring of the other string. - /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if is a substring of , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; - // ReSharper disable RedundantNullableFlowAttribute - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// Ensures that the specified is not greater than the given value, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must contain . + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. + /// Thrown when the specified is greater than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.NotSubstring(parameter, value, parameterName, message); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + { + Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string that must contain . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !value.Contains(parameter)) - Throw.CustomException(exceptionFactory, parameter, value!); + if (parameter is null || parameter.CompareTo(other) > 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + return parameter; } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// Ensures that the specified is less than the given value, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must contain . - /// One of the enumeration values that specifies the rules for the search. + /// The comparable to be checked. + /// The boundary value that must be greater than . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// Thrown when the specified is not less than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) == -1) - Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + { + Throw.MustBeLessThan(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string that must contain . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not contain , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + if (parameter is null || parameter.CompareTo(other) >= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must not contain . + /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// + /// The value to be checked. + /// The items that must not contain the value. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. + /// Thrown when is equal to one of the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.Substring(parameter, value, parameterName, message); + // ReSharper disable PossibleMultipleEnumeration + if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + { + Throw.ValueIsOneOf(parameter, items, parameterName, message); + } + return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string that must not contain . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when or is null. + /// The value to be checked. + /// The items that must not contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("items:null => halt")] + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.Contains(parameter)) - Throw.CustomException(exceptionFactory, parameter, value!); + if (items is null || parameter.IsOneOf(items)) + { + Throw.CustomException(exceptionFactory, parameter, items!); + } + return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// Checks if the specified span is empty or contains only white space characters. /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// The span to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static bool IsEmptyOrWhiteSpace(this Span span) => ((ReadOnlySpan)span).IsEmptyOrWhiteSpace(); + /// + /// Checks if the specified span is empty or contains only white space characters. + /// + /// The span to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) { - if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) != -1) - Throw.Substring(parameter, value, comparisonType, parameterName, message); - return parameter; + if (span.IsEmpty) + { + return true; + } + + foreach (var character in span) + { + if (!character.IsWhiteSpace()) + { + return false; + } + } + + return true; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Checks if the specified memory is empty or contains only white space characters. /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The memory to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static bool IsEmptyOrWhiteSpace(this Memory memory) => memory.Span.IsEmptyOrWhiteSpace(); + /// + /// Checks if the specified memory is empty or contains only white space characters. + /// + /// The memory to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + { + Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that the string starts with the specified value, or otherwise throws a . + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string must start with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not start with . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) { - Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . /// - /// The string to be checked. - /// The other string must start with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// + /// The type of the enum. + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is no valid enum value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : struct, Enum { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.StartsWith(value)) + if (!EnumInfo.IsValidEnumValue(parameter)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.EnumValueNotDefined(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . /// - /// The string to be checked. - /// The other string must start with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The type of the enum. + /// The value to be checked. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is no valid enum value, or when is no enum type. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) + where T : struct, Enum { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) + if (!EnumInfo.IsValidEnumValue(parameter)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws a . + /// Ensures that the specified GUID is not empty, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The GUID to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when starts with . - /// Thrown when or is null. - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// Thrown when is an empty GUID. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Guid MustNotBeEmpty(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + if (parameter == Guid.Empty) { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + Throw.EmptyGuid(parameterName, message); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string that must not start with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// + /// The GUID to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is an empty GUID. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value)) + if (parameter == Guid.Empty) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// Checks if the specified strings are equal, using the given comparison rules. /// - /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The first string to compare. + /// The second string to compare. + /// One of the enumeration values that specifies the rules for the comparison. + /// True if the two strings are considered equal, else false. + /// Thrown when is no valid enum value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + if ((int)comparisonType < 6) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + return string.Equals(@string, value, (StringComparison)comparisonType); } - return parameter; + switch (comparisonType) + { + case StringComparisonType.OrdinalIgnoreWhiteSpace: + return @string.EqualsOrdinalIgnoreWhiteSpace(value); + case StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace: + return @string.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(value); + default: + Throw.EnumValueNotDefined(comparisonType, nameof(comparisonType)); + return false; + } } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Ensures that the specified is not greater than the given value, or otherwise throws an . /// - /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not end with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) - { - Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); - } - + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); return parameter; } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string must end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. - /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value)) - { - Throw.CustomException(exceptionFactory, parameter, value!); - } - + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + Throw.CustomException(exceptionFactory, parameter!, other); return parameter; } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions + /// are regarded as equal. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); + /// + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// This overload uses the specified to compare the types. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); + /// + /// Checks if the given is equal to the specified or if it derives from it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); + /// + /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified + /// to compare the types. + /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); + /// + /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is not trimmed at the start, i.e. they start with white space characters. + /// Empty strings are regarded as trimmed. /// + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.NotTrimmedAtStart(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when ends with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) { - Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// Checks if the type implements the specified interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The string to be checked. - /// The other string must not end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + /// The type to be checked. + /// The interface type that should implement. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value)) + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) { - Throw.CustomException(exceptionFactory, parameter, value!); + if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) + { + return true; + } } - return parameter; + return false; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// Checks if the type implements the specified interface type. This overload uses the specified + /// to compare the interface types. /// - /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + /// The type to be checked. + /// The interface type that should implement. + /// The equality comparer used to compare the interface types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] + public static bool Implements( +#if NET8_0 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + typeComparer.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) + { + return true; + } } - return parameter; + return false; } /// - /// Checks if the specified string is an email address using the default email regular expression - /// defined in . - /// - /// The string to be checked if it is an email address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); - /// - /// Checks if the specified string is an email address using the provided regular expression for validation. - /// - /// The string to be checked. - /// The regular expression that determines whether the input string is an email address. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); - /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws an . + /// Ensures that the specified URI is a relative one, or otherwise throws an . /// - /// The email address that will be validated. + /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. + /// Thrown when is an absolute URI. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) - Throw.InvalidEmailAddress(parameter, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) + { + Throw.MustBeRelativeUri(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws your custom exception. + /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. /// - /// The email address that will be validated. + /// The URI to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. + /// Your custom exception thrown when is an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (!parameter.IsEmailAddress()) + if (parameter is null || parameter.IsAbsoluteUri) + { Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) - Throw.InvalidEmailAddress(parameter, parameterName, message); + if (parameter.Kind != DateTimeKind.Utc) + { + Throw.MustBeUtcDateTime(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) - Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); + if (parameter.Kind != DateTimeKind.Utc) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the string is shorter than the specified length, or otherwise throws a . + /// Checks if the specified character is a white space character. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); + /// + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . /// /// The string to be checked. - /// The length that the string must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than or equal to . + /// Thrown when is not equal to "\n" or "\r\n". /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length >= length) - Throw.StringNotShorterThan(parameter, length, parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) + { + Throw.NotNewLine(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . /// /// The string to be checked. - /// The length that the string must be shorter than. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than or equal to . + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is not equal to "\n" or "\r\n". [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter is null || parameter.Length >= length) - Throw.CustomException(exceptionFactory, parameter, length); + if (!parameter.IsNewLine()) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// Checks if the specified string represents a valid file extension. /// - /// The string to be checked. - /// The length that the string must be shorter than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than . - /// Thrown when is null. + /// + /// The string to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the string is a valid file extension, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static bool IsFileExtension([NotNullWhen(true)] this string? value) => value != null && IsFileExtension(value.AsSpan()); + /// + /// Checks if the specified character span represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this Span value) => IsFileExtension((ReadOnlySpan)value); + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlyMemory value) => IsFileExtension(value.Span); + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this Memory value) => IsFileExtension(value.Span); + /// + /// Checks if the specified character span represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlySpan value) { - if (parameter.MustNotBeNull(parameterName, message).Length > length) - Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); - return parameter; + // ReSharper disable once UseIndexFromEndExpression -- cannot use index from end expression in .NET Standard 2.0 + if (value.Length <= 1 || value[0] != '.' || value[value.Length - 1] == '.') + { + return false; + } + + var hasAlphanumeric = false; + for (var i = 1; i < value.Length; i++) + { + var character = value[i]; + if (character.IsLetterOrDigit()) + { + hasAlphanumeric = true; + } + else if (character != '.') + { + return false; + } + } + + return hasAlphanumeric; } /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . /// /// The string to be checked. - /// The length that the string must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) - { - if (parameter is null || parameter.Length > length) - Throw.CustomException(exceptionFactory, parameter, length); - return parameter; - } - + [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] + public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; /// - /// Ensures that the string has the specified length, or otherwise throws a . + /// Checks if the given is a generic type that has open generic parameters, + /// but is no generic type definition. + /// + /// The type to be checked. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOpenConstructedGenericType([NotNull][ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; + /// + /// Checks if the specified string is an email address using the default email regular expression + /// defined in . + /// + /// The string to be checked if it is an email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); + /// + /// Checks if the specified string is an email address using the provided regular expression for validation. + /// + /// The string to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); + /// + /// Checks if the specified character is a letter. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLetter(this char character) => char.IsLetter(character); + /// + /// Checks if the string is a substring of the other string. + /// + /// The string to be checked. + /// The other string. + /// True if is a substring of , else false. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper restore RedundantNullableFlowAttribute + /// + /// Checks if the string is a substring of the other string. + /// + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if is a substring of , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + /// + /// Ensures that the string's length is within the specified range, or otherwise throws a . /// /// The string to be checked. - /// The asserted length of the string. + /// The range where the string's length must be in-between. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length other than . + /// Thrown when the length of is not with the specified . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length != length) - Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) + { + Throw.StringLengthNotInRange(parameter, range, parameterName, message); + } + return parameter; } /// - /// Ensures that the string has the specified length, or otherwise throws your custom exception. + /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. /// /// The string to be checked. - /// The asserted length of the string. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length other than . + /// The range where the string's length must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or its length is not within the specified range. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) { - if (parameter is null || parameter.Length != length) - Throw.CustomException(exceptionFactory, parameter, length); + if (parameter is null || !range.IsValueWithinRange(parameter.Length)) + { + Throw.CustomException(exceptionFactory, parameter, range); + } + return parameter; } /// - /// Ensures that the string is longer than the specified length, or otherwise throws a . + /// Ensures that the specified parameter is not the default value, or otherwise throws an + /// for reference types, or an for value types. /// - /// The string to be checked. - /// The length that the string must be longer than. + /// The value to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length shorter than or equal to . - /// Thrown when is null. + /// Thrown when is a reference type and null. + /// Thrown when is a value type and the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length <= length) - Throw.StringNotLongerThan(parameter, length, parameterName, message); + if (default(T)is null) + { + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default !)) + { + Throw.ArgumentDefault(parameterName, message); + } + +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } /// - /// Ensures that the string is longer than the specified length, or otherwise throws your custom exception. + /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The length that the string must be longer than. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length shorter than or equal to . + /// The value to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) { - if (parameter is null || parameter.Length <= length) - Throw.CustomException(exceptionFactory, parameter, length); + if (default(T)is null) + { + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default !)) + { + Throw.CustomException(exceptionFactory); + } + +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . + /// Ensures that the string is shorter than the specified length, or otherwise throws a . /// /// The string to be checked. - /// The length that the string must be longer than or equal to. + /// The length that the string must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length shorter than . + /// Thrown when has a length greater than or equal to . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length < length) - Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).Length >= length) + { + Throw.StringNotShorterThan(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. /// /// The string to be checked. - /// The length that the string must be longer than or equal to. + /// The length that the string must be shorter than. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length shorter than . + /// Your custom exception thrown when is null or when it has a length greater than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (parameter is null || parameter.Length < length) + if (parameter is null || parameter.Length >= length) + { Throw.CustomException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the string's length is within the specified range, or otherwise throws a . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The string to be checked. - /// The range where the string's length must be in-between. + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the length of is not with the specified . - /// Thrown when is null. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) - Throw.StringLengthNotInRange(parameter, range, parameterName, message); + ((ReadOnlySpan)parameter).MustBeShorterThan(length, parameterName, message); return parameter; } /// - /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The range where the string's length must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or its length is not within the specified range. + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) + public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is null || !range.IsValueWithinRange(parameter.Length)) - Throw.CustomException(exceptionFactory, parameter, range); + if (parameter.Length >= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] - public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; - /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The string to be checked. + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to "\n" or "\r\n". - /// Thrown when is null. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) - Throw.NotNewLine(parameter, parameterName, message); + if (parameter.Length >= length) + { + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is not equal to "\n" or "\r\n". + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (!parameter.IsNewLine()) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length >= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Checks if the specified string is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty string will return true. When null is passed, - /// you can control the return value with which will - /// return true by default. + /// Checks if the specified collection is null or empty. /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed, else false. An empty string will result in true. - /// You can control the return value with when the - /// is null. - /// + /// The collection to be checked. + /// True if the collection is null or empty, else false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); + [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; /// - /// Checks if the specified character span is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty span will return true. + /// Checks if the specified string is null or empty. /// - /// The character span to be checked. - /// True if the is trimmed, else false. An empty span will result in true. + /// The string to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace() && !parameter[parameter.Length - 1].IsWhiteSpace(); + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); /// - /// Ensures that the string is not null and trimmed, or otherwise throws a . + /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . + /// + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); + /// + /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "https", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); + /// + /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . /// Empty strings are regarded as trimmed. /// /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when is not trimmed, i.e. they start or end with white space characters. + /// Thrown when is not trimmed at the end, i.e. they end with white space characters. /// Empty strings are regarded as trimmed. /// /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) - Throw.NotTrimmed(parameter, parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) + { + Throw.NotTrimmedAtEnd(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed at the end, or otherwise throws your custom exception. /// Empty strings are regarded as trimmed. /// /// The string to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. + /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmed()) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) + { Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Checks if the specified string is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the start, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); - /// - /// Checks if the specified character span is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// - /// True if the is trimmed at the start, else false. - /// An empty span will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace(); - /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. /// - /// The string to be checked. + /// The value to be checked for null. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed at the start, i.e. they start with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. + /// Thrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) - Throw.NotTrimmedAtStart(parameter, parameterName, message); + if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. + /// + /// The value to be checked for null. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is a reference type and is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + { + if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + return parameter; } /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. + /// Ensures that the string does not end with the specified value, or otherwise throws a . /// /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when ends with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Checks if the specified string is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the start, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); - /// - /// Checks if the specified character span is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// - /// True if the is trimmed at the end, else false. - /// An empty span will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); - /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed at the end, i.e. they end with white space characters. - /// Empty strings are regarded as trimmed. + /// The other string must not end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. /// - /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) - Throw.NotTrimmedAtEnd(parameter, parameterName, message); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; } /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) - Throw.CustomException(exceptionFactory, parameter); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } /// - /// Ensures that the specified URI is an absolute one, or otherwise throws a . + /// Ensures that the string has the specified length, or otherwise throws a . /// - /// The URI to be checked. + /// The string to be checked. + /// The asserted length of the string. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not an absolute URI. + /// Thrown when has a length other than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) - Throw.MustBeAbsoluteUri(parameter, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).Length != length) + { + Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. + /// Ensures that the string has the specified length, or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when is not an absolute URI, or when is null. + /// The string to be checked. + /// The asserted length of the string. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length other than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (parameter is null || parameter.IsAbsoluteUri == false) - Throw.CustomException(exceptionFactory, parameter); + if (parameter is null || parameter.Length != length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the specified URI is a relative one, or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws an . /// - /// The URI to be checked. + /// The span to be checked. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an absolute URI. - /// Thrown when is null. + /// Thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) - Throw.MustBeRelativeUri(parameter, parameterName, message); + ((ReadOnlySpan)parameter).MustHaveLength(length, parameterName, message); return parameter; } /// - /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an absolute URI, or when is null. + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.IsAbsoluteUri) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length != length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The scheme that the URI should have. + /// The span to be checked. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than the specified one. - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) - Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + if (parameter.Length != length) + { + Throw.InvalidSpanLength(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than the specified one, - /// or when is a relative URI, - /// or when is null. - /// + /// The span to be checked. + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) - Throw.CustomException(exceptionFactory, parameter); + if (parameter.Length != length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Checks if the specified type derives from the other type. Internally, this method uses + /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. and are passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than the specified one, - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + /// The type info to be checked. + /// The base class that should derive from. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt")] + public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass) { - if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) - Throw.CustomException(exceptionFactory, parameter, scheme); - return parameter; + baseClass.MustNotBeNull(nameof(baseClass)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (currentBaseType.IsEquivalentTypeTo(baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; + } + + return false; } /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "https". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); - /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// Checks if the specified type derives from the other type. This overload uses the specified + /// to compare the types. /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than "https", - /// or when is a relative URI, - /// or when is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); + /// The type info to be checked. + /// The base class that should derive from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] + public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) + { + baseClass.MustNotBeNull(nameof(baseClass)); + typeComparer.MustNotBeNull(nameof(typeComparer)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) + { + if (typeComparer.Equals(currentBaseType, baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; + } + + return false; + } + /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The URI to be checked. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. + /// + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// - /// Your custom exception thrown when uses a different scheme than "http", - /// or when is a relative URI, - /// or when is null. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, Func exceptionFactory) + { + if (!parameter.IsGreaterThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The URI to be checked. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http" or "https". - /// Thrown when is relative and thus has no scheme. - /// Throw when is null. + /// + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) - Throw.UriMustHaveOneSchemeOf(parameter, new[] { "https", "http" }, parameterName, message); + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// /// - /// Your custom exception thrown when uses a different scheme than "http" or "https", - /// or when is a relative URI, - /// or when is null. + /// Your custom exception thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) - Throw.CustomException(exceptionFactory, parameter); + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws an . + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the scheme is not equal to one of the specified schemes. - /// Thrown when is relative and thus has no scheme. - /// Throw when or is null. + /// + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] - public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - // ReSharper disable PossibleMultipleEnumeration - parameter.MustBeAbsoluteUri(parameterName, message); - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - return parameter; - } - - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - return parameter; - // ReSharper restore PossibleMultipleEnumeration - } - + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. + /// Ensures that the specified is greater than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. - /// The delegate that creates your custom exception. - /// - /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, - /// or when is a relative URI, - /// or when is null. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not greater than or approximately equal to . /// - /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) - where TCollection : class, IEnumerable + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, Func exceptionFactory) { - if (parameter is null || !parameter.IsAbsoluteUri) - Throw.CustomException(exceptionFactory, parameter, schemes); - if (schemes is ICollection collection) + if (!parameter.IsGreaterThanOrApproximately(other)) { - if (!collection.Contains(parameter.Scheme)) - Throw.CustomException(exceptionFactory, parameter, schemes); - return parameter; + Throw.CustomException(exceptionFactory, parameter, other); } - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (schemes is null || !schemes.Contains(parameter.Scheme)) - Throw.CustomException(exceptionFactory, parameter, schemes!); return parameter; } - /* - * ------------------------------------- - * Must Not Be Less Than - * Must Be Greater Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. + /// + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustNotBeLessThan(parameter, other, parameterName, message); + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. + /// The value to be checked. + /// The value that should be greater than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs off - if (parameter is null || parameter.CompareTo(other) < 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the specified is greater than the given value, or otherwise throws an . /// /// The comparable to be checked. - /// The boundary value that must be less than or equal to . + /// The boundary value that must be less than . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . + /// Thrown when the specified is less than or equal to . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + { + Throw.MustBeGreaterThan(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. /// /// The comparable to be checked. - /// The boundary value that must be less than or equal to . + /// The boundary value that must be less than . /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) + if (parameter is null || parameter.CompareTo(other) <= 0) + { Throw.CustomException(exceptionFactory, parameter!, other); + } + return parameter; } - /* - * ------------------------------------- - * Must Be Less Than - * Must Not Be Greater Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustBeLessThan(parameter, other, parameterName, message); - return parameter; - } - + public static double MustBeLessThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static double MustBeLessThanOrApproximately(this double parameter, double other, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!parameter.IsLessThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) - Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } - /* - * ------------------------------------- - * Must Be Greater Than - * Must Not Be Less Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustBeGreaterThan(parameter, other, parameterName, message); - return parameter; - } - + public static float MustBeLessThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static float MustBeLessThanOrApproximately(this float parameter, float other, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!parameter.IsLessThanOrApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . + /// + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!parameter.IsLessThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } - /* - * ------------------------------------- - * Must Not Be Greater Than - * Must Be Less Than or Equal To - * ------------------------------------- - */ /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Checks if the value is not within the specified range. /// /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . + /// The range where must not be in-between. + /// True if the parameter is not within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNotIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => !range.IsValueWithinRange(parameter); + /// + /// Ensures that the collection contains the specified item, or otherwise throws a . + /// + /// The collection to be checked. + /// The item that must be part of the collection. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . + /// Thrown when does not contain . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// The collection to be checked. + /// The item that must be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is ICollection collection) + { + if (!collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } + + if (parameter is null || !parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Ensures that the string contains the specified substring, or otherwise throws a . /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . + /// The string to be checked. + /// The substring that must be part of . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. + /// Thrown when does not contain . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + { + Throw.StringDoesNotContain(parameter, value, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// The string to be checked. + /// The substring that must be part of . + /// The delegate that creates you custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (parameter is null || value is null || !parameter.Contains(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; } - /* - * ------------------------------------- - * Ranges - * ------------------------------------- - */ - /// - /// Checks if the value is within the specified range. - /// - /// The comparable to be checked. - /// The range where must be in-between. - /// True if the parameter is within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsIn([NotNull, ValidatedNotNull] this T parameter, Range range) - where T : IComparable => range.IsValueWithinRange(parameter); - /// - /// Checks if the value is not within the specified range. - /// - /// The comparable to be checked. - /// The range where must not be in-between. - /// True if the parameter is not within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNotIn([NotNull, ValidatedNotNull] this T parameter, Range range) - where T : IComparable => !range.IsValueWithinRange(parameter); /// - /// Ensures that is within the specified range, or otherwise throws an . + /// Ensures that the string contains the specified value, or otherwise throws a . /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must be in-between. + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not within . - /// Thrown when is null. + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustBeInRange(parameter, range, parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) + { + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that is within the specified range, or otherwise throws your custom exception. + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. /// - /// The parameter to be checked. - /// The range where must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not within , or when is null. + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates you custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || !range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws an . + /// Ensures that the specified is not less than the given value, or otherwise throws an . /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must not be in-between. + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is within . + /// Thrown when the specified is less than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { - if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) - Throw.MustNotBeInRange(parameter, range, parameterName, message); + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + { + Throw.MustNotBeLessThan(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// The parameter to be checked. - /// The range where must not be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is within , or when is null. + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || range.IsValueWithinRange(parameter)) - Throw.CustomException(exceptionFactory, parameter!, range); + if (parameter is null || parameter.CompareTo(other) < 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + return parameter; } } @@ -3934,7 +5384,7 @@ internal sealed class EquivalentTypeComparer : IEqualityComparer /// /// Gets a singleton instance of the equality comparer. /// - public static readonly EquivalentTypeComparer Instance = new EquivalentTypeComparer(); + public static readonly EquivalentTypeComparer Instance = new(); /// /// Checks if the two types are equivalent (using ). /// This check works like the normal type equality comparison, but when two @@ -3951,7 +5401,7 @@ internal sealed class EquivalentTypeComparer : IEqualityComparer /// /// The type whose hash code is requested. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalse + public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); } @@ -4012,7 +5462,7 @@ public Range(T from, T to, bool isFromInclusive = true, bool isToInclusive = tru /// The value that indicates the inclusive lower boundary of the resulting range. /// A value you can use to fluently define the upper boundary of a new range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RangeFromInfo FromInclusive(T value) => new RangeFromInfo(value, true); + public static RangeFromInfo FromInclusive(T value) => new(value, true); /// /// Use this method to create a range in a fluent style using method chaining. /// Defines the lower boundary as an exclusive value. @@ -4020,7 +5470,7 @@ public Range(T from, T to, bool isFromInclusive = true, bool isToInclusive = tru /// The value that indicates the exclusive lower boundary of the resulting range. /// A value you can use to fluently define the upper boundary of a new range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RangeFromInfo FromExclusive(T value) => new RangeFromInfo(value, false); + public static RangeFromInfo FromExclusive(T value) => new(value, false); /// /// The nested can be used to fluently create a . /// @@ -4050,7 +5500,7 @@ public RangeFromInfo(T from, bool isFromInclusive) /// Thrown when is less than the lower boundary value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range ToExclusive(T value) => new Range(_from, value, _isFromInclusive, false); + public Range ToExclusive(T value) => new(_from, value, _isFromInclusive, false); /// /// Use this method to create a range in a fluent style using method chaining. /// Defines the upper boundary as an inclusive value. @@ -4061,7 +5511,7 @@ public RangeFromInfo(T from, bool isFromInclusive) /// Thrown when is less than the lower boundary value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range ToInclusive(T value) => new Range(_from, value, _isFromInclusive); + public Range ToInclusive(T value) => new(_from, value, _isFromInclusive); } /// @@ -4087,7 +5537,10 @@ public RangeFromInfo(T from, bool isFromInclusive) public bool Equals(Range other) { if (IsFromInclusive != other.IsFromInclusive || IsToInclusive != other.IsToInclusive) + { return false; + } + var comparer = EqualityComparer.Default; return comparer.Equals(From, other.From) && comparer.Equals(To, other.To); } @@ -4096,7 +5549,10 @@ public bool Equals(Range other) public override bool Equals(object? other) { if (other is null) + { return false; + } + return other is Range range && Equals(range); } @@ -4166,7 +5622,7 @@ public static Range ExclusiveBetween(T from, T to) /// /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(IEnumerable enumerable) => new(0, enumerable.Count(), isFromInclusive: true, isToInclusive: false); + public static Range For(IEnumerable enumerable) => new(0, enumerable.Count(), true, false); /// /// Creates a range for the specified enumerable that encompasses all valid indexes. /// @@ -4175,7 +5631,7 @@ public static Range ExclusiveBetween(T from, T to) /// is actually a collection, not a lazy enumerable. /// /// Thrown when is null. - public static Range For(IEnumerable enumerable) => new(0, enumerable.GetCount(), isFromInclusive: true, isToInclusive: false); + public static Range For(IEnumerable enumerable) => new(0, enumerable.GetCount(), true, false); /// /// Creates a range for the specified span that encompasses all valid indexes. /// @@ -4183,7 +5639,7 @@ public static Range ExclusiveBetween(T from, T to) /// The length of the span is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(ReadOnlySpan span) => new(0, span.Length, isFromInclusive: true, isToInclusive: false); + public static Range For(ReadOnlySpan span) => new(0, span.Length, true, false); /// /// Creates a range for the specified span that encompasses all valid indexes. /// @@ -4191,7 +5647,7 @@ public static Range ExclusiveBetween(T from, T to) /// The length of the span is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(Span span) => new(0, span.Length, isFromInclusive: true, isToInclusive: false); + public static Range For(Span span) => new(0, span.Length, true, false); /// /// Creates a range for the specified memory that encompasses all valid indexes. /// @@ -4199,7 +5655,7 @@ public static Range ExclusiveBetween(T from, T to) /// The length of the memory is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(Memory memory) => new(0, memory.Length, isFromInclusive: true, isToInclusive: false); + public static Range For(Memory memory) => new(0, memory.Length, true, false); /// /// Creates a range for the specified memory that encompasses all valid indexes. /// @@ -4207,7 +5663,7 @@ public static Range ExclusiveBetween(T from, T to) /// The length of the memory is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(ReadOnlyMemory memory) => new(0, memory.Length, isFromInclusive: true, isToInclusive: false); + public static Range For(ReadOnlyMemory memory) => new(0, memory.Length, true, false); /// /// Creates a range for the specified memory that encompasses all valid indexes. /// @@ -4215,20 +5671,37 @@ public static Range ExclusiveBetween(T from, T to) /// The count of the segment is used to create a valid index range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(ArraySegment segment) => new(0, segment.Count, isFromInclusive: true, isToInclusive: false); + public static Range For(ArraySegment segment) => new(0, segment.Count, true, false); } /// /// Provides regular expressions that are used in string assertions. /// +#if NET8_0 +public static partial class RegularExpressions +#else internal static class RegularExpressions +#endif { + /// + /// Gets the string that represents the . + /// + // This is an AI-generated regex. I don't have any clue and I find it way to complex to ever understand it. + public const string EmailRegexText = @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+(?:\.[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+)*)@(?:(?:[A-Za-z0-9](?:[A-Za-z0-9\-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}|(?:\[(?:IPv6:[0-9A-Fa-f:.]+)\])|(?:25[0-5]|2[0-4]\d|[01]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)){3}))$"; /// /// Gets the default regular expression for email validation. /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. /// - public static readonly Regex EmailRegex = new Regex(@"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((((\w+\-?)+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$", RegexOptions.CultureInvariant | RegexOptions.ECMAScript); + public static readonly Regex EmailRegex = +#if NET8_0 + GenerateEmailRegex(); + + [GeneratedRegex(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant)] + private static partial Regex GenerateEmailRegex(); +#else + new(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); +#endif } /// @@ -4289,7 +5762,7 @@ internal enum StringComparisonType /// Compare strings using ordinal sort rules, ignoring the case and ignoring the /// white space characters of the strings being compared. /// - OrdinalIgnoreCaseIgnoreWhiteSpace = 7 + OrdinalIgnoreCaseIgnoreWhiteSpace = 7, } /// @@ -4322,7 +5795,9 @@ public int GetHashCode(string @string) foreach (var character in @string) { if (!character.IsWhiteSpace()) + { hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); + } } return hashBuilder.BuildHash(); @@ -4360,9 +5835,12 @@ static EnumInfo() #else EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); #endif - EnumConstants = new(EnumConstantsArray); + EnumConstants = new ReadOnlyMemory(EnumConstantsArray); if (!IsFlagsEnum) + { return; + } + for (var i = 0; i < EnumConstantsArray.Length; ++i) { var convertedValue = ConvertToUInt64(EnumConstantsArray[i]); @@ -4381,8 +5859,13 @@ private static bool IsValidValue(T parameter) { var comparer = EqualityComparer.Default; for (var i = 0; i < EnumConstantsArray.Length; ++i) + { if (comparer.Equals(EnumConstantsArray[i], parameter)) + { return true; + } + } + return false; } @@ -4409,14 +5892,11 @@ private static ulong ConvertToUInt64(T value) return Unsafe.As(ref value); default: ThrowUnknownEnumSize(); - return default; + return 0UL; } } - private static void ThrowUnknownEnumSize() - { - throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); - } + private static void ThrowUnknownEnumSize() => throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); } /// @@ -4448,21 +5928,15 @@ public int GetHashCode(string @string) foreach (var character in @string) { if (!character.IsWhiteSpace()) + { hashCodeBuilder.CombineIntoHash(character); + } } return hashCodeBuilder.BuildHash(); } } - /// - /// Represents a delegate that receives a span and a value as parameters and that produces an exception. - /// - internal delegate Exception SpanExceptionFactory(Span span, T value); - /// - /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. - /// - internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); [AttributeUsage(AttributeTargets.Parameter)] internal sealed class ValidatedNotNullAttribute : Attribute { @@ -5046,9 +6520,105 @@ protected InvalidDateTimeException(SerializationInfo info, StreamingContext cont #endif } + /// + /// This exception indicates that a string is empty. + /// + [Serializable] + internal class EmptyStringException : StringException + { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + { + } + +#if !NET8_0 + /// + protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + /// + /// This exception indicates that an item is not part of a collection. + /// + [Serializable] + internal class ValueIsNotOneOfException : ArgumentException + { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + { + } + +#if !NET8_0 + /// + protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + /// + /// This exception indicates that a value cannot be cast to another type. + /// + [Serializable] + internal class TypeCastException : ArgumentException + { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) + { + } + +#if !NET8_0 + /// + protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + /// + /// This exception indicates that the data is in invalid state. + /// + [Serializable] + internal class InvalidStateException : Exception + { + /// + /// Creates a new instance of . + /// + /// The message of the exception (optional). + /// The exception that is the cause of this one (optional). + public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) + { + } + +#if !NET8_0 + /// + protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } +} + +namespace Light.GuardClauses.ExceptionFactory +{ /// /// Provides static factory methods that throw default exceptions. /// + // ReSharper disable once RedundantTypeDeclarationBody - requried for the Source Code Transformation internal static class Throw { /// @@ -5058,275 +6628,337 @@ internal static class Throw [DoesNotReturn] public static void ArgumentNull(string? parameterName = null, string? message = null) => throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); /// - /// Throws the default indicating that a value is the default value of its type, using the optional parameter name and message. + /// Throws the default indicating that a string is empty, using the optional + /// parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ArgumentDefault(string? parameterName = null, string? message = null) => throw new ArgumentDefaultException(parameterName, message ?? $"{parameterName ?? "The value"} must not be the default value."); + public static void EmptyString(string? parameterName = null, string? message = null) => throw new EmptyStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is."); /// - /// Throws the default indicating that a reference cannot be downcast, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must not be + /// less than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidTypeCast(object? parameter, Type targetType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new TypeCastException(parameterName, message ?? $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"."); + public static void MustNotBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a value is not one of the constants defined in an enum, using the optional parameter name and message. + /// Throws the default indicating that a collection contains the specified item + /// that should not be part of it, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EnumValueNotDefined(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : Enum => throw new EnumValueNotDefinedException(parameterName, message ?? $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not."); + public static void ExistingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ExistingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does.").AppendCollectionContent(parameter).ToString()); /// - /// Throws the default indicating that a GUID is empty, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must be greater + /// than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EmptyGuid(string? parameterName = null, string? message = null) => throw new EmptyGuidException(parameterName, message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one."); + public static void MustBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}."); /// - /// Throws an using the optional message. + /// Throws the default indicating that a string is not trimmed at the end. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); + public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws an using the optional message. + /// Throws the default indicating that a value must be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidState(string? message = null) => throw new InvalidStateException(message); + public static void MustBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws an using the optional parameter name and message. + /// Throws the exception that is returned by . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void Argument(string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); + public static void CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); /// - /// Throws an using the optional message. + /// Throws the exception that is returned by . is + /// passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidEmailAddress(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"."); + public static void CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); /// - /// Throws the default indicating that a has no value, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NullableHasNoValue(string? parameterName = null, string? message = null) => throw new NullableHasNoValueException(parameterName, message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null."); + public static void CustomException(Func exceptionFactory, T1 first, T2 second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a comparable value must not be less than the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}."); + public static void CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); /// - /// Throws the default indicating that a comparable value must be less than the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}."); + public static void CustomSpanException(SpanExceptionFactory exceptionFactory, Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); /// - /// Throws the default indicating that a comparable value must not be less than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . is + /// passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}."); + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); /// - /// Throws the default indicating that a comparable value must not be greater than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}."); + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); /// - /// Throws the default indicating that a comparable value must be greater than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}."); + public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a comparable value must be greater than the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}."); + public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second, T third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); /// - /// Throws the default indicating that a comparable value must not be greater than the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a value is not within a specified + /// range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}."); + public static void MustBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); /// - /// Throws the default indicating that a comparable value must be less than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a value is within a specified + /// range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}."); + public static void MustNotBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); /// - /// Throws the default indicating that a value is not within a specified range, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must not be + /// less than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); + public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a value is within a specified range, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must be less + /// than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); + public static void MustBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that two references point to the same object, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must be greater + /// than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SameObjectReference(T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class => throw new SameObjectReferenceException(parameterName, message ?? $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does."); + public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a string is empty, using the optional parameter name and message. + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EmptyString(string? parameterName = null, string? message = null) => throw new EmptyStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is."); + public static void MustBeUtcDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); /// - /// Throws the default indicating that a string contains only white space, using the optional parameter name and message. + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void WhiteSpaceString(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"."); + public static void MustBeLocalDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); /// - /// Throws the default indicating that a string does not match a regular expression, using the optional parameter name and message. + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotMatch(string parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringDoesNotMatchException(parameterName, message ?? $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"."); + public static void MustBeUnspecifiedDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. + /// Throws the default indicating that a string is not equal to "\n" or "\r\n". /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); + public static void NotNewLine(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string does not contain another string as a substring, using the optional parameter name and message. + /// Throws the default indicating that a string is not shorter than the given + /// length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void StringNotShorterThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. + /// Throws the default indicating that a string is not shorter or equal to the + /// given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringContains(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}."); + public static void StringNotShorterThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string does contain another string as a substring, using the optional parameter name and message. + /// Throws the default indicating that a string has a different length than the + /// specified one, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringContains(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void StringLengthNotEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}."); + /// + /// Throws the default indicating that a string is not longer than the given + /// length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}."); + /// + /// Throws the default indicating that a string is not longer than or equal to + /// the given length, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringNotLongerThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); + /// + /// Throws the default indicating that a string's length is not in within the + /// given range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringLengthNotInRange(string parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void NotSubstring(string parameter, string other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not a substring of another one, using the optional parameter name and message. + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void NotSubstring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void Substring(string parameter, string other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\", but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is a substring of another one, using the optional parameter name and message. + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void Substring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. + /// Throws the default indicating that a span has an invalid length, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void InvalidSpanLength(ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. + /// Throws the default indicating that a span is not longer than the + /// specified length. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void SpanMustBeLongerThan(ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. + /// Throws the default indicating that a span is not longer than and + /// not equal to the specified length. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void SpanMustBeLongerThanOrEqualTo(ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. + /// Throws the default indicating that a span is not shorter than the + /// specified length. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void SpanMustBeShorterThan(ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. + /// Throws the default indicating that a span is not shorter than the + /// specified length. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringNotShorterThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}."); + public static void SpanMustBeShorterThanOrEqualTo(ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. + /// Throws the default indicating that a collection has less than a + /// minimum number of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringNotShorterThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}."); + public static void InvalidMinimumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}."); /// - /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. + /// Throws the default indicating that a string contains only white space, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringLengthNotEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}."); + public static void WhiteSpaceString(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringNotLongerThan(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}."); + public static void InvalidState(string? message = null) => throw new InvalidStateException(message); /// - /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. + /// Throws the default indicating that a URI is relative instead of absolute, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringNotLongerThanOrEqualTo(string parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); + public static void MustBeAbsoluteUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new RelativeUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. + /// Throws the default indicating that a URI is absolute instead of relative, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringLengthNotInRange(string parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); + public static void MustBeRelativeUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new AbsoluteUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a string is not equal to "\n" or "\r\n". + /// Throws the default indicating that a URI has an unexpected scheme, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NotNewLine(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}."); + public static void UriMustHaveScheme(Uri parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a string is not trimmed. + /// Throws the default indicating that a URI does not use one of a set of + /// expected schemes, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NotTrimmed(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}."); + public static void UriMustHaveOneSchemeOf(Uri parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes").AppendItemsWithNewLine(schemes).AppendLine($"but it actually is \"{parameter}\".").ToString()); + /// + /// Throws the default indicating that a collection has no items, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EmptyCollection(string? parameterName = null, string? message = null) => throw new EmptyCollectionException(parameterName, message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is."); /// /// Throws the default indicating that a string is not trimmed at the start. /// @@ -5334,302 +6966,277 @@ public static void SameObjectReference(T? parameter, [CallerArgumentExpressio [DoesNotReturn] public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not trimmed at the end. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); + public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); + /// + /// Throws the default indicating that a comparable value must be less + /// than or equal to the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that two values are not equal, using the optional parameter name and message. + /// Throws the default indicating that two values are not equal, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void ValuesNotEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesNotEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that two values are equal, using the optional parameter name and message. + /// Throws the default indicating that two values are equal, using the optional + /// parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void ValuesEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a collection has an invalid number of items, using the optional parameter name and message. + /// Throws the default indicating that a value is not one of the + /// constants defined in an enum, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}."); + public static void EnumValueNotDefined(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : Enum => throw new EnumValueNotDefinedException(parameterName, message ?? $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not."); /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. + /// Throws the default indicating that a value must be less than or approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidSpanLength(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must have length {length}, but it actually has length {parameter.Length}."); + public static void MustBeLessThanOrApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws the default indicating that a span has an invalid length, using the optional parameter name and message. + /// Throws the default indicating that a value is the default value of its + /// type, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidSpanLength(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The read-only span"} must have length {length}, but it actually has length {parameter.Length}."); + public static void ArgumentDefault(string? parameterName = null, string? message = null) => throw new ArgumentDefaultException(parameterName, message ?? $"{parameterName ?? "The value"} must not be the default value."); /// - /// Throws the default indicating that a collection has less than a minimum number of items, using the optional parameter name and message. + /// Throws an using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidMinimumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}."); + public static void Argument(string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); + /// + /// Throws the default indicating that a collection is not containing the + /// specified item, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MissingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new MissingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not.").AppendCollectionContent(parameter).ToString()); /// - /// Throws the default indicating that a span is not longer than the specified length. + /// Throws the default indicating that a value is one of a specified collection + /// of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeLongerThan(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}."); + public static void ValueIsOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); /// - /// Throws the default indicating that a span is not longer than the specified length. + /// Throws the default indicating that two references point to the same + /// object, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeLongerThan(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than {length}, but it actually has length {parameter.Length}."); + public static void SameObjectReference(T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class => throw new SameObjectReferenceException(parameterName, message ?? $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does."); /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. + /// Throws the default indicating that a GUID is empty, using the optional + /// parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); + public static void EmptyGuid(string? parameterName = null, string? message = null) => throw new EmptyGuidException(parameterName, message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one."); /// - /// Throws the default indicating that a span is not longer than and not equal to the specified length. + /// Throws the default indicating that a string is not trimmed. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeLongerThanOrEqualTo(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); + public static void NotTrimmed(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a span is not shorter than the specified length. + /// Throws the default indicating that a comparable value must not be + /// greater than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeShorterThan(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}."); + public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a span is not shorter than the specified length. + /// Throws the default indicating that a has + /// no value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo(in Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); + public static void NullableHasNoValue(string? parameterName = null, string? message = null) => throw new NullableHasNoValueException(parameterName, message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null."); /// - /// Throws the default indicating that a span is not shorter than the specified length. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeShorterThanOrEqualTo(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); + public static void InvalidEmailAddress(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a span is not shorter than the specified length. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanMustBeShorterThan(in ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than {length}, but it actually has length {parameter.Length}."); + public static void InvalidEmailAddress(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"."); /// - /// Throws the default indicating that a collection has more than a maximum number of items, using the optional parameter name and message. + /// Throws the default indicating that a collection has more than a + /// maximum number of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] public static void InvalidMaximumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}."); /// - /// Throws the default indicating that a collection has no items, using the optional parameter name and message. + /// Throws the default indicating that a string is not a valid file extension. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EmptyCollection(string? parameterName = null, string? message = null) => throw new EmptyCollectionException(parameterName, message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is."); + public static void NotFileExtension(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a collection is not containing the specified item, using the optional parameter name and message. + /// Throws the default indicating that a string is not a valid file extension. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MissingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new MissingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not.").AppendCollectionContent(parameter).ToString()); + public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}."); /// - /// Throws the default indicating that a collection contains the specified item that should not be part of it, using the optional parameter name and message. + /// Throws the default indicating that a value must be greater than or approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ExistingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ExistingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does.").AppendCollectionContent(parameter).ToString()); + public static void MustBeGreaterThanOrApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws the default indicating that a value is not one of a specified collection of items, using the optional parameter name and message. + /// Throws the default indicating that a string does not end with another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ValueNotOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsNotOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); + public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a value is one of a specified collection of items, using the optional parameter name and message. + /// Throws the default indicating that a reference cannot be downcast, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ValueIsOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); + public static void InvalidTypeCast(object? parameter, Type targetType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new TypeCastException(parameterName, message ?? $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"."); /// - /// Throws the default indicating that a URI is relative instead of absolute, using the optional parameter name and message. + /// Throws the default indicating that a collection has an invalid + /// number of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeAbsoluteUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new RelativeUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"."); + public static void InvalidCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}."); /// - /// Throws the default indicating that a URI is absolute instead of relative, using the optional parameter name and message. + /// Throws the default indicating that a string ends with another one, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeRelativeUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new AbsoluteUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"."); + public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a URI has an unexpected scheme, using the optional parameter name and message. + /// Throws the default indicating that a string does not match a regular + /// expression, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void UriMustHaveScheme(Uri parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"."); + public static void StringDoesNotMatch(string parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringDoesNotMatchException(parameterName, message ?? $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"."); /// - /// Throws the default indicating that a URI does not use one of a set of expected schemes, using the optional parameter name and message. + /// Throws the default indicating that a value is not one of a specified + /// collection of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void UriMustHaveOneSchemeOf(Uri parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes").AppendItemsWithNewLine(schemes).AppendLine($"but it actually is \"{parameter}\".").ToString()); + public static void ValueNotOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsNotOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// Throws the default indicating that a value must not be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeUtcDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Utc}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); + public static void MustNotBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeLocalDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Local}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); + public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a date time is not using , using the optional parameter name and message. + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeUnspecifiedDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); + public static void StringStartsWith(ReadOnlySpan parameter, ReadOnlySpan other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}."); /// - /// Throws the exception that is returned by . + /// Throws the default indicating that a comparable value must not be + /// greater than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); + public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}."); /// - /// Throws the exception that is returned by . is passed to . + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); + public static void StringDoesNotContain(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the exception that is returned by . and are passed to . + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + public static void StringDoesNotContain(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the exception that is returned by . , , and are passed to . + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + public static void StringContains(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the exception that is returned by . and are passed to . + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(SpanExceptionFactory exceptionFactory, in Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + public static void StringContains(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the exception that is returned by . and are passed to . + /// Throws the default indicating that a string does not start with another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, in ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); } /// - /// This exception indicates that a string is empty. + /// Represents a delegate that receives a span and a value as parameters and that produces an exception. /// - [Serializable] - internal class EmptyStringException : StringException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) - { - } - -#if !NET8_0 - /// - protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif - } - + internal delegate Exception SpanExceptionFactory(Span span, T value); /// - /// This exception indicates that an item is not part of a collection. + /// Represents a delegate that receives a read-only span and produces an exception. /// - [Serializable] - internal class ValueIsNotOneOfException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - -#if !NET8_0 - /// - protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif - } - + internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span); /// - /// This exception indicates that a value cannot be cast to another type. + /// Represents a delegate that receives a read-only span and a value as parameters and that produces an exception. /// - [Serializable] - internal class TypeCastException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - -#if !NET8_0 - /// - protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif - } - + internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span, T value); /// - /// This exception indicates that the data is in invalid state. + /// Represents a delegate that receives two spans and produces an exception. /// - [Serializable] - internal class InvalidStateException : Exception - { - /// - /// Creates a new instance of . - /// - /// The message of the exception (optional). - /// The exception that is the cause of this one (optional). - public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) - { - } - -#if !NET8_0 - /// - protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif - } + internal delegate Exception ReadOnlySpansExceptionFactory(ReadOnlySpan span1, ReadOnlySpan span2); + /// + /// Represents a delegate that receives two spans and a value as parameters and that produces an exception. + /// + internal delegate Exception ReadOnlySpansExceptionFactory(ReadOnlySpan span1, ReadOnlySpan span2, T value); } namespace Light.GuardClauses.FrameworkExtensions @@ -5665,7 +7272,7 @@ public MultiplyAddHashBuilder CombineIntoHash(T value) /// Initializes a new instance of with the specified initial hash. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new MultiplyAddHashBuilder(initialHash); + public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new(initialHash); } /// @@ -5683,13 +7290,16 @@ internal static class ExpressionExtensions /// /// Throw when the is not of the shape "object => object.Property". /// - // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - public static PropertyInfo ExtractProperty([NotNull, ValidatedNotNull] this Expression> expression) + public static PropertyInfo ExtractProperty(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; if (!(memberExpression?.Member is PropertyInfo propertyInfo)) + { throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Property", nameof(expression)); + } + return propertyInfo; } @@ -5703,13 +7313,16 @@ public static PropertyInfo ExtractProperty([NotNull, ValidatedNotN /// /// Throw when the is not of the shape "object => object.Field". /// - // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - public static FieldInfo ExtractField([NotNull, ValidatedNotNull] this Expression> expression) + public static FieldInfo ExtractField(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); var memberExpression = expression.Body as MemberExpression; if (!(memberExpression?.Member is FieldInfo fieldInfo)) + { throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Field", nameof(expression)); + } + return fieldInfo; } } @@ -6044,7 +7657,7 @@ internal static class EnumerableExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); + public static IList AsList([NotNull][ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); /// /// Tries to cast the specified enumerable to an , or /// creates a new collection containing the enumerable items by calling the specified delegate. @@ -6056,8 +7669,8 @@ internal static class EnumerableExtensions /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + public static IList AsList(// ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); /// /// Tries to downcast the specified enumerable to an array, or creates a new array with the specified items. /// @@ -6068,7 +7681,7 @@ internal static class EnumerableExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static T[] AsArray([NotNull, ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); + public static T[] AsArray([NotNull][ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); /// /// Performs the action on each item of the specified enumerable. If the enumerable contains items that are null, this /// method can either throw an exception or ignore the value (your delegate will not be called in this case). @@ -6079,8 +7692,8 @@ internal static class EnumerableExtensions /// The value indicating whether this method should throw a when any of the items is null (optional). Defaults to true. /// Thrown when or is null. /// Thrown when contains a value that is null and is set to true. - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) + public static IEnumerable ForEach(// ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) { // ReSharper disable PossibleMultipleEnumeration action.MustNotBeNull(nameof(action)); @@ -6093,7 +7706,10 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer if (item is null) { if (throwWhenItemIsNull) + { throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + } + continue; } @@ -6107,7 +7723,10 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer if (item is null) { if (throwWhenItemIsNull) + { throw new CollectionException(nameof(enumerable), $"The collection contains null at index {i}."); + } + ++i; continue; } @@ -6132,7 +7751,7 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); + public static IReadOnlyList AsReadOnlyList([NotNull][ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); /// /// Tries to cast the specified enumerable as an , or /// creates a new collection containing the enumerable items by calling the specified delegate. @@ -6145,7 +7764,7 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source, [NotNull, ValidatedNotNull] Func, IReadOnlyList> createCollection) => source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + public static IReadOnlyList AsReadOnlyList([NotNull][ValidatedNotNull] this IEnumerable source, [NotNull][ValidatedNotNull] Func, IReadOnlyList> createCollection) => source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); // ReSharper restore RedundantNullableFlowAttribute /// /// Gets the count of the specified enumerable. @@ -6155,12 +7774,18 @@ public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumer [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) + public static int Count([NotNull][ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + return DetermineCountViaEnumerating(enumerable); } @@ -6173,12 +7798,18 @@ public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int Count([NotNull, ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) + public static int Count([NotNull][ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + return DetermineCountViaEnumerating(enumerable, parameterName, message); } @@ -6190,14 +7821,23 @@ public static int Count([NotNull, ValidatedNotNull] this IEnumerable? enumerable [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable) + public static int GetCount([NotNull][ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + if (TryGetCollectionOfTCount(enumerable, out var count)) + { return count; + } + return DetermineCountViaEnumerating(enumerable); } @@ -6210,14 +7850,23 @@ public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable en /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable, string? parameterName, string? message = null) + public static int GetCount([NotNull][ValidatedNotNull] this IEnumerable enumerable, string? parameterName, string? message = null) { if (enumerable is ICollection collection) + { return collection.Count; + } + if (enumerable is string @string) + { return @string.Length; + } + if (TryGetCollectionOfTCount(enumerable, out var count)) + { return count; + } + return DetermineCountViaEnumerating(enumerable, parameterName, message); } @@ -6244,7 +7893,10 @@ private static int DetermineCountViaEnumerating(IEnumerable? enumerable) var count = 0; var enumerator = enumerable.MustNotBeNull(nameof(enumerable)).GetEnumerator(); while (enumerator.MoveNext()) + { count++; + } + if (enumerator is IDisposable disposable) { disposable.Dispose(); @@ -6258,7 +7910,10 @@ private static int DetermineCountViaEnumerating([NotNull] IEnumerable? enumerabl var count = 0; var enumerator = enumerable.MustNotBeNull(parameterName, message).GetEnumerator(); while (enumerator.MoveNext()) + { count++; + } + if (enumerator is IDisposable disposable) { disposable.Dispose(); @@ -6273,7 +7928,9 @@ internal static bool ContainsViaForeach(this IEnumerable items, TI foreach (var i in items) { if (equalityComparer.Equals(i, item)) + { return true; + } } return false; @@ -6292,33 +7949,69 @@ internal static class TextExtensions /// /// Gets the list of types that will not be surrounded by quotation marks in error messages. /// - public static readonly ReadOnlyCollection UnquotedTypes = new([typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float)]); + public static readonly ReadOnlyCollection UnquotedTypes = new([typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float), ]); private static bool IsUnquotedType() { if (typeof(T) == typeof(int)) + { return true; + } + if (typeof(T) == typeof(long)) + { return true; + } + if (typeof(T) == typeof(short)) + { return true; + } + if (typeof(T) == typeof(sbyte)) + { return true; + } + if (typeof(T) == typeof(uint)) + { return true; + } + if (typeof(T) == typeof(ulong)) + { return true; + } + if (typeof(T) == typeof(ushort)) + { return true; + } + if (typeof(T) == typeof(byte)) + { return true; + } + if (typeof(T) == typeof(bool)) + { return true; + } + if (typeof(T) == typeof(double)) + { return true; + } + if (typeof(T) == typeof(decimal)) + { return true; + } + if (typeof(T) == typeof(float)) + { return true; + } + return false; } @@ -6337,12 +8030,15 @@ private static bool IsUnquotedType() /// /// The value whose string representation is requested. [ContractAnnotation("value:null => halt; value:notnull => notnull")] - public static string? ToStringRepresentation([NotNull, ValidatedNotNull] this T value) + public static string? ToStringRepresentation([NotNull][ValidatedNotNull] this T value) { value.MustNotBeNullReference(nameof(value)); var content = value.ToString(); if (IsUnquotedType() || content.IsNullOrEmpty()) + { return content; + } + // ReSharper disable UseIndexFromEndExpression -- not possible in netstandard2.0 if (content.Length <= 126) { @@ -6372,7 +8068,7 @@ private static bool IsUnquotedType() [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendCollectionContent([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); + public static StringBuilder AppendCollectionContent([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the string representations of the specified items to the string builder. @@ -6384,7 +8080,7 @@ private static bool IsUnquotedType() /// Thrown when or is null. [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + public static StringBuilder AppendItems([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); @@ -6392,14 +8088,22 @@ public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this Stri var currentIndex = 0; var itemsCount = list.Count; if (itemsCount == 0) + { return stringBuilder.Append(emptyCollectionText); + } + while (true) { stringBuilder.Append(list[currentIndex].ToStringOrNull()); if (currentIndex < itemsCount - 1) + { stringBuilder.Append(itemSeparator); + } else + { return stringBuilder; + } + ++currentIndex; } } @@ -6415,7 +8119,7 @@ public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this Stri [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItemsWithNewLine([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); + public static StringBuilder AppendItemsWithNewLine([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the value to the specified string builder if the condition is true. @@ -6426,11 +8130,14 @@ public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this Stri /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable once RedundantNullableFlowAttribute - public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) + public static StringBuilder AppendIf(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) { if (condition) + { stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); + } + return stringBuilder; } @@ -6443,11 +8150,14 @@ public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuil /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable once RedundantNullableFlowAttribute - public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") + public static StringBuilder AppendLineIf(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") { if (condition) + { stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); + } + return stringBuilder; } @@ -6457,7 +8167,7 @@ public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this String /// /// Thrown when any parameter is null. // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] Exception exception) + public static StringBuilder AppendExceptionMessages([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] Exception exception) // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); @@ -6467,7 +8177,10 @@ public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] // ReSharper disable once PossibleNullReferenceException stringBuilder.AppendLine(exception.Message); if (exception.InnerException is null) + { return stringBuilder; + } + stringBuilder.AppendLine(); exception = exception.InnerException; } @@ -6479,7 +8192,7 @@ public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] /// /// Thrown when is null. // ReSharper disable once RedundantNullableFlowAttribute - public static string GetAllExceptionMessages([NotNull, ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); + public static string GetAllExceptionMessages([NotNull][ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space /// of the provided strings. @@ -6487,11 +8200,20 @@ public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) { if (ReferenceEquals(x, y)) + { return true; + } + if (x is null || y is null) + { return false; + } + if (x.Length == 0) + { return y.Length == 0; + } + var indexX = 0; var indexY = 0; bool wasXSuccessful; @@ -6503,7 +8225,9 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { if (x[indexX++] != y[indexY++]) + { return false; + } } return wasXSuccessful == wasYSuccessful; @@ -6516,11 +8240,20 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, string? y) { if (ReferenceEquals(x, y)) + { return true; + } + if (x is null || y is null) + { return false; + } + if (x.Length == 0) + { return y.Length == 0; + } + var indexX = 0; var indexY = 0; bool wasXSuccessful; @@ -6532,7 +8265,9 @@ public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, strin while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) { if (char.ToLowerInvariant(x[indexX++]) != char.ToLowerInvariant(y[indexY++])) + { return false; + } } return wasXSuccessful == wasYSuccessful; @@ -6543,7 +8278,10 @@ private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, while (currentIndex < @string.Length) { if (!char.IsWhiteSpace(@string[currentIndex])) + { return true; + } + ++currentIndex; }