diff --git a/AGENTS.md b/AGENTS.md index df13986f..cf25ed3c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,3 +13,5 @@ Read ./ai-plans/AGENTS.md for details on how to write plans. ## Here is Your Space If you encounter something worth noting while you are working on this code base, write it down here in this section. Once you are finished, I will discuss it with you, and we can decide where to put your notes. + +- The source exporter used to merge `Check.*.cs` files in `DirectoryInfo.GetFiles` order, which is filesystem-dependent and unordered on APFS, so adding source files could reshuffle the entire generated single file. `SourceFileMerger` now sorts the file list with `OrderBy(f => f.Name, StringComparer.Ordinal)`, making the output deterministic. The regeneration that shipped with the sign guards contains the resulting one-time reshuffle; future diffs will only show actual content changes. diff --git a/Light.GuardClauses.SingleFile.cs b/Light.GuardClauses.SingleFile.cs index cda3893c..9d1d245f 100644 --- a/Light.GuardClauses.SingleFile.cs +++ b/Light.GuardClauses.SingleFile.cs @@ -54,159 +54,6 @@ namespace Light.GuardClauses // ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation internal static 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 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 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; - } - /// /// 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. @@ -260,120 +107,104 @@ public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotN } /// - /// Checks if the specified character is a letter. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetter(this char character) => char.IsLetter(character); - /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// Checks if the specified strings are equal, using the given comparison rules. /// - /// 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 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")] - public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) { - if (parameter.MustNotBeNull(parameterName, message).Length > length) + if ((int)comparisonType < 6) { - Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + 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 is shorter than or equal to 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 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) + /// 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([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType) { - if (parameter is null || parameter.Length > length) + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) { - Throw.CustomException(exceptionFactory, parameter, length); + if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) + { + return true; + } } - return parameter; + return false; } /// - /// Ensures that the span is shorter than or equal to 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 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) + /// 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([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) { - ((ReadOnlySpan)parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); - return parameter; + 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 shorter than or equal to 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 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 . + /// 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 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; baseClassOrInterfaceType:null => halt")] + public static bool InheritsFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType) => 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 your custom exception. + /// 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 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 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 ReadOnlySpan MustBeShorterThanOrEqualTo(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; typeComparer:null => halt")] + public static bool InheritsFrom([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); /// /// Checks if the specified is true and throws an in this case. /// @@ -424,517 +255,313 @@ public static void InvalidArgument(bool condition, T parameter, Func - /// Ensures that is not within the specified range, or otherwise throws an . + /// Checks if the specified is true and throws an in this case. /// - /// 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. + /// 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)] - [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 + public static void InvalidOperation(bool condition, string? message = null) { - if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + if (condition) { - Throw.MustNotBeInRange(parameter, range, parameterName, message); + Throw.InvalidOperation(message); } - - return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// Checks if the specified is true and throws an in this case. /// - /// 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 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)] - [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 + public static void InvalidState(bool condition, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || range.IsValueWithinRange(parameter)) + if (condition) { - Throw.CustomException(exceptionFactory, parameter!, range); + Throw.InvalidState(message); } - - return parameter; } /// - /// Checks if the given is a generic type that has open generic parameters, - /// but is no generic type definition. + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. /// - /// The type to be checked. - /// Thrown when is null. + /// 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)] - [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; + public static bool IsApproximately(this double value, double other, double tolerance) => Math.Abs(value - other) <= tolerance; /// - /// Checks if the specified character is a letter or digit. + /// 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 IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); + public static bool IsApproximately(this double value, double other) => Math.Abs(value - other) <= 0.0001; /// - /// 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. + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. /// - /// 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. - /// + /// 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 the is trimmed at the end, else false. - /// An empty string will result in true. + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); + public static bool IsApproximately(this float value, float other, float tolerance) => Math.Abs(value - other) <= tolerance; /// - /// 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. + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. /// - /// The character span to be checked. + /// The first value to be compared. + /// The second value to be compared. /// - /// True if the is trimmed at the end, else false. - /// An empty span will result in true. + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001f, otherwise false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); - /// - /// Ensures that the specified single-precision floating-point value is finite, or otherwise throws an . - /// + public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) <= 0.0001f; + /// Checks if the character is an ASCII code point. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeFinite(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static bool IsAscii(this char parameter) => parameter <= 0x7F; + /// Checks if the byte is an ASCII value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this byte parameter) => parameter <= 0x7F; + /// Checks if the string is non-null and contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this string? parameter) => parameter is not null && parameter.AsSpan().IsAscii(); + /// Checks if the character span contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this Span parameter) => ((ReadOnlySpan)parameter).IsAscii(); + /// Checks if the read-only character span contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this ReadOnlySpan parameter) { - if (!parameter.IsFinite()) + foreach (var value in parameter) { - Throw.NotFinite(parameter, parameterName, message); + if (value > 0x7F) + { + return false; + } } - return parameter; + return true; } - /// - /// Ensures that the specified single-precision floating-point value is finite, or otherwise throws your custom exception. - /// + /// Checks if the character memory contains only ASCII characters. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static float MustBeFinite(this float parameter, Func exceptionFactory) + public static bool IsAscii(this Memory parameter) => parameter.Span.IsAscii(); + /// Checks if the read-only character memory contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this ReadOnlyMemory parameter) => parameter.Span.IsAscii(); + /// Checks if the byte span contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this Span parameter) => ((ReadOnlySpan)parameter).IsAscii(); + /// Checks if the read-only byte span contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this ReadOnlySpan parameter) { - if (!parameter.IsFinite()) + foreach (var value in parameter) { - Throw.CustomException(exceptionFactory, parameter); + if (value > 0x7F) + { + return false; + } } - return parameter; + return true; } + /// Checks if the byte memory contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this Memory parameter) => parameter.Span.IsAscii(); + /// Checks if the read-only byte memory contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAscii(this ReadOnlyMemory parameter) => parameter.Span.IsAscii(); /// - /// Ensures that the specified double-precision floating-point value is finite, or otherwise throws an . + /// Checks if the specified character is a digit. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeFinite(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.IsFinite()) - { - Throw.NotFinite(parameter, parameterName, message); - } - - return parameter; - } - + public static bool IsDigit(this char character) => char.IsDigit(character); /// - /// Ensures that the specified double-precision floating-point value is finite, or otherwise throws your custom exception. + /// 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("exceptionFactory:null => halt")] - public static double MustBeFinite(this double parameter, Func exceptionFactory) - { - if (!parameter.IsFinite()) - { - Throw.CustomException(exceptionFactory, parameter); - } - - return parameter; - } - + [ContractAnnotation("emailAddress:null => false")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); /// - /// Ensures that the has at most the specified length, or otherwise throws an . + /// Checks if the specified string is an email address using the provided regular expression for validation. /// - /// The to be checked. - /// The maximum length the should have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when has more than the specified length. - /// The default instance of will be treated as having length 0. + /// 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)] - public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength > length) - { - Throw.InvalidMaximumImmutableArrayLength(parameter, length, parameterName, message); - } - - return parameter; - } - + [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 has at most the specified length, or otherwise throws your custom exception. + /// Checks if the specified GUID is an empty one. /// - /// The to be checked. - /// The maximum length the should have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when has more than the specified length. - /// The default instance of will be treated as having length 0. + /// The GUID to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) - { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength > length) - { - Throw.CustomException(exceptionFactory, parameter, length); - } - - return parameter; - } - + public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; /// - /// Ensures that the specified URI is a relative one, or otherwise throws an . + /// Checks if the specified span is empty or contains only white space characters. /// - /// 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. + /// The span to be checked. [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; - } - + public static bool IsEmptyOrWhiteSpace(this Span span) => ((ReadOnlySpan)span).IsEmptyOrWhiteSpace(); /// - /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. + /// Checks if the specified span is empty or contains only white space characters. /// - /// 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. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) { - if (parameter is null || parameter.IsAbsoluteUri) + if (span.IsEmpty) { - Throw.CustomException(exceptionFactory, parameter); + return true; } - 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) + foreach (var character in span) { - Throw.MustBeLocalDateTime(parameter, parameterName, message); + if (!character.IsWhiteSpace()) + { + return false; + } } - return parameter; + return true; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Checks if the specified memory is empty or contains only white space characters. /// - /// 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 memory to be checked. [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; - } - + public static bool IsEmptyOrWhiteSpace(this Memory memory) => memory.Span.IsEmptyOrWhiteSpace(); /// - /// Ensures that the string matches the specified regular expression, or otherwise throws a . + /// Checks if the specified memory is empty or contains only white space characters. /// - /// 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. + /// The memory to be checked. [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; - } - + public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); /// - /// Ensures that the string matches the specified regular expression, 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 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. - /// + /// 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 string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) + 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) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || regex is null || !regex.IsMatch(parameter)) + if (type.IsConstructedGenericType) { - Throw.CustomException(exceptionFactory, parameter, regex!); + return type.GetGenericTypeDefinition() == other; } - return parameter; + return other.GetGenericTypeDefinition() == type; } /// - /// Ensures that the specified is not default or empty, or otherwise throws an . + /// Checks if the specified string represents a valid file extension. /// - /// The to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is default or empty. + /// + /// 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 ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.IsDefaultOrEmpty) - { - Throw.EmptyCollection(parameterName, message); - } - - return parameter; - } - + public static bool IsFileExtension([NotNullWhen(true)] this string? value) => value != null && IsFileExtension(value.AsSpan()); /// - /// Ensures that the specified is not default or empty, or otherwise throws your custom exception. + /// Checks if the specified character span represents a valid file extension. /// - /// The to be checked. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is default or empty. + /// + /// 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)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, Func, Exception> exceptionFactory) - { - if (parameter.IsDefaultOrEmpty) - { - Throw.CustomException(exceptionFactory, parameter); - } - - return parameter; - } - + public static bool IsFileExtension(this Span value) => IsFileExtension((ReadOnlySpan)value); /// - /// 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 - /// . + /// Checks if the specified character memory represents a valid file extension. /// - /// 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 . - /// + /// + /// 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 double MustBeGreaterThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001, parameterName, message); + public static bool IsFileExtension(this ReadOnlyMemory value) => IsFileExtension(value.Span); /// - /// 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 - /// . + /// Checks if the specified character memory represents a valid file extension. /// - /// 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. + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. /// - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// True if the span is a valid file extension, false otherwise. [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; - } - + public static bool IsFileExtension(this Memory value) => IsFileExtension(value.Span); /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws an . + /// Checks if the specified character span represents a valid file extension. /// - /// 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 . - /// + /// + /// 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 double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static bool IsFileExtension(this ReadOnlySpan value) { - 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)) + // 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] == '.') { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + return false; } - 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)) + var hasAlphanumeric = false; + for (var i = 1; i < value.Length; i++) { - Throw.CustomException(exceptionFactory, parameter, other); + var character = value[i]; + if (character.IsLetterOrDigit()) + { + hasAlphanumeric = true; + } + else if (character != '.') + { + return false; + } } - return parameter; + return hasAlphanumeric; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws an . + /// Checks if the specified single-precision floating-point value is finite. /// - /// 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; - } - + public static bool IsFinite(this float parameter) => parameter >= float.MinValue && parameter <= float.MaxValue; /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Checks if the specified double-precision floating-point value is finite. /// - /// 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; - } - + public static bool IsFinite(this double parameter) => parameter >= double.MinValue && parameter <= double.MaxValue; /// /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// @@ -982,378 +609,321 @@ public static float MustBeGreaterThanOrApproximately(this float parameter, float [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Checks if the value is within the specified range. /// /// 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 . + /// 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)] - [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; - } - + public static bool IsIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => range.IsValueWithinRange(parameter); /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// - /// 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 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)] - [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; - } - + public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); /// - /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. /// - /// 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. + /// 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)] - [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 bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); /// - /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// - /// 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 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)] - [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; - } - + public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. /// - /// 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. + /// 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)] - [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); + public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// Checks if the specified character is a letter. /// - /// 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); + public static bool IsLetter(this char character) => char.IsLetter(character); /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Checks if the specified character is a letter or digit. /// - /// 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; - } - + public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); /// - /// Ensures that the specified is not greater than the given value, 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. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] + public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; + /// + /// 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 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 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)] - [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; - } - - /// Ensures that the character is ASCII, or otherwise throws an . + public static bool IsNotIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => !range.IsValueWithinRange(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)] - public static char MustBeAscii(this char parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.IsAscii()) - { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The character"} must be ASCII, but it actually is '{parameter}'."); - } - - return parameter; - } - - /// Ensures that the character is ASCII, or otherwise throws your custom exception. + [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("exceptionFactory:null => halt")] - public static char MustBeAscii(this char parameter, Func exceptionFactory) - { - if (!parameter.IsAscii()) - { - Throw.CustomException(exceptionFactory, parameter); - } - - return parameter; - } - - /// Ensures that the byte is ASCII, or otherwise throws an . + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); + /// + /// Checks if the specified string is null, empty, or contains only white space. + /// + /// The string to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte MustBeAscii(this byte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.IsAscii()) - { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The byte"} must be ASCII, but it actually is {parameter}."); - } - - return parameter; - } - - /// Ensures that the byte is ASCII, or otherwise throws your custom exception. + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); + /// + /// 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("exceptionFactory:null => halt")] - public static byte MustBeAscii(this byte 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 (!parameter.IsAscii()) + if (items is ICollection collection) { - Throw.CustomException(exceptionFactory, parameter); + return collection.Contains(item); } - return parameter; - } - - /// Ensures that the string is non-null and contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeAscii([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - parameter.MustNotBeNull(parameterName, message); - if (!parameter.IsAscii()) + if (items is string @string && item is char character) { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The string"} must contain only ASCII characters."); + return @string.IndexOf(character) != -1; } - return parameter; + return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); } - /// Ensures that the string is non-null and contains only ASCII characters, 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 type to be checked. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustBeAscii([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) - { - if (parameter is null || !parameter.IsAscii()) - { - Throw.CustomException(exceptionFactory, parameter); - } - - return parameter; - } - - /// Ensures that the character span contains only ASCII characters. + [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 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)] - public static Span MustBeAscii(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter).MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the character span contains only ASCII characters, or otherwise throws your custom exception. + [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)] - public static Span MustBeAscii(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter).MustBeAscii(exceptionFactory); - return parameter; - } - - /// Ensures that the read-only character span contains only ASCII characters. + [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); + /// + /// 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)] - public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.IsAscii()) - { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The character span"} must contain only ASCII characters."); - } - - return parameter; - } - - /// Ensures that the read-only character span contains only ASCII characters, or otherwise throws your custom exception. + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements([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)] - public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (!parameter.IsAscii()) - { - Throw.CustomSpanException(exceptionFactory, parameter); - } - - return parameter; - } - - /// Ensures that the character memory contains only ASCII characters. + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements([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 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)] - public static Memory MustBeAscii(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter.Span).MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the character memory contains only ASCII characters, or otherwise throws your custom exception. + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrInheritsFrom([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)] - public static Memory MustBeAscii(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter.Span).MustBeAscii(exceptionFactory); - return parameter; - } - - /// Ensures that the read-only character memory contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - parameter.Span.MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the read-only character memory contains only ASCII characters, or otherwise throws your custom exception. + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrInheritsFrom([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 and point to the same object. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - parameter.Span.MustBeAscii(exceptionFactory); - return parameter; - } - - /// Ensures that the byte span contains only ASCII values. + // 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 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)] - public static Span MustBeAscii(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter).MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the byte span contains only ASCII values, or otherwise throws your custom exception. + [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)] - public static Span MustBeAscii(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter).MustBeAscii(exceptionFactory); - return parameter; - } - - /// Ensures that the read-only byte span contains only ASCII values. + [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; + /// + /// 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 ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.IsAscii()) - { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The byte span"} must contain only ASCII values."); - } - - return parameter; - } - - /// Ensures that the read-only byte span contains only ASCII values, or otherwise throws your custom exception. + 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 ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (!parameter.IsAscii()) - { - Throw.CustomSpanException(exceptionFactory, parameter); - } - - return parameter; - } - - /// Ensures that the byte memory contains only ASCII values. + public static bool IsTrimmed(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace() && !parameter[parameter.Length - 1].IsWhiteSpace(); + /// + /// 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 Memory MustBeAscii(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter.Span).MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the byte memory contains only ASCII values, or otherwise throws your custom exception. + 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 Memory MustBeAscii(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter.Span).MustBeAscii(exceptionFactory); - return parameter; - } - - /// Ensures that the read-only byte memory contains only ASCII values. + public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); + /// + /// 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 ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - parameter.Span.MustBeAscii(parameterName, message); - return parameter; - } - - /// Ensures that the read-only byte memory contains only ASCII values, or otherwise throws your custom exception. + 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 ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - parameter.Span.MustBeAscii(exceptionFactory); - return parameter; - } - + public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace(); /// /// Checks if the specified GUID structurally identifies an RFC/IETF UUID version 7. /// @@ -1367,1120 +937,911 @@ public static bool IsUuidVersion7(this Guid parameter) } /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// 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 string to be checked. + /// 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 the specified character is a white space character. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); + /// + /// 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 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 and are not equal. [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 T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) + if (!EqualityComparer.Default.Equals(parameter, other)) { - Throw.NotTrimmedAtEnd(parameter, parameterName, message); + Throw.ValuesNotEqual(parameter, other, 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. + /// Ensures that is equal to using the default equality comparer, 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 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)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static T MustBe(this T parameter, T other, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) + if (!EqualityComparer.Default.Equals(parameter, other)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Checks if the specified is true and throws an in this case. + /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . /// - /// 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 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)] - public static void InvalidState(bool condition, string? message = null) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (condition) + if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) { - Throw.InvalidState(message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } - } - /// - /// 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; + return parameter; } /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. + /// Ensures that is equal to using the specified equality comparer, 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 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("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> 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 (equalityComparer is null || !equalityComparer.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + } + + return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// Ensures that the two strings are equal using the specified , or otherwise throws a . /// - /// The string to be checked. - /// The other string that must not contain . + /// 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 not equal to . + /// Thrown when is not a valid value from the enum. [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) + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + if (!string.Equals(parameter, other, comparisonType)) { - Throw.Substring(parameter, value, parameterName, message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the two strings are equal using the specified , 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. - /// + /// 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)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string? MustBe(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 || value.Contains(parameter)) + if (!string.Equals(parameter, other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// Ensures that the two strings are equal using the specified , 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 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 not a valid value. + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. [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 string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - value.MustNotBeNull(nameof(value), message); - parameter.MustNotBeNull(parameterName, message); - if (value.IndexOf(parameter, comparisonType) != -1) + if (!parameter.Equals(other, comparisonType)) { - Throw.Substring(parameter, value, comparisonType, parameterName, message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the two strings are equal using the specified , 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) + /// 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) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + if (!parameter.Equals(other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } return parameter; } /// - /// Ensures that the string is longer than the specified length, or otherwise throws a . + /// Ensures that the specified URI is an absolute one, or otherwise throws a . /// - /// The string to be checked. - /// The length that the string must be longer than. + /// The URI 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 not an absolute URI. /// 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) + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length <= length) + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) { - Throw.StringNotLongerThan(parameter, length, parameterName, message); + Throw.MustBeAbsoluteUri(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string is longer than the specified length, or otherwise throws your custom exception. + /// Ensures that the specified URI is an absolute one, 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 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 string MustBeLongerThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (parameter is null || parameter.Length <= length) + if (parameter is null || parameter.IsAbsoluteUri == false) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the span is longer than the specified length, 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 span to be checked. - /// The value that the span must be longer than. + /// 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 is shorter than or equal to . + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter).MustBeLongerThan(length, 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 span is longer than the specified length, 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 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 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 Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static double MustBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter.Length <= length) + if (!parameter.IsApproximately(other)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the span is longer than the specified length, or otherwise throws an . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . /// - /// The span to be checked. - /// The value that the span must be longer than. + /// 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 is shorter than or equal to . + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [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.Length <= length) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the span is longer than the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, 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 . + /// 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 ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + public static double MustBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (parameter.Length <= length) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + 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) + if (!parameter.IsApproximately(other)) { - Throw.InvalidCollectionCount(parameter, count, parameterName, message); + 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) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter, count); + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws an . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// 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. + /// 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 MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [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 is ICollection collection) - { - if (collection.Contains(item)) - { - Throw.ExistingItem(parameter, item, parameterName, message); - } - - return parameter; - } - - if (parameter.MustNotBeNull(parameterName, message).Contains(item)) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.ExistingItem(parameter, item, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } 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 . + /// Ensures that the character is ASCII, or otherwise throws an . [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 char MustBeAscii(this char parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is ICollection collection) + if (!parameter.IsAscii()) { - if (collection.Contains(item)) - { - Throw.CustomException(exceptionFactory, parameter, item); - } - - return parameter; + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The character"} must be ASCII, but it actually is '{parameter}'."); } - if (parameter is null || parameter.Contains(item)) + return parameter; + } + + /// Ensures that the character is ASCII, or otherwise throws your custom exception. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static char MustBeAscii(this char parameter, Func exceptionFactory) + { + if (!parameter.IsAscii()) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.CustomException(exceptionFactory, parameter); } 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. + /// Ensures that the byte is ASCII, or otherwise throws an . [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 byte MustBeAscii(this byte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + if (!parameter.IsAscii()) { - Throw.StringContains(parameter, value, parameterName, message); + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The byte"} must be ASCII, but it actually is {parameter}."); } 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. - /// + /// Ensures that the byte is ASCII, or otherwise throws your custom exception. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static byte MustBeAscii(this byte parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.Contains(value)) + if (!parameter.IsAscii()) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter); } 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. + /// Ensures that the string is non-null and contains only ASCII characters. [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 string MustBeAscii([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) + parameter.MustNotBeNull(parameterName, message); + if (!parameter.IsAscii()) { - Throw.StringContains(parameter, value, comparisonType, parameterName, message); + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The string"} must contain only ASCII characters."); } 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. + /// Ensures that the string is non-null and contains only ASCII characters, or otherwise throws your custom exception. [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) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static string MustBeAscii([NotNull][ValidatedNotNull] this string? parameter, 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 (parameter is null || !parameter.IsAscii()) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } - /// - /// Ensures that the does not contain the specified item, or otherwise throws an . - /// - /// The to be checked. - /// The item that must not be part of the . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// - /// The default instance of cannot contain any items, so this method will not throw for default instances. - /// + /// Ensures that the character span contains only ASCII characters. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeAscii(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsDefault && parameter.Contains(item)) - { - Throw.ExistingItem(parameter, item, parameterName, message); - } + ((ReadOnlySpan)parameter).MustBeAscii(parameterName, message); + return parameter; + } + /// Ensures that the character span contains only ASCII characters, or otherwise throws your custom exception. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeAscii(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter).MustBeAscii(exceptionFactory); return parameter; } - /// - /// Ensures that the does not contain the specified item, or otherwise throws your custom exception. - /// - /// The to be checked. - /// The item that must not be part of the . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . - /// - /// The default instance of cannot contain any items, so this method will not throw for default instances. - /// + /// Ensures that the read-only character span contains only ASCII characters. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) + public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsDefault && parameter.Contains(item)) + if (!parameter.IsAscii()) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The character span"} must contain only ASCII characters."); } 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. - /// Thrown when or is null. + /// Ensures that the read-only character span contains only ASCII characters, or otherwise throws your custom exception. [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) + public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - // ReSharper disable PossibleMultipleEnumeration - parameter.MustBeAbsoluteUri(parameterName, message); - if (schemes is ICollection collection) + if (!parameter.IsAscii()) { - if (!collection.Contains(parameter.Scheme)) - { - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - } - - return parameter; + Throw.CustomSpanException(exceptionFactory, parameter); } - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) - { - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - } + return parameter; + } + /// Ensures that the character memory contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustBeAscii(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter.Span).MustBeAscii(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. + /// Ensures that the character memory contains only ASCII characters, or otherwise throws your custom exception. [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 Memory MustBeAscii(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is null || !parameter.IsAbsoluteUri) - { - Throw.CustomException(exceptionFactory, parameter, schemes); - } + ((ReadOnlySpan)parameter.Span).MustBeAscii(exceptionFactory); + return parameter; + } - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - { - Throw.CustomException(exceptionFactory, parameter, schemes); - } + /// Ensures that the read-only character memory contains only ASCII characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + parameter.Span.MustBeAscii(parameterName, message); + return parameter; + } - return parameter; - } + /// Ensures that the read-only character memory contains only ASCII characters, or otherwise throws your custom exception. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + parameter.Span.MustBeAscii(exceptionFactory); + 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!); - } + /// Ensures that the byte span contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeAscii(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustBeAscii(parameterName, message); + return parameter; + } + /// Ensures that the byte span contains only ASCII values, or otherwise throws your custom exception. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustBeAscii(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter).MustBeAscii(exceptionFactory); 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) + /// Ensures that the read-only byte span contains only ASCII values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + if (!parameter.IsAscii()) { - Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The byte span"} must contain only ASCII values."); } 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. - /// + /// Ensures that the read-only byte span contains only ASCII values, or otherwise throws your custom exception. [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) + public static ReadOnlySpan MustBeAscii(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value)) + if (!parameter.IsAscii()) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomSpanException(exceptionFactory, parameter); } 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. - /// + /// Ensures that the byte memory contains only ASCII values. [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) + public static Memory MustBeAscii(this Memory 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)) - { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); - } - + ((ReadOnlySpan)parameter.Span).MustBeAscii(parameterName, message); return parameter; } - /// - /// 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 . - /// + /// Ensures that the byte memory contains only ASCII values, or otherwise throws your custom exception. [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 . - /// + public static Memory MustBeAscii(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter.Span).MustBeAscii(exceptionFactory); + return parameter; + } + + /// Ensures that the read-only byte memory contains only ASCII values. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, Func exceptionFactory) + public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsLessThanOrApproximately(other)) - { - Throw.CustomException(exceptionFactory, parameter, other); - } + parameter.Span.MustBeAscii(parameterName, message); + return parameter; + } + /// Ensures that the read-only byte memory contains only ASCII values, or otherwise throws your custom exception. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory MustBeAscii(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + parameter.Span.MustBeAscii(exceptionFactory); return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , 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 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 not less than or approximately equal to . - /// + /// Thrown when is no valid email address. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(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 (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) { - Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.InvalidEmailAddress(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, 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 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 . - /// + /// 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)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (!parameter.IsEmailAddress()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter); } 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 - /// . + /// Ensures that the string is a valid email address using the provided regular expression, + /// 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 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 not less than or approximately equal to . - /// + /// Thrown when is no valid email address. + /// Thrown when is null. [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); + [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 specified is less than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. /// - /// 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 . - /// + /// 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 float MustBeLessThanOrApproximately(this float parameter, float other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is a valid file extension, 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 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 less than or approximately equal to . - /// + /// Thrown when is not a valid file extension. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) { - Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.NotFileExtension(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is a valid file extension, 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 . - /// + /// 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)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (parameter is null || !parameter.IsFileExtension()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + 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 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. + /// Ensures that the character span is a valid file extension, or otherwise throws a . /// - /// 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 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 bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); + public static Span MustBeFileExtension(this Span parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); + return parameter; + } + /// - /// 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. + /// Ensures that the character span is a valid file extension, or otherwise throws your custom exception. /// /// 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(); + /// 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; + } + /// - /// Ensures that the collection contains the specified item, or otherwise throws a . + /// Ensures that the character memory is a valid file extension, or otherwise throws a . /// - /// The collection to be checked. - /// The item that must be part of the collection. + /// 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 does not contain . - /// Thrown when is null. + /// The original character memory. + /// Thrown when is not a valid file extension. [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 + public static Memory MustBeFileExtension(this Memory parameter, [CallerArgumentExpression(nameof(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); - } - + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. + /// Ensures that the character memory is a valid file extension, 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 + /// 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 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); - } - + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the string contains the specified substring, or otherwise throws a . + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . /// - /// The string to be checked. - /// The substring that must be part of . + /// 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 does not contain . - /// Thrown when or is null. + /// The original read-only character memory. + /// Thrown when is not a valid file extension. [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) + public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - { - Throw.StringDoesNotContain(parameter, value, parameterName, message); - } - + parameter.Span.MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the string contains the specified value, 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 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) + /// 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) { - // 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!); - } - + parameter.Span.MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws a . + /// Ensures that the read-only character span is a valid file extension, 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 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 does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// The original read-only character span. + /// Thrown when is not a valid file extension. [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 ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) + if (!parameter.IsFileExtension()) { - Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + Throw.NotFileExtension(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string contains the specified value, 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 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) + /// 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) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + if (!parameter.IsFileExtension()) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomSpanException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the immutable array contains the specified item, or otherwise throws a . + /// Ensures that the specified single-precision floating-point value is finite, or otherwise throws an . /// - /// The immutable array to be checked. - /// The item that must be part of the immutable array. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBeFinite(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.Contains(item)) + if (!parameter.IsFinite()) { - Throw.MissingItem(parameter, item, parameterName, message); + Throw.NotFinite(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the immutable array contains the specified item, or otherwise throws your custom exception. + /// Ensures that the specified single-precision floating-point value is finite, or otherwise throws your custom exception. /// - /// The immutable array to be checked. - /// The item that must be part of the immutable array. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustBeFinite(this float parameter, Func exceptionFactory) { - if (!parameter.Contains(item)) + if (!parameter.IsFinite()) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws a . + /// Ensures that the specified double-precision floating-point value is finite, or otherwise throws an . /// - /// 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 + public static double MustBeFinite(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (ReferenceEquals(parameter, other)) + if (!parameter.IsFinite()) { - Throw.SameObjectReference(parameter, parameterName, message); + Throw.NotFinite(parameter, 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 double-precision floating-point value is finite, 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 + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustBeFinite(this double parameter, Func exceptionFactory) { - if (ReferenceEquals(parameter, other)) + if (!parameter.IsFinite()) { Throw.CustomException(exceptionFactory, parameter); } @@ -2489,354 +1850,385 @@ public static ImmutableArray MustContain(this ImmutableArray 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's length is within the specified range, or otherwise throws a . + /// Ensures that the specified is greater than the given value, or otherwise throws an . /// - /// The string to be checked. - /// The range where the string's length must be in-between. + /// 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 length of is not with the specified . + /// 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 string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, [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 (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) { - Throw.StringLengthNotInRange(parameter, range, parameterName, message); + Throw.MustBeGreaterThan(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. + /// Ensures that the specified is greater than the given value, 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 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")] - public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) + [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 { - if (parameter is null || !range.IsValueWithinRange(parameter.Length)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) { - Throw.CustomException(exceptionFactory, parameter, range); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the 's length is within the specified range, 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 to be checked. - /// The range where the 's length must be in-between. + /// 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 length of is not within the specified . + /// + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + 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) { - var length = parameter.IsDefault ? 0 : parameter.Length; - if (!range.IsValueWithinRange(length)) + if (!parameter.IsGreaterThanOrApproximately(other)) { - Throw.ImmutableArrayLengthNotInRange(parameter, range, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the 's length is within the specified range, or otherwise throws your custom exception. + /// Ensures that the specified is greater than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The to be checked. - /// The range where the 's length must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the length of is not within the specified range. + /// 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)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, Func, Range, Exception> exceptionFactory) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - var length = parameter.IsDefault ? 0 : parameter.Length; - if (!range.IsValueWithinRange(length)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter, range); + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the span length is within the specified range, or otherwise throws an . + /// 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 Span MustHaveLengthIn(this Span parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - ((ReadOnlySpan)parameter).MustHaveLengthIn(range, parameterName, message); + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } + return parameter; } /// - /// Ensures that the span length is within the specified range, 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.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 Span MustHaveLengthIn(this Span parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) - { - ((ReadOnlySpan)parameter).MustHaveLengthIn(range, exceptionFactory); - return parameter; - } - + 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 read-only span length is within the specified range, 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 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 ReadOnlySpan MustHaveLengthIn(this ReadOnlySpan parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, Func exceptionFactory) { - if (!range.IsValueWithinRange(parameter.Length)) + if (!parameter.IsGreaterThanOrApproximately(other)) { - Throw.SpanLengthNotInRange(parameter, range, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the read-only span length is within the specified range, or otherwise throws your custom exception. + /// 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 ReadOnlySpan MustHaveLengthIn(this ReadOnlySpan parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!range.IsValueWithinRange(parameter.Length)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { - Throw.CustomSpanException(exceptionFactory, parameter, range); + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the memory length is within the specified range, or otherwise throws an . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory MustHaveLengthIn(this Memory parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter.Span).MustHaveLengthIn(range, parameterName, message); - return parameter; - } - - /// - /// Ensures that the memory length is within the specified range, or otherwise throws your custom exception. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory MustHaveLengthIn(this Memory parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) - { - ((ReadOnlySpan)parameter.Span).MustHaveLengthIn(range, exceptionFactory); - return parameter; - } - - /// - /// Ensures that the read-only memory length is within the specified range, or otherwise throws an . + /// 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 ReadOnlyMemory MustHaveLengthIn(this ReadOnlyMemory parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - parameter.Span.MustHaveLengthIn(range, parameterName, message); - return parameter; - } + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } - /// - /// Ensures that the read-only memory length is within the specified range, or otherwise throws your custom exception. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory MustHaveLengthIn(this ReadOnlyMemory parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) - { - parameter.Span.MustHaveLengthIn(range, exceptionFactory); 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 is not equal to using the default equality comparer, or otherwise throws a . + /// 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 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 equal. + /// Thrown when the specified is less than . + /// 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 T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (EqualityComparer.Default.Equals(parameter, other)) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that is not equal to using the default 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 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 and are equal. + /// Your custom exception thrown when the specified is less than , 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; exceptionFactory:null => halt")] + public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - if (EqualityComparer.Default.Equals(parameter, other)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . + /// Ensures that the specified URI has the "http" or "https" scheme, 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 URI to be checked. /// 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 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("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 Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.UriMustHaveOneSchemeOf(parameter, ["https", "http"], parameterName, message); } return parameter; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that the specified URI has the "http" or "https" scheme, 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 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("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 Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || equalityComparer.Equals(parameter, other)) + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) { - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// 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 equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when uses a different scheme than "http". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. [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; - } - + [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 two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that the specified URI has the "http" scheme, 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. + /// 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)] - 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; - } - + [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 two strings are not equal using the specified , or otherwise throws a . + /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// 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 equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when uses a different scheme than "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [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 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 (parameter.Equals(other, comparisonType)) + if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.MustBeInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that is within the specified range, 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. + /// 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)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + [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 { - if (parameter.Equals(other, comparisonType)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || !range.IsValueWithinRange(parameter)) { - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + Throw.CustomException(exceptionFactory, parameter!, range); } return parameter; @@ -2886,232 +2278,215 @@ public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, } /// - /// Ensures that the string is not null and trimmed, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// 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 string to be checked. + /// 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 trimmed, i.e. they start or end with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// 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 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; - } - + 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 string is not null and trimmed, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. + /// 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 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. + /// 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")] - public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static double MustBeLessThanOrApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmed()) + if (!parameter.IsLessThanOrApproximately(other)) { - Throw.CustomException(exceptionFactory, parameter); + 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 or approximately equal to the given + /// value, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must contain . + /// 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 does not contain . - /// Thrown when or is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [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) + public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.NotSubstring(parameter, value, parameterName, message); + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, 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 or approximately equal to 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. + /// 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 does not contain , - /// or when is null, - /// or when is null. + /// Your custom exception thrown when is not less than or approximately equal to . /// [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) + 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 || value is null || !value.Contains(parameter)) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } 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 or approximately equal to the given + /// value, using the default tolerance of 0.0001f, 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 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 does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// + /// Thrown when is not less than or approximately equal to . + /// [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; - } - + 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 string is a substring of the specified other string, 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 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. + /// 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 . /// - /// 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) + 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 || value is null || value.IndexOf(parameter, comparisonType) == -1) + if (!parameter.IsLessThanOrApproximately(other)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the collection count is within the specified range, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The collection to be checked. - /// The range in which the collection count must lie. + /// 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 original collection. - /// Thrown when the collection count is not within . - /// 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 TCollection MustHaveCountIn([NotNull][ValidatedNotNull] this TCollection? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var actualCount = parameter.Count(parameterName, message); - if (!range.IsValueWithinRange(actualCount)) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.CollectionCountNotInRange(parameter, actualCount, range, parameterName, message); + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the collection count is within the specified range, 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 collection to be checked. - /// The range in which the collection count must lie. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// The original collection. - /// Your custom exception thrown when the collection is null or its count is not within . + /// 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 TCollection MustHaveCountIn([NotNull][ValidatedNotNull] this TCollection? parameter, Range range, Func, Exception> exceptionFactory) - where TCollection : class, IEnumerable + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (parameter is null || !range.IsValueWithinRange(parameter.Count())) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter, range); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } 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 specified is greater than the given value, or otherwise throws an . + /// 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 less than . + /// 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 less than or equal to . + /// Thrown when the specified is greater than . /// 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) + 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) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) { - Throw.MustBeGreaterThan(parameter, other, parameterName, message); + Throw.MustBeLessThanOrEqualTo(parameter, other, 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 not 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 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 less than or equal to , or when is null. + /// 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 MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + 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) + if (parameter is null || parameter.CompareTo(other) > 0) { Throw.CustomException(exceptionFactory, parameter!, other); } @@ -3120,52 +2495,34 @@ public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T paramete } /// - /// 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 specified uses , or otherwise throws an . /// - /// The type of the enum. - /// The value to be checked. + /// 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 enum value. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : struct, Enum + public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!EnumInfo.IsValidEnumValue(parameter)) + if (parameter.Kind != DateTimeKind.Local) { - Throw.EnumValueNotDefined(parameter, parameterName, message); + Throw.MustBeLocalDateTime(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 . + /// Ensures that the specified uses , 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. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// 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 T MustBeValidEnumValue(this T parameter, Func exceptionFactory) - where T : struct, Enum + public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) { - if (!EnumInfo.IsValidEnumValue(parameter)) + if (parameter.Kind != DateTimeKind.Local) { Throw.CustomException(exceptionFactory, parameter); } @@ -3174,224 +2531,262 @@ public static T MustBeValidEnumValue(this T parameter, Func exc } /// - /// 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 GUID is not empty, or otherwise throws an . + /// Ensures that the string is longer than the specified length, or otherwise throws a . /// - /// The GUID to be checked. + /// 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 is an empty GUID. + /// Thrown when has a length shorter than or equal to . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid MustNotBeEmpty(this Guid parameter, [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 == Guid.Empty) + if (parameter.MustNotBeNull(parameterName, message).Length <= length) { - Throw.EmptyGuid(parameterName, message); + Throw.StringNotLongerThan(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. + /// Ensures that the string is longer than the specified length, 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. + /// 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("exceptionFactory:null => halt")] - public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (parameter == Guid.Empty) + if (parameter is null || parameter.Length <= length) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified span is not empty, or otherwise throws an . + /// 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 MustNotBeEmpty(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeLongerThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - ((ReadOnlySpan)parameter).MustNotBeEmpty(parameterName, message); + ((ReadOnlySpan)parameter).MustBeLongerThan(length, parameterName, message); return parameter; } /// - /// Ensures that the specified span is not empty, or otherwise throws your custom exception. + /// 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 MustNotBeEmpty(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + public static Span MustBeLongerThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - ((ReadOnlySpan)parameter).MustNotBeEmpty(exceptionFactory); + if (parameter.Length <= length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the specified read-only span is not empty, or otherwise throws an . + /// 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 MustNotBeEmpty(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsEmpty) + if (parameter.Length <= length) { - Throw.EmptyCollection(parameterName, message); + Throw.SpanMustBeLongerThan(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified read-only span is not empty, or otherwise throws your custom exception. + /// 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 MustNotBeEmpty(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) + public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.IsEmpty) + if (parameter.Length <= length) { - Throw.CustomSpanException(exceptionFactory, parameter); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified memory is not empty, or otherwise throws an . + /// 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)] - public static Memory MustNotBeEmpty(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [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) { - ((ReadOnlySpan)parameter.Span).MustNotBeEmpty(parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).Length < length) + { + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified memory is not empty, or otherwise throws your custom exception. + /// 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)] - public static Memory MustNotBeEmpty(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - ((ReadOnlySpan)parameter.Span).MustNotBeEmpty(exceptionFactory); + if (parameter is null || parameter.Length < length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + return parameter; } /// - /// Ensures that the specified read-only memory is not empty, or otherwise throws an . + /// 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 ReadOnlyMemory MustNotBeEmpty(this ReadOnlyMemory 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) { - parameter.Span.MustNotBeEmpty(parameterName, message); + ((ReadOnlySpan)parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); return parameter; } /// - /// Ensures that the specified read-only memory is not 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 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 ReadOnlyMemory MustNotBeEmpty(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - parameter.Span.MustNotBeEmpty(exceptionFactory); + if (parameter.Length < length) + { + Throw.CustomSpanException(exceptionFactory, parameter, length); + } + return parameter; } - /// Checks if the character is an ASCII code point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this char parameter) => parameter <= 0x7F; - /// Checks if the byte is an ASCII value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this byte parameter) => parameter <= 0x7F; - /// Checks if the string is non-null and contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this string? parameter) => parameter is not null && parameter.AsSpan().IsAscii(); - /// Checks if the character span contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this Span parameter) => ((ReadOnlySpan)parameter).IsAscii(); - /// Checks if the read-only character span contains only ASCII characters. + /// + /// 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 bool IsAscii(this ReadOnlySpan parameter) + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - foreach (var value in parameter) + if (parameter.Length < length) { - if (value > 0x7F) - { - return false; - } + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); } - return true; + return parameter; } - /// Checks if the character memory contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this Memory parameter) => parameter.Span.IsAscii(); - /// Checks if the read-only character memory contains only ASCII characters. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this ReadOnlyMemory parameter) => parameter.Span.IsAscii(); - /// Checks if the byte span contains only ASCII values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this Span parameter) => ((ReadOnlySpan)parameter).IsAscii(); - /// Checks if the read-only byte span contains only ASCII values. + /// + /// 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 bool IsAscii(this ReadOnlySpan parameter) + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - foreach (var value in parameter) + if (parameter.Length < length) { - if (value > 0x7F) - { - return false; - } + Throw.CustomSpanException(exceptionFactory, parameter, length); } - return true; + return parameter; } - /// Checks if the byte memory contains only ASCII values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this Memory parameter) => parameter.Span.IsAscii(); - /// Checks if the read-only byte memory contains only ASCII values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAscii(this ReadOnlyMemory parameter) => parameter.Span.IsAscii(); /// - /// 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 specified is negative (less than zero), or otherwise + /// throws an . /// - /// The string to be checked. + /// 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 not equal to "\n" or "\r\n". - /// Thrown when is null. + /// + /// Thrown when is zero or positive. + /// [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 int MustBeNegative(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) + if (!(parameter < 0)) { - Throw.NotNewLine(parameter, parameterName, message); + Throw.MustBeNegative(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 . + /// Ensures that the specified is negative (less than zero), 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 value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustBeNegative(this int parameter, Func exceptionFactory) { - if (!parameter.IsNewLine()) + if (!(parameter < 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -3400,114 +2795,130 @@ public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? para } /// - /// Ensures that the string starts with the specified value, or otherwise throws a . + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . /// - /// 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 value to be checked. /// 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. + /// + /// Thrown when is zero or positive. + /// [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) + public static long MustBeNegative(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + if (!(parameter < 0L)) { - Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + Throw.MustBeNegative(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified is negative (less than zero), 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. + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. + /// Your custom exception thrown when is zero or positive. /// [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) + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustBeNegative(this long 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 < 0L)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . /// - /// 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. + /// 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 zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustBeNegative(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter < 0m)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. + /// Your custom exception thrown when is zero or positive. /// - /// 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) + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustBeNegative(this decimal parameter, 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)) + if (!(parameter < 0m)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// 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 is negative (less than zero), or otherwise + /// throws an . /// - /// The string to be checked. + /// 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 not trimmed at the start, i.e. they start with white space characters. - /// Empty strings are regarded as trimmed. + /// + /// Thrown when is zero (including negative zero), positive, or NaN. /// - /// 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) + public static float MustBeNegative(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) + if (!(parameter < 0f)) { - Throw.NotTrimmedAtStart(parameter, parameterName, message); + Throw.MustBeNegative(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. + /// Ensures that the specified is negative (less than zero), 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 start. Empty strings are regarded as trimmed. + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), positive, or NaN. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustBeNegative(this float parameter, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) + if (!(parameter < 0f)) { Throw.CustomException(exceptionFactory, parameter); } @@ -3516,1380 +2927,1142 @@ public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this strin } /// - /// Ensures that the value is one of the specified items, or otherwise throws a . + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . /// /// 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. + /// + /// Thrown when is zero (including negative zero), positive, or NaN. + /// [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) + public static double MustBeNegative(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + if (!(parameter < 0d)) { - Throw.ValueNotOneOf(parameter, items, parameterName, message); + Throw.MustBeNegative(parameter, 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 negative (less than zero), 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 delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), positive, or NaN. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustBeNegative(this double parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || !parameter.IsOneOf(items)) + if (!(parameter < 0d)) { - Throw.CustomException(exceptionFactory, parameter, items!); + Throw.CustomException(exceptionFactory, parameter); } 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. - /// - /// 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); - /// - /// 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; - /// - /// 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 specified is approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the specified is negative (less than zero), 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. + /// Thrown when is or positive. /// [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); + public static TimeSpan MustBeNegative(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter < TimeSpan.Zero)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Ensures that the specified is approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. /// /// 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. + /// The delegate that creates your custom exception. is passed to this delegate. /// - /// - /// Thrown when the absolute difference between and is not - /// less than 0.0001. + /// + /// Your custom exception thrown when is or positive. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustBeNegative(this TimeSpan parameter, Func exceptionFactory) { - if (!parameter.IsApproximately(other)) + if (!(parameter < TimeSpan.Zero)) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . /// - /// 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 string to be checked. /// 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 . - /// + /// Thrown when is not equal to "\n" or "\r\n". + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [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.IsApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) { - Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.NotNewLine(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given - /// value, 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 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 . - /// + /// 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)] - public static double MustBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsApproximately(other, tolerance)) + if (!parameter.IsNewLine()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given - /// value, using the default tolerance of 0.0001f, or otherwise throws an - /// . + /// Ensures that can be cast to and returns the cast value, or otherwise throws a . /// - /// The value to be checked. - /// The value that should be approximately equal to. + /// The value to be cast. /// 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. + /// Thrown when cannot be cast to . + /// Thrown when is null. [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); + [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 the specified is approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// 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; + } + + /// + /// Ensures that the value is one of the specified items, or otherwise throws a . /// /// 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. - /// + /// 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)] - public static float MustBeApproximately(this float parameter, float other, Func exceptionFactory) + [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) { - if (!parameter.IsApproximately(other)) + // ReSharper disable PossibleMultipleEnumeration + if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.ValueNotOneOf(parameter, items, parameterName, message); } return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the specified is approximately equal to the given - /// value, or otherwise throws an . + /// 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 specified is positive (greater than zero), 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 . + /// Thrown when is zero or negative. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static int MustBePositive(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsApproximately(other, tolerance)) + if (!(parameter > 0)) { - Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.MustBePositive(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the specified is positive (greater than zero), 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 delegate that creates your custom exception. is passed to this delegate. /// /// - /// Your custom exception thrown when the absolute difference between and - /// is not less than . + /// Your custom exception thrown when is zero or negative. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustBePositive(this int parameter, Func exceptionFactory) { - if (!parameter.IsApproximately(other, tolerance)) + if (!(parameter > 0)) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . /// - /// The URI to be checked. + /// The value 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); - /// - /// 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) => ((ReadOnlySpan)span).IsEmptyOrWhiteSpace(); - /// - /// Checks if the specified span is empty or contains only white space characters. - /// - /// The span to be checked. + /// + /// Thrown when is zero or negative. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) + public static long MustBePositive(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (span.IsEmpty) + if (!(parameter > 0L)) { - return true; - } - - foreach (var character in span) - { - if (!character.IsWhiteSpace()) - { - return false; - } + Throw.MustBePositive(parameter, parameterName, message); } - return true; + return parameter; } /// - /// 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. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. /// - /// The memory to be checked. + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or negative. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustBePositive(this long parameter, Func exceptionFactory) + { + if (!(parameter > 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws an . + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . /// - /// The collection to be checked. - /// The number of items the collection should have at most. + /// The value to be checked. /// 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. + /// + /// Thrown when is zero or negative. + /// [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 decimal MustBePositive(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) > count) + if (!(parameter > 0m)) { - Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); + Throw.MustBePositive(parameter, 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 specified is positive (greater than zero), 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 value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or negative. + /// [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 + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustBePositive(this decimal parameter, Func exceptionFactory) { - if (parameter is null || parameter.Count() > count) + if (!(parameter > 0m)) { - Throw.CustomException(exceptionFactory, parameter, count); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the has at least the specified length, or otherwise throws an . + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . /// - /// The to be checked. - /// The minimum length the should have. + /// 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 less than the specified length. - /// The default instance of will be treated as having length 0. + /// + /// Thrown when is zero (including negative zero), negative, or NaN. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBePositive(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength < length) + if (!(parameter > 0f)) { - Throw.InvalidMinimumImmutableArrayLength(parameter, length, parameterName, message); + Throw.MustBePositive(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the has at least the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. /// - /// The to be checked. - /// The minimum length the should have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when has less than the specified length. - /// The default instance of will be treated as having length 0. + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), negative, or NaN. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustBePositive(this float parameter, Func exceptionFactory) { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength < length) + if (!(parameter > 0f)) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Checks if the specified single-precision floating-point value is finite. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFinite(this float parameter) => parameter >= float.MinValue && parameter <= float.MaxValue; - /// - /// Checks if the specified double-precision floating-point value is finite. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFinite(this double parameter) => parameter >= double.MinValue && parameter <= double.MaxValue; - /// - /// Ensures that the character span is neither empty nor all white space. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . /// + /// 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 zero (including negative zero), negative, or NaN. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustNotBeEmptyOrWhiteSpace(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustBePositive(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - ((ReadOnlySpan)parameter).MustNotBeEmptyOrWhiteSpace(parameterName, message); + if (!(parameter > 0d)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the character span is neither empty nor all white space, or otherwise throws your custom exception. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustNotBeEmptyOrWhiteSpace(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustBePositive(this double parameter, Func exceptionFactory) { - ((ReadOnlySpan)parameter).MustNotBeEmptyOrWhiteSpace(exceptionFactory); + if (!(parameter > 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the read-only character span is neither empty nor all white space. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . /// - /// Thrown when is empty. - /// Thrown when contains only white space. + /// 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 or negative. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotBeEmptyOrWhiteSpace(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static TimeSpan MustBePositive(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsEmpty) - { - Throw.EmptyString(parameterName, message); - } - - if (parameter.IsEmptyOrWhiteSpace()) + if (!(parameter > TimeSpan.Zero)) { - Throw.WhiteSpaceSpan(parameter, parameterName, message); + Throw.MustBePositive(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the read-only character span is neither empty nor all white space, or otherwise throws your custom exception. + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is or negative. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotBeEmptyOrWhiteSpace(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustBePositive(this TimeSpan parameter, Func exceptionFactory) { - if (parameter.IsEmptyOrWhiteSpace()) + if (!(parameter > TimeSpan.Zero)) { - Throw.CustomSpanException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the character memory is neither empty nor all white space. + /// 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)] - public static Memory MustNotBeEmptyOrWhiteSpace(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - ((ReadOnlySpan)parameter.Span).MustNotBeEmptyOrWhiteSpace(parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) + { + Throw.MustBeRelativeUri(parameter, parameterName, message); + } + return parameter; } /// - /// Ensures that the character memory is neither empty nor all white space, or otherwise throws your custom exception. + /// 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)] - public static Memory MustNotBeEmptyOrWhiteSpace(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - ((ReadOnlySpan)parameter.Span).MustNotBeEmptyOrWhiteSpace(exceptionFactory); + if (parameter is null || parameter.IsAbsoluteUri) + { + Throw.CustomException(exceptionFactory, parameter); + } + return parameter; } /// - /// Ensures that the read-only character memory is neither empty nor all white space. + /// 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)] - public static ReadOnlyMemory MustNotBeEmptyOrWhiteSpace(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [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) { - parameter.Span.MustNotBeEmptyOrWhiteSpace(parameterName, message); + if (parameter.MustNotBeNull(parameterName, message).Length >= length) + { + Throw.StringNotShorterThan(parameter, length, parameterName, message); + } + return parameter; } /// - /// Ensures that the read-only character memory is neither empty nor all white space, 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 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)] - public static ReadOnlyMemory MustNotBeEmptyOrWhiteSpace(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - parameter.Span.MustNotBeEmptyOrWhiteSpace(exceptionFactory); + if (parameter is null || parameter.Length >= length) + { + Throw.CustomException(exceptionFactory, parameter, length); + } + 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 structurally identifies an RFC/IETF UUID version 7, or otherwise throws an . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The GUID 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). - /// The original GUID. - /// Thrown when the UUID version is not 7 or the variant is not RFC/IETF. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid MustBeUuidVersion7(this Guid parameter, [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 (!parameter.IsUuidVersion7()) - { - Throw.Argument(parameterName, message ?? $"{parameterName ?? "The GUID"} must be an RFC/IETF UUID version 7, but it actually is \"{parameter}\"."); - } - + ((ReadOnlySpan)parameter).MustBeShorterThan(length, parameterName, message); return parameter; } /// - /// Ensures that the specified GUID structurally identifies an RFC/IETF UUID version 7, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The GUID to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original GUID. - /// Your custom exception thrown when the UUID version is not 7 or the variant is not RFC/IETF. + /// 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("exceptionFactory:null => halt")] - public static Guid MustBeUuidVersion7(this Guid parameter, Func exceptionFactory) + public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (!parameter.IsUuidVersion7()) + if (parameter.Length >= length) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified object reference is not null, or otherwise throws an . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The object reference 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 null. + /// Thrown when is longer than or equal to . [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 ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null) + if (parameter.Length >= length) { - Throw.ArgumentNull(parameterName, message); + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, 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. + /// 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; exceptionFactory:null => halt")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : class + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is null) + if (parameter.Length >= length) { - Throw.CustomException(exceptionFactory); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws an . + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . /// - /// The URI to be checked. - /// The scheme that the URI should have. + /// 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 uses a different scheme than the specified one. - /// Thrown when is relative and thus has no scheme. + /// Thrown when has a length greater than . /// 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) + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) + if (parameter.MustNotBeNull(parameterName, message).Length > length) { - Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Ensures that the string is shorter than or equal to 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 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 Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) + if (parameter is null || parameter.Length > length) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// - /// 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. + /// 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)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) - { - Throw.CustomException(exceptionFactory, parameter, scheme); - } - + ((ReadOnlySpan)parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); return parameter; } /// - /// 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 span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// 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. + /// 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 T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory 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) + if (parameter.Length > length) { - Throw.ArgumentNull(parameterName, message); + Throw.CustomSpanException(exceptionFactory, parameter, length); } 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 span is shorter than or equal to the specified length, or otherwise throws an . /// - /// 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 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)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [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) + if (parameter.Length > length) { - Throw.CustomException(exceptionFactory); + Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// 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 . + /// 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 DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.Kind != DateTimeKind.Unspecified) + if (parameter.Length > length) { - Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// - /// 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 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("exceptionFactory:null => halt")] - public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + [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 (parameter.Kind != DateTimeKind.Unspecified) + if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) { - Throw.CustomException(exceptionFactory, parameter); + Throw.NotSubstring(parameter, value, parameterName, message); } return parameter; } /// - /// 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) => 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. + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. /// - /// - /// 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. + /// 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)] - public static bool IsFileExtension(this ReadOnlySpan value) + [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 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++) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !value.Contains(parameter)) { - var character = value[i]; - if (character.IsLetterOrDigit()) - { - hasAlphanumeric = true; - } - else if (character != '.') - { - return false; - } + Throw.CustomException(exceptionFactory, parameter, value!); } - return hasAlphanumeric; + 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 is not approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// - /// The value to be checked. - /// The value that should not be approximately equal to. + /// 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 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. - /// + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBeApproximately(this double parameter, double other, Func exceptionFactory) + [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 (parameter.IsApproximately(other)) + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) == -1) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is a substring of the specified other string, 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 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 . + /// 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)] - public static double MustNotBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [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) { - if (parameter.IsApproximately(other, tolerance)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) { - Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// - /// 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 . + /// 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)] - public static double MustNotBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [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.IsApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.NotTrimmed(parameter, parameterName, message); } 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 - /// . + /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// - /// 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. - /// + /// 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 float MustNotBeApproximately(this float parameter, float other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.IsApproximately(other)) + if (parameter is null || !parameter.AsSpan().IsTrimmed()) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// - /// 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 string to be checked. /// 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 . + /// + /// 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)] - public static float MustNotBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) { - Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.NotTrimmedAtEnd(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, 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 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 . - /// + /// 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. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.IsApproximately(other, tolerance)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Checks if the specified strings are equal, using the given comparison rules. + /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// - /// 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 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)] - public static bool Equals(this string? @string, string? value, StringComparisonType comparisonType) + [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 ((int)comparisonType < 6) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) { - return string.Equals(@string, value, (StringComparison)comparisonType); + Throw.NotTrimmedAtStart(parameter, parameterName, message); } - switch (comparisonType) - { - case StringComparisonType.OrdinalIgnoreWhiteSpace: - return @string.EqualsOrdinalIgnoreWhiteSpace(value); - case StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace: - return @string.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(value); - default: - Throw.EnumValueNotDefined(comparisonType, nameof(comparisonType)); - return false; - } + return parameter; } /// - /// Checks if the value is not within the specified range. + /// 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 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 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)] - public static bool IsNotIn([NotNull][ValidatedNotNull] this T parameter, Range range) - where T : IComparable => !range.IsValueWithinRange(parameter); + [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; + } + /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. + /// 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 and are not equal. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!EqualityComparer.Default.Equals(parameter, other)) + if (parameter.Kind != DateTimeKind.Unspecified) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); } return parameter; } /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. + /// Ensures that the specified uses , 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 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)] - public static T MustBe(this T parameter, T other, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) { - if (!EqualityComparer.Default.Equals(parameter, other)) + if (parameter.Kind != DateTimeKind.Unspecified) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . + /// Ensures that the specified uses , 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 date time to be checked. /// 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 does not use . [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) + public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + if (parameter.Kind != DateTimeKind.Utc) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.MustBeUtcDateTime(parameter, parameterName, message); } return parameter; } /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that the specified uses , 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 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("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> 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 (equalityComparer is null || !equalityComparer.Equals(parameter, other)) + if (parameter.Kind != DateTimeKind.Utc) { - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the specified has a zero offset, or otherwise throws an . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The date time offset 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 date time offset without conversion. + /// Thrown when does not have as its offset. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTimeOffset MustBeUtc(this DateTimeOffset parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!string.Equals(parameter, other, comparisonType)) + if (parameter.Offset != TimeSpan.Zero) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.MustBeUtcDateTimeOffset(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the specified has a zero offset, 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. + /// The date time offset to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original date time offset without conversion. + /// Your custom exception thrown when does not have as its offset. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTimeOffset MustBeUtc(this DateTimeOffset parameter, Func exceptionFactory) { - if (!string.Equals(parameter, other, comparisonType)) + if (parameter.Offset != TimeSpan.Zero) { - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the specified GUID structurally identifies an RFC/IETF UUID version 7, or otherwise throws an . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// 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 not equal to . - /// Thrown when is not a valid value from the enum. + /// The original GUID. + /// Thrown when the UUID version is not 7 or the variant is not RFC/IETF. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Guid MustBeUuidVersion7(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.Equals(other, comparisonType)) + if (!parameter.IsUuidVersion7()) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.Argument(parameterName, message ?? $"{parameterName ?? "The GUID"} must be an RFC/IETF UUID version 7, but it actually is \"{parameter}\"."); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the specified GUID structurally identifies an RFC/IETF UUID version 7, 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 GUID to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original GUID. + /// Your custom exception thrown when the UUID version is not 7 or the variant is not RFC/IETF. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static Guid MustBeUuidVersion7(this Guid parameter, Func exceptionFactory) { - if (!parameter.Equals(other, comparisonType)) + if (!parameter.IsUuidVersion7()) { - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// 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 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; - /// - /// 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; - /// - /// 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 string is a valid file extension, or otherwise throws an . + /// 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 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 not a valid file extension. - /// Thrown when is null. + /// Thrown when is no valid enum value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : struct, Enum { - if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) + if (!EnumInfo.IsValidEnumValue(parameter)) { - Throw.NotFileExtension(parameter, parameterName, message); + Throw.EnumValueNotDefined(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string is a valid file extension, 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 delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not a valid file extension. + /// 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")] - public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) + where T : struct, Enum { - if (parameter is null || !parameter.IsFileExtension()) + if (!EnumInfo.IsValidEnumValue(parameter)) { Throw.CustomException(exceptionFactory, parameter); } @@ -4898,298 +4071,386 @@ public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string } /// - /// Ensures that the character span is a valid file extension, or otherwise throws a . + /// Ensures that the collection contains the specified item, or otherwise throws a . /// - /// The character span to be checked. + /// 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). - /// The original character span. - /// Thrown when is not a valid file extension. + /// Thrown when does not contain . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeFileExtension(this Span parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [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 { - ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); - return parameter; - } + 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); + } - /// - /// 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; } /// - /// Ensures that the character memory is a valid file extension, or otherwise throws a . + /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. /// - /// 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. + /// 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)] - public static Memory MustBeFileExtension(this Memory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); - return parameter; - } + 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); + } - /// - /// 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; } /// - /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . + /// Ensures that the string contains the specified substring, or otherwise throws a . /// - /// The read-only character memory to be checked. + /// 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). - /// The original read-only character memory. - /// Thrown when is not a valid file extension. + /// Thrown when does not contain . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [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) { - parameter.Span.MustBeFileExtension(parameterName, message); + if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + { + Throw.StringDoesNotContain(parameter, value, parameterName, message); + } + return parameter; } /// - /// Ensures that the read-only character memory is a valid file extension, or otherwise throws your custom exception. + /// Ensures that the string contains the specified value, 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) + /// 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) { - parameter.Span.MustBeFileExtension(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 read-only character span is a valid file extension, or otherwise throws a . + /// Ensures that the string contains the specified value, or otherwise throws a . /// - /// The read-only character span to be checked. + /// 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). - /// The original read-only character span. - /// Thrown when is not a valid file extension. + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [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.IsFileExtension()) + if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) { - Throw.NotFileExtension(parameter, parameterName, message); + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the read-only character span is a valid file extension, or otherwise throws your custom exception. + /// Ensures that the string contains the specified value, 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) + /// 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) { - if (!parameter.IsFileExtension()) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) { - Throw.CustomSpanException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } return parameter; } /// - /// Checks if the given is one of the specified . + /// Ensures that the immutable array contains the specified item, or otherwise throws a . /// - /// The item to be checked. - /// The collection that might contain the . - /// Thrown when is null. + /// The immutable array to be checked. + /// The item that must be part of the immutable array. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . [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 ImmutableArray MustContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (items is ICollection collection) - { - return collection.Contains(item); - } - - if (items is string @string && item is char character) + if (!parameter.Contains(item)) { - return @string.IndexOf(character) != -1; + Throw.MissingItem(parameter, item, parameterName, message); } - return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Ensures that the immutable array 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 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 immutable array to be checked. + /// The item that must be part of the immutable array. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain . [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 ImmutableArray MustContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + if (!parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the string ends with the specified value, 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. - [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 + /// 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) { - // 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.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . + /// Ensures that the string ends with the specified value, 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. + /// 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)] - 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; 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.HasValue) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) { - Throw.NullableHasNoValue(parameterName, message); + Throw.CustomException(exceptionFactory, parameter, value!); } - return parameter.Value; + return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The nullable to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no value. + /// 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("exceptionFactory:null => halt")] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : struct + [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.HasValue) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } - return parameter.Value; + return parameter; } /// - /// Ensures that the specified is greater 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 less 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 less than or equal to . + /// 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 MustNotBeLessThanOrEqualTo([NotNull][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) + if (parameter!.Count(parameterName, message) != count) { - Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + Throw.InvalidCollectionCount(parameter, count, parameterName, message); } return parameter; } /// - /// Ensures that the specified is greater 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 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 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 MustNotBeLessThanOrEqualTo([NotNull][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 ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) + if (parameter is null || parameter.Count() != count) { - Throw.CustomException(exceptionFactory, parameter!, other); + Throw.CustomException(exceptionFactory, parameter, count); } return parameter; } /// - /// Ensures that the string has the specified length, or otherwise throws a . + /// Ensures that the collection count is within the specified range, or otherwise throws an . /// - /// The string to be checked. - /// The asserted length of the string. + /// The collection to be checked. + /// The range in which the collection count must lie. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length other than . + /// The original collection. + /// Thrown when the collection count is not within . /// 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 TCollection MustHaveCountIn([NotNull][ValidatedNotNull] this TCollection? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter.MustNotBeNull(parameterName, message).Length != length) + var actualCount = parameter.Count(parameterName, message); + if (!range.IsValueWithinRange(actualCount)) { - Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + Throw.CollectionCountNotInRange(parameter, actualCount, range, parameterName, message); } return parameter; } /// - /// Ensures that the string has the specified length, or otherwise throws your custom exception. + /// Ensures that the collection count 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 . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + /// The collection to be checked. + /// The range in which the collection count must lie. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// The original collection. + /// Your custom exception thrown when the collection is null or its count is not within . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static TCollection MustHaveCountIn([NotNull][ValidatedNotNull] this TCollection? parameter, Range range, Func, Exception> exceptionFactory) + where TCollection : class, IEnumerable + { + if (parameter is null || !range.IsValueWithinRange(parameter.Count())) + { + Throw.CustomException(exceptionFactory, parameter, range); + } + + return parameter; + } + + /// + /// 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) { @@ -5349,476 +4610,256 @@ public static ImmutableArray MustHaveLength(this ImmutableArray paramet } /// - /// 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([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([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 URI is an absolute one, or otherwise throws a . + /// Ensures that the string's length is within the specified range, or otherwise throws a . /// - /// The URI to be checked. + /// 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 is not an absolute URI. + /// 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 Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, [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).IsAbsoluteUri == false) + if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) { - Throw.MustBeAbsoluteUri(parameter, parameterName, message); + Throw.StringLengthNotInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. + /// Ensures that the string's length is within the specified range, 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 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 Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) { - if (parameter is null || parameter.IsAbsoluteUri == false) + if (parameter is null || !range.IsValueWithinRange(parameter.Length)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, range); } return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws an . + /// Ensures that the 's length is within the specified range, or otherwise throws an . /// - /// The collection to be checked. + /// The to be checked. + /// The range where the '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 no items. - /// Thrown when is null. + /// Thrown when the length of is not within the specified . [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 ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) == 0) + var length = parameter.IsDefault ? 0 : parameter.Length; + if (!range.IsValueWithinRange(length)) { - Throw.EmptyCollection(parameterName, message); + Throw.ImmutableArrayLengthNotInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. + /// Ensures that the 's length is within the specified range, 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 to be checked. + /// The range where the 's length must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the length of is not within the specified range. [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 + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, Func, Range, Exception> exceptionFactory) { - if (parameter is null || parameter.Count() == 0) + var length = parameter.IsDefault ? 0 : parameter.Length; + if (!range.IsValueWithinRange(length)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, range); } return parameter; } /// - /// Ensures that the specified string is not null or empty, or otherwise throws an or . + /// Ensures that the span length is within the specified range, 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 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) + public static Span MustHaveLengthIn(this Span parameter, Range range, [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).MustHaveLengthIn(range, parameterName, message); + return parameter; + } + /// + /// Ensures that the span length is within the specified range, or otherwise throws your custom exception. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span MustHaveLengthIn(this Span parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) + { + ((ReadOnlySpan)parameter).MustHaveLengthIn(range, exceptionFactory); return parameter; } /// - /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. + /// Ensures that the read-only span length is within the specified range, or otherwise throws an . /// - /// 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) + public static ReadOnlySpan MustHaveLengthIn(this ReadOnlySpan parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsNullOrEmpty()) + if (!range.IsValueWithinRange(parameter.Length)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.SpanLengthNotInRange(parameter, range, parameterName, message); } 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 read-only span length is within the specified range, 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. - /// [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) + public static ReadOnlySpan MustHaveLengthIn(this ReadOnlySpan parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) { - if (type.IsConstructedGenericType) + if (!range.IsValueWithinRange(parameter.Length)) { - return type.GetGenericTypeDefinition() == other; + Throw.CustomSpanException(exceptionFactory, parameter, range); } - return other.GetGenericTypeDefinition() == type; + 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. - /// - /// 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([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. + /// Ensures that the memory length is within the specified range, or otherwise throws an . /// - /// , - /// 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([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); + public static Memory MustHaveLengthIn(this Memory parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter.Span).MustHaveLengthIn(range, parameterName, message); + return parameter; + } + /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the memory length is within the specified range, or otherwise throws your custom exception. /// - /// 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 + public static Memory MustHaveLengthIn(this Memory parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) - { - Throw.MustNotBeLessThan(parameter, other, parameterName, message); - } - + ((ReadOnlySpan)parameter.Span).MustHaveLengthIn(range, exceptionFactory); return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Ensures that the read-only memory length is within the specified range, or otherwise throws an . /// - /// 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 + public static ReadOnlyMemory MustHaveLengthIn(this ReadOnlyMemory parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - { - Throw.CustomException(exceptionFactory, parameter!, other); - } - + parameter.Span.MustHaveLengthIn(range, parameterName, message); return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the read-only memory length is within the specified range, or otherwise throws your custom exception. /// - /// 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 + public static ReadOnlyMemory MustHaveLengthIn(this ReadOnlyMemory parameter, Range range, ReadOnlySpanExceptionFactory> exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - { - Throw.CustomException(exceptionFactory, parameter!, other); - } - + parameter.Span.MustHaveLengthIn(range, exceptionFactory); 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. - /// - /// 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([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([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; - } - - /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . + /// Ensures that the collection has at most the specified number of items, or otherwise throws an . /// - /// The string 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 contains only white space. - /// Thrown when is an empty string. + /// 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 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; - } - - /// - /// 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) + 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) + if (parameter.Count(parameterName, message) > count) { - Throw.MustBeUtcDateTime(parameter, parameterName, message); + 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) + if (parameter is null || parameter.Count() > count) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, count); } return parameter; } /// - /// Ensures that the specified has a zero offset, or otherwise throws an . + /// Ensures that the has at most the specified length, or otherwise throws an . /// - /// The date time offset to be checked. + /// The to be checked. + /// The maximum length the should have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// The original date time offset without conversion. - /// Thrown when does not have as its offset. + /// Thrown when has more than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTimeOffset MustBeUtc(this DateTimeOffset parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Offset != TimeSpan.Zero) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength > length) { - Throw.MustBeUtcDateTimeOffset(parameter, parameterName, message); + Throw.InvalidMaximumImmutableArrayLength(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified has a zero offset, or otherwise throws your custom exception. + /// Ensures that the has at most the specified length, or otherwise throws your custom exception. /// - /// The date time offset to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original date time offset without conversion. - /// Your custom exception thrown when does not have as its offset. + /// The to be checked. + /// The maximum length the should have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when has more than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTimeOffset MustBeUtc(this DateTimeOffset parameter, Func exceptionFactory) + public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) { - if (parameter.Offset != TimeSpan.Zero) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength > length) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } - /// - /// Checks if the specified character is a digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsDigit(this char character) => char.IsDigit(character); - /// - /// 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 collection has at least the specified number of items, or otherwise throws an . /// @@ -5862,518 +4903,644 @@ public static TCollection MustHaveMinimumCount([NotNull][ValidatedN } /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws an . + /// Ensures that the has at least the specified length, or otherwise throws an . /// - /// The email address that will be validated. + /// The to be checked. + /// The minimum length the should have. /// 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 has less than the specified length. + /// The default instance of will be treated as having length 0. [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 ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength < length) { - Throw.InvalidEmailAddress(parameter, parameterName, message); + Throw.InvalidMinimumImmutableArrayLength(parameter, length, 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 has at least the specified length, 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. + /// The to be checked. + /// The minimum length the should have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when has less than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) { - if (!parameter.IsEmailAddress()) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength < length) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws an . + /// Ensures that the URI has one of the specified schemes, 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 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 is no valid email address. - /// Thrown when is null. + /// 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; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [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 (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) + // ReSharper disable PossibleMultipleEnumeration + parameter.MustBeAbsoluteUri(parameterName, message); + if (schemes is ICollection collection) { - Throw.InvalidEmailAddress(parameter, parameterName, message); + 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 string is a valid email address using the provided regular expression, - /// or otherwise throws your custom exception. + /// Ensures that the URI has one of the specified schemes, 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 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; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, 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 (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 (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) + if (schemes is null || !schemes.Contains(parameter.Scheme)) { - Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); + Throw.CustomException(exceptionFactory, parameter, schemes!); } return parameter; } /// - /// 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([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([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); - /// - /// Ensures that the string is shorter than the specified length, or otherwise throws a . + /// Ensures that the has the specified scheme, or otherwise throws an . /// - /// The string to be checked. - /// The length that the string must be shorter than. + /// 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 has a length greater than or equal to . + /// 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 string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, [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 (parameter.MustNotBeNull(parameterName, message).Length >= length) + if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) { - Throw.StringNotShorterThan(parameter, length, parameterName, message); + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); } return parameter; } /// - /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the has the specified scheme, 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 . + /// 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 string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { - if (parameter is null || parameter.Length >= length) + if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } 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) - { - ((ReadOnlySpan)parameter).MustBeShorterThan(length, parameterName, message); - return parameter; - } - - /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the has the specified scheme, 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 . + /// 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)] - public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { - if (parameter.Length >= length) + if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, scheme); } return parameter; } /// - /// Ensures that the span is shorter than 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 value that the span must be shorter than. + /// The nullable to be checked. /// 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 . + /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan 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) + if (!parameter.HasValue) { - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + Throw.NullableHasNoValue(parameterName, message); } - return parameter; + return parameter.Value; } /// - /// Ensures that the span is shorter than 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 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 nullable to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : struct { - if (parameter.Length >= length) + if (!parameter.HasValue) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory); } - return parameter; + return parameter.Value; } /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . + /// Ensures that the string matches the specified regular expression, or otherwise throws a . /// /// The string to be checked. - /// The length that the string must be longer than or equal to. + /// 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 has a length shorter than . - /// Thrown when is null. + /// Thrown when does not match the specified regular expression. + /// Thrown when or 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) + [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 (parameter.MustNotBeNull(parameterName, message).Length < length) + if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) { - Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + Throw.StringDoesNotMatch(parameter, regex, 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 matches the specified regular expression, 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 . + /// 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)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) { - if (parameter is null || parameter.Length < length) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || regex is null || !regex.IsMatch(parameter)) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, regex!); } return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. + /// 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 is shorter than . + /// Thrown when and are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [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) { - ((ReadOnlySpan)parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesEqual(parameter, other, 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 is not equal to using the default equality comparer, 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 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 Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static T MustNotBe(this T parameter, T other, Func exceptionFactory) { - if (parameter.Length < length) + if (EqualityComparer.Default.Equals(parameter, other)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. + /// 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 is shorter than . + /// Thrown when and are equal. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(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.Length < length) + if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) { - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); + Throw.ValuesEqual(parameter, other, 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 is not equal to using the specified equality comparer, 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 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)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) { - if (parameter.Length < length) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (equalityComparer is null || equalityComparer.Equals(parameter, other)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); } return parameter; } /// - /// Ensures that the string does not start with 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 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 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 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) + /// 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 (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + if (string.Equals(parameter, other, comparisonType)) { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string does not start with 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 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 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; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] 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.StartsWith(value)) + if (string.Equals(parameter, other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the two strings are not equal using the specified , 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. - /// 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 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)] - [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 string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [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.StartsWith(value, comparisonType)) + if (parameter.Equals(other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the span does not start with the specified value, or otherwise throws a . + /// Ensures that the two strings are not equal using the specified , 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 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. + /// 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 ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { - if (parameter.StartsWith(value, comparisonType)) + if (parameter.Equals(other, comparisonType)) { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } return parameter; } /// - /// Ensures that the span does not start with the specified value, 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 span to be checked. - /// The other span that must not start with. + /// 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 + /// 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 the absolute difference between and is + /// less than 0.0001. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, ReadOnlySpansExceptionFactory exceptionFactory) - where T : IEquatable + public static double MustNotBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter.StartsWith(value)) + if (parameter.IsApproximately(other)) { - Throw.CustomSpanException(exceptionFactory, parameter, value); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . /// - /// 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. + /// 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 ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, ReadOnlySpansExceptionFactory exceptionFactory) + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.StartsWith(value, comparisonType)) + if (parameter.IsApproximately(other, tolerance)) { - Throw.CustomSpanException(exceptionFactory, parameter, value, comparisonType); + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); } 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 is not approximately equal to the given + /// value, 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 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 T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (default(T)is null) - { - if (parameter is null) - { - Throw.ArgumentNull(parameterName, message); - } - - return parameter; - } - - if (EqualityComparer.Default.Equals(parameter, default!)) + if (parameter.IsApproximately(other, tolerance)) { - Throw.ArgumentDefault(parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } -#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. + /// 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 delegate that creates your custom exception. - /// Your custom exception thrown when is the default 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 the absolute difference between and is + /// less than 0.0001f. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + 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; + } + + /// + /// 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) @@ -6396,1521 +5563,3508 @@ public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter return parameter; #pragma warning restore CS8777 } - } - /// - /// 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. + /// Ensures that the specified is not default or empty, or otherwise throws an . /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) + /// The to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is default or empty. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - x.MustNotBeNull(nameof(x)); - y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(y); + if (parameter.IsDefaultOrEmpty) + { + Throw.EmptyCollection(parameterName, message); + } + + return parameter; } /// - /// 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. + /// Ensures that the specified is not default or empty, or otherwise throws your custom exception. /// - /// Thrown when is null. - public int GetHashCode(string @string) + /// The to be checked. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is default or empty. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, Func, Exception> exceptionFactory) { - @string.MustNotBeNull(nameof(@string)); - var hashBuilder = MultiplyAddHashBuilder.Create(); - foreach (var character in @string) + if (parameter.IsDefaultOrEmpty) { - if (!character.IsWhiteSpace()) - { - hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); - } + Throw.CustomException(exceptionFactory, parameter); } - return hashBuilder.BuildHash(); + return parameter; } - } - /// - /// Provides regular expressions that are used in string assertions. - /// - internal 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 = @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\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/. + /// Ensures that the specified GUID is not empty, or otherwise throws an . /// - public static readonly Regex EmailRegex = new(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); - } + /// 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); + } - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class ValidatedNotNullAttribute : Attribute - { - } + return parameter; + } - /// - /// Defines a range that can be used to check if a specified is in between it or not. - /// - /// The type that the range should be applied to. - internal readonly struct Range : IEquatable> where T : IComparable - { - /// - /// Gets the lower boundary of the range. - /// - public readonly T From; - /// - /// Gets the upper boundary of the range. - /// - public readonly T To; - /// - /// Gets the value indicating whether the From value is included in the range. - /// - public readonly bool IsFromInclusive; - /// - /// Gets the value indicating whether the To value is included in the range. - /// - public readonly bool IsToInclusive; - private readonly int _expectedLowerBoundaryResult; - private readonly int _expectedUpperBoundaryResult; /// - /// Creates a new instance of . + /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. /// - /// The lower boundary of the range. - /// The upper boundary of the range. - /// The value indicating whether is part of the range. - /// The value indicating whether is part of the range. - /// Thrown when is less than . + /// The GUID to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is an empty GUID. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range(T from, T to, bool isFromInclusive = true, bool isToInclusive = true) + [ContractAnnotation("exceptionFactory:null => halt")] + public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) { - From = from.MustNotBeNullReference(nameof(from)); - To = to.MustNotBeLessThan(from, nameof(to)); - IsFromInclusive = isFromInclusive; - IsToInclusive = isToInclusive; - _expectedLowerBoundaryResult = isFromInclusive ? 0 : 1; - _expectedUpperBoundaryResult = isToInclusive ? 0 : -1; + if (parameter == Guid.Empty) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; } /// - /// Checks if the specified is within range. + /// Ensures that the specified span is not empty, or otherwise throws an . /// - /// The value to be checked. - /// True if value is within range, otherwise false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsValueWithinRange(T value) => value.MustNotBeNullReference(nameof(value)).CompareTo(From) >= _expectedLowerBoundaryResult && value.CompareTo(To) <= _expectedUpperBoundaryResult; + public static Span MustNotBeEmpty(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustNotBeEmpty(parameterName, message); + return parameter; + } + /// - /// Use this method to create a range in a fluent style using method chaining. - /// Defines the lower boundary as an inclusive value. + /// Ensures that the specified span is not empty, or otherwise throws your custom exception. /// - /// 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(value, true); + public static Span MustNotBeEmpty(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter).MustNotBeEmpty(exceptionFactory); + return parameter; + } + /// - /// Use this method to create a range in a fluent style using method chaining. - /// Defines the lower boundary as an exclusive value. + /// Ensures that the specified read-only span is not empty, or otherwise throws an . /// - /// 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(value, false); - /// - /// The nested can be used to fluently create a . - /// - public readonly struct RangeFromInfo + public static ReadOnlySpan MustNotBeEmpty(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - private readonly T _from; - private readonly bool _isFromInclusive; - /// - /// Creates a new RangeFromInfo. - /// - /// The lower boundary of the range. - /// The value indicating whether is part of the range. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RangeFromInfo(T from, bool isFromInclusive) + if (parameter.IsEmpty) { - _from = from; - _isFromInclusive = isFromInclusive; + Throw.EmptyCollection(parameterName, message); } - /// - /// Use this method to create a range in a fluent style using method chaining. - /// Defines the upper boundary as an exclusive value. - /// - /// The value that indicates the exclusive upper boundary of the resulting range. - /// A new range with the specified upper and lower boundaries. - /// - /// Thrown when is less than the lower boundary value. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - 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. - /// - /// The value that indicates the inclusive upper boundary of the resulting range. - /// A new range with the specified upper and lower boundaries. - /// - /// Thrown when is less than the lower boundary value. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Range ToInclusive(T value) => new(_from, value, _isFromInclusive); + return parameter; } - /// - public override string ToString() => $"Range from {CreateRangeDescriptionText()}"; - /// - /// 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 on whether is true or false. - /// - public string UpperBoundaryText {[MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetBoundaryText(IsToInclusive); } - /// - /// Returns a text description of this range with the following pattern: From (inclusive | exclusive) to To (inclusive | exclusive). + /// Ensures that the specified read-only span is not empty, or otherwise throws your custom exception. /// - public string CreateRangeDescriptionText(string fromToConnectionWord = "to") => From + " (" + LowerBoundaryText + ") " + fromToConnectionWord + ' ' + To + " (" + UpperBoundaryText + ")"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string GetBoundaryText(bool isInclusive) => isInclusive ? "inclusive" : "exclusive"; - /// - public bool Equals(Range other) + public static ReadOnlySpan MustNotBeEmpty(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (IsFromInclusive != other.IsFromInclusive || IsToInclusive != other.IsToInclusive) + if (parameter.IsEmpty) { - return false; + Throw.CustomSpanException(exceptionFactory, parameter); } - var comparer = EqualityComparer.Default; - return comparer.Equals(From, other.From) && comparer.Equals(To, other.To); + return parameter; } - /// - public override bool Equals(object? other) + /// + /// Ensures that the specified memory is not empty, or otherwise throws an . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustNotBeEmpty(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (other is null) - { - return false; - } - - return other is Range range && Equals(range); + ((ReadOnlySpan)parameter.Span).MustNotBeEmpty(parameterName, message); + return parameter; } - /// - public override int GetHashCode() => MultiplyAddHash.CreateHashCode(From, To, IsFromInclusive, IsToInclusive); /// - /// Checks if two ranges are equal. + /// Ensures that the specified memory is not empty, or otherwise throws your custom exception. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Range first, Range second) => first.Equals(second); + public static Memory MustNotBeEmpty(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter.Span).MustNotBeEmpty(exceptionFactory); + return parameter; + } + /// - /// Checks if two ranges are not equal. + /// Ensures that the specified read-only memory is not empty, or otherwise throws an . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Range first, Range second) => first.Equals(second) == false; - } + public static ReadOnlyMemory MustNotBeEmpty(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + parameter.Span.MustNotBeEmpty(parameterName, message); + return parameter; + } - /// - /// Provides methods to simplify the creation of instances. - /// - internal static class Range - { /// - /// Use this method to create a range in a fluent style using method chaining. - /// Defines the lower boundary as an inclusive value. + /// Ensures that the specified read-only memory is not empty, or otherwise throws your custom exception. /// - /// 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 Range.RangeFromInfo FromInclusive(T value) - where T : IComparable => new(value, true); + public static ReadOnlyMemory MustNotBeEmpty(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + parameter.Span.MustNotBeEmpty(exceptionFactory); + return parameter; + } + /// - /// Use this method to create a range in a fluent style using method chaining. - /// Defines the lower boundary as an exclusive value. + /// Ensures that the character span is neither empty nor all white space. /// - /// 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 Range.RangeFromInfo FromExclusive(T value) - where T : IComparable => new(value, false); + public static Span MustNotBeEmptyOrWhiteSpace(this Span parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustNotBeEmptyOrWhiteSpace(parameterName, message); + return parameter; + } + /// - /// Creates a range with both boundaries inclusive. + /// Ensures that the character span is neither empty nor all white space, or otherwise throws your custom exception. /// - /// 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); + public static Span MustNotBeEmptyOrWhiteSpace(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter).MustNotBeEmptyOrWhiteSpace(exceptionFactory); + return parameter; + } + /// - /// Creates a range with both boundaries exclusive. + /// Ensures that the read-only character span is neither empty nor all white space. /// - /// The lower boundary of the range. - /// The upper boundary of the range. - /// A new range with both boundaries exclusive. - /// Thrown when is less than . + /// Thrown when is empty. + /// Thrown when contains only white space. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range ExclusiveBetween(T from, T to) - where T : IComparable => new(from, to, false, false); + public static ReadOnlySpan MustNotBeEmptyOrWhiteSpace(this ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.IsEmpty) + { + Throw.EmptyString(parameterName, message); + } + + if (parameter.IsEmptyOrWhiteSpace()) + { + Throw.WhiteSpaceSpan(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Creates a range for the specified enumerable that encompasses all valid indexes. + /// Ensures that the read-only character span is neither empty nor all white space, or otherwise throws your custom exception. /// - /// - /// 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. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Range For(IEnumerable enumerable) => new(0, enumerable.Count(), true, false); + public static ReadOnlySpan MustNotBeEmptyOrWhiteSpace(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + if (parameter.IsEmptyOrWhiteSpace()) + { + Throw.CustomSpanException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Creates a range for the specified enumerable that encompasses all valid indexes. + /// Ensures that the character memory is neither empty nor all white space. /// - /// - /// 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. - public static Range For(IEnumerable enumerable) => new(0, enumerable.GetCount(), true, false); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory MustNotBeEmptyOrWhiteSpace(this Memory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter.Span).MustNotBeEmptyOrWhiteSpace(parameterName, message); + return parameter; + } + /// - /// Creates a range for the specified span that encompasses all valid indexes. + /// Ensures that the character memory is neither empty nor all white space, or otherwise throws your custom exception. /// - /// - /// 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, true, false); + public static Memory MustNotBeEmptyOrWhiteSpace(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter.Span).MustNotBeEmptyOrWhiteSpace(exceptionFactory); + return parameter; + } + /// - /// Creates a range for the specified span that encompasses all valid indexes. + /// Ensures that the read-only character memory is neither empty nor all white space. /// - /// - /// 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, true, false); + public static ReadOnlyMemory MustNotBeEmptyOrWhiteSpace(this ReadOnlyMemory parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + parameter.Span.MustNotBeEmptyOrWhiteSpace(parameterName, message); + return parameter; + } + /// - /// Creates a range for the specified memory that encompasses all valid indexes. + /// Ensures that the read-only character memory is neither empty nor all white space, or otherwise throws your custom exception. /// - /// - /// 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, true, false); + public static ReadOnlyMemory MustNotBeEmptyOrWhiteSpace(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + parameter.Span.MustNotBeEmptyOrWhiteSpace(exceptionFactory); + return parameter; + } + /// - /// Creates a range for the specified memory that encompasses all valid indexes. + /// Ensures that the specified is not greater than the given value, or otherwise throws an . /// - /// - /// The length of the memory is used to create a valid index range. - /// + /// 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)] - public static Range For(ReadOnlyMemory memory) => new(0, memory.Length, true, false); + [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; + } + /// - /// Creates a range for the specified memory that encompasses all valid indexes. + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. /// - /// - /// The count of the segment is used to create a valid index range. - /// + /// 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)] - public static Range For(ArraySegment segment) => new(0, segment.Count, true, false); - } + [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; + } - /// - /// 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. + /// Ensures that the specified is less than the given value, or otherwise throws an . /// - Ordinal = 4, + /// 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; + } + /// - /// Compare strings using ordinal sort rules and ignoring the case of the strings - /// being compared. + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. /// - OrdinalIgnoreCase = 5, + /// 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; + } + /// - /// Compare strings using ordinal sort rules and ignoring the white space characters - /// of the strings being compared. + /// Ensures that is not within the specified range, or otherwise throws an . /// - OrdinalIgnoreWhiteSpace = 6, + /// 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; + } + /// - /// Compare strings using ordinal sort rules, ignoring the case and ignoring the - /// white space characters of the strings being compared. + /// Ensures that is not within the specified range, or otherwise throws your custom exception. /// - OrdinalIgnoreCaseIgnoreWhiteSpace = 7, - } + /// 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; + } - /// - /// This class caches instances to avoid use of the typeof operator. - /// - internal abstract class Types - { /// - /// Gets the type. + /// Ensures that the specified is not less than the given value, or otherwise throws an . /// - public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); - } + /// 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; + } - /// - /// 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. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) + /// 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 { - x.MustNotBeNull(nameof(x)); - y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreWhiteSpace(y); + // 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; } /// - /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. + /// Ensures that the specified is greater than the given value, or otherwise throws an . /// - /// Thrown when is null. - public int GetHashCode(string @string) + /// 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 { - @string.MustNotBeNull(nameof(@string)); - var hashCodeBuilder = MultiplyAddHashBuilder.Create(); - foreach (var character in @string) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) { - if (!character.IsWhiteSpace()) - { - hashCodeBuilder.CombineIntoHash(character); - } + Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); } - return hashCodeBuilder.BuildHash(); + return parameter; } - } - /// - /// Overlays the stable sequential GUID field layout so UUID structural fields can be inspected without allocations. - /// - [StructLayout(LayoutKind.Explicit, Size = 16)] - internal readonly struct GuidLayout - { - [FieldOffset(0)] - private readonly Guid _value; - [FieldOffset(6)] - public readonly ushort TimeHighAndVersion; - [FieldOffset(8)] - public readonly byte ClockSequenceHighAndReserved; - public GuidLayout(Guid value) => _value = value; - } - - /// - /// Represents an that uses - /// to compare types. This check works like the normal type equality comparison, but when two - /// generic types are compared, they are regarded as equal when one of them is a constructed generic type - /// and the other one is the corresponding generic type definition. - /// - internal sealed class EquivalentTypeComparer : IEqualityComparer - { - /// - /// Gets a singleton instance of the equality comparer. - /// - 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 - /// generic types are compared, they are regarded as equal when one of them is a constructed generic type - /// and the other one is the corresponding generic type definition. - /// - /// The first type. - /// The second type. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Type? x, Type? y) => x.IsEquivalentTypeTo(y); /// - /// Returns the hash code of the given type. When the specified type is a constructed generic type, - /// the hash code of the generic type definition is returned instead. + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. /// - /// The type whose hash code is requested. + /// 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)] - public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); - } - - /// - /// 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() + [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 { - EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); - EnumConstants = new ReadOnlyMemory(EnumConstantsArray); - if (!IsFlagsEnum) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) { - return; + Throw.CustomException(exceptionFactory, parameter!, other); } - for (var i = 0; i < EnumConstantsArray.Length; ++i) - { - var convertedValue = ConvertToUInt64(EnumConstantsArray[i]); - FlagsPattern |= convertedValue; - } + return parameter; } + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsValidFlagsValue(T enumValue) - { - var convertedValue = ConvertToUInt64(enumValue); - return (FlagsPattern & convertedValue) == convertedValue; - } - - private static bool IsValidValue(T parameter) + public static int MustNotBeNegative(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var comparer = EqualityComparer.Default; - for (var i = 0; i < EnumConstantsArray.Length; ++i) + if (!(parameter >= 0)) { - if (comparer.Equals(EnumConstantsArray[i], parameter)) - { - return true; - } + Throw.MustNotBeNegative(parameter, parameterName, message); } - return false; + 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 negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The enum value to be checked. - /// True if either the enum value is + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidEnumValue(T enumValue) => IsFlagsEnum ? IsValidFlagsValue(enumValue) : IsValidValue(enumValue); - private static ulong ConvertToUInt64(T value) + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBeNegative(this int parameter, Func exceptionFactory) { - switch (EnumSize) + if (!(parameter >= 0)) { - 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 0UL; + Throw.CustomException(exceptionFactory, parameter); } - } - 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."); - } -} + return parameter; + } -namespace Light.GuardClauses.Exceptions -{ - /// - /// This exception indicates that a collection has no items. - /// - [Serializable] - internal class EmptyCollectionException : InvalidCollectionCountException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBeNegative(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - } + if (!(parameter >= 0L)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } - /// - protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that an URI has an invalid scheme. - /// - [Serializable] - internal class InvalidUriSchemeException : UriException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public InvalidUriSchemeException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBeNegative(this long parameter, Func exceptionFactory) { - } + if (!(parameter >= 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } - /// - protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a or value is invalid. - /// - [Serializable] - internal class InvalidDateTimeException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBeNegative(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - } + if (!(parameter >= 0m)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } - /// - protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a string is in an invalid state. - /// - [Serializable] - internal class StringException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBeNegative(this decimal parameter, Func exceptionFactory) { - } + if (!(parameter >= 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } - /// - protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a string is empty. - /// - [Serializable] - internal class EmptyStringException : StringException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeNegative(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - } + if (!(parameter >= 0f)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } - /// - protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that two values are not equal. - /// - [Serializable] - internal class ValuesNotEqualException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBeNegative(this float parameter, Func exceptionFactory) { - } + if (!(parameter >= 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } - /// - protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a value is not defined in the corresponding enum type. - /// - [Serializable] - internal class EnumValueNotDefinedException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . /// - /// The name of the parameter. - /// The message of the exception. - public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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 less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeNegative(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - } + if (!(parameter >= 0d)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } - /// - protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a collection contains an item that must not be part of it. - /// - [Serializable] - internal class ExistingItemException : CollectionException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBeNegative(this double parameter, Func exceptionFactory) { - } + if (!(parameter >= 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } - /// - protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that an item is not present in a collection. - /// - [Serializable] - internal class MissingItemException : CollectionException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBeNegative(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - } + if (!(parameter >= TimeSpan.Zero)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } - /// - protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that an URI is absolute instead of relative. - /// - [Serializable] - internal class AbsoluteUriException : UriException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBeNegative(this TimeSpan parameter, Func exceptionFactory) { - } + if (!(parameter >= TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } - /// - protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that that a GUID is empty. - /// - [Serializable] - internal class EmptyGuidException : ArgumentException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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); + } - /// - protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a has no value. - /// - [Serializable] - internal class NullableHasNoValueException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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); + } - /// - protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that two references point to the same object. - /// - [Serializable] - internal class SameObjectReferenceException : ArgumentException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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); + } - /// - protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a value cannot be cast to another type. - /// - [Serializable] - internal class TypeCastException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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); + } - /// - protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that a string has an invalid length. - /// - [Serializable] - internal class StringLengthException : StringException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// 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); + } - /// - protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) - { + if (parameter.Length == 0) + { + Throw.EmptyString(parameterName, message); + } + + return parameter; } - } - /// - /// This exception indicates that a string contains only white space. - /// - [Serializable] - internal class WhiteSpaceStringException : StringException - { /// - /// Creates a new instance of . + /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// 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); + } - /// - protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that an item is not part of a collection. - /// - [Serializable] - internal class ValueIsNotOneOfException : ArgumentException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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; + } + } - /// - protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) - { + Throw.WhiteSpaceString(parameter, parameterName, message); + return null; } - } - /// - /// This exception indicates that a string is in an invalid state. - /// - [Serializable] - internal class SubstringException : StringException - { /// - /// Creates a new instance of . + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// 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); + } - /// - protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that configuration data is invalid. - /// - [Serializable] - internal class InvalidConfigurationException : Exception - { /// - /// Initializes a new instance of . + /// 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 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) + /// 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. - /// - protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; } - } - /// - /// This exception indicates that the data is in invalid state. - /// - [Serializable] - internal class InvalidStateException : Exception - { /// - /// Creates a new instance of . + /// 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 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) + /// 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. - /// - protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; } - } - /// - /// This exception indicates that a value of a value type is the default value. - /// - [Serializable] - internal class ArgumentDefaultException : ArgumentException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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); + } - /// - protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; + // ReSharper restore PossibleMultipleEnumeration } - } - /// - /// This exception indicates that the state of a collection is invalid. - /// - [Serializable] - internal class CollectionException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// 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!); + } - /// - protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } - /// - /// This exception indicates that an URI is invalid. - /// - [Serializable] - internal class UriException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustNotBePositive(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBePositive(this int parameter, Func exceptionFactory) { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that a string is not matching a regular expression. - /// - [Serializable] - internal class StringDoesNotMatchException : StringException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public StringDoesNotMatchException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBePositive(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= 0L)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBePositive(this long parameter, Func exceptionFactory) { + if (!(parameter <= 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that two values are equal. - /// - [Serializable] - internal class ValuesEqualException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBePositive(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= 0m)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBePositive(this decimal parameter, Func exceptionFactory) { + if (!(parameter <= 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that an Email address is invalid. - /// - [Serializable] - internal class InvalidEmailAddressException : StringException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBePositive(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= 0f)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBePositive(this float parameter, Func exceptionFactory) { + if (!(parameter <= 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that an URI is relative instead of absolute. - /// - [Serializable] - internal class RelativeUriException : UriException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBePositive(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= 0d)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBePositive(this double parameter, Func exceptionFactory) { + if (!(parameter <= 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that an item is part of a collection. - /// - [Serializable] - internal class ValueIsOneOfException : ArgumentException - { /// - /// Creates a new instance of . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// + /// The value to be checked. /// The name of the parameter (optional). - /// The message of the exception (optional). - public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBePositive(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; } - /// - protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBePositive(this TimeSpan parameter, Func exceptionFactory) { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; } - } - /// - /// This exception indicates that a collection has an invalid number of items. - /// - [Serializable] - internal class InvalidCollectionCountException : CollectionException - { /// - /// Creates a new instance of . + /// 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 of the exception (optional). - public InvalidCollectionCountException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// 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); + } - /// - protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) - { + return parameter; } - } -} -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 - { - /// - /// 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 string is empty, using the optional - /// parameter name and message. + /// Ensures that and do not point to the same object instance, or otherwise + /// throws your custom exception. /// - [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 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}."); + /// 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; + } + /// - /// Throws the default indicating that a value is one of a specified collection - /// of items, using the optional parameter name and message. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - [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()); + /// 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; + } + /// - /// Throws the default indicating that a reference cannot be downcast, using the - /// optional parameter name and message. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - [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}\"."); + /// 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; + } + /// - /// Throws the default indicating that a string is not trimmed at the start. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - [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()}."); + /// 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; + } + /// - /// Throws the default indicating that a string does not start with another one, - /// using the optional parameter name and message. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - [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()}."); + /// 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; + } + /// - /// Throws the default indicating that a comparable value must be less - /// than the given boundary value, using the optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [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}."); + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustNotBeZero(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws the default indicating that a value is the default value of its - /// type, using the optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [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."); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBeZero(this int parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return 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. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [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}."); + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBeZero(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0L) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws an indicating that a floating-point value is not finite. + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFinite(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be finite, but it actually is {parameter}."); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBeZero(this long parameter, Func exceptionFactory) + { + if (parameter == 0L) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Throws the default indicating that a collection count is outside a range. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void CollectionCountNotInRange(IEnumerable parameter, int actualCount, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have its count between {range.CreateRangeDescriptionText("and")}, but it actually has count {actualCount}."); + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBeZero(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0m) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws the default indicating that a string is not equal to "\n" or "\r\n". + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [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()}."); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBeZero(this decimal parameter, Func exceptionFactory) + { + if (parameter == 0m) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Throws the default indicating that a value is not within a specified - /// range, using the optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [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 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 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."); + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeZero(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0f) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws the default indicating that a string contains only white space, - /// using the optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [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}\"."); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBeZero(this float parameter, Func exceptionFactory) + { + if (parameter == 0f) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Throws the default indicating that a character span contains only - /// white space, using the optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void WhiteSpaceSpan(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The character span"} must not contain only white space, but it actually has length {parameter.Length}."); + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeZero(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0d) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws an using the optional message. + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBeZero(this double parameter, Func exceptionFactory) + { + if (parameter == 0d) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return 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. + /// Ensures that the specified is not zero, or otherwise + /// throws an . /// - [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}."); + /// 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 . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBeZero(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == TimeSpan.Zero) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Throws the default indicating that a collection has no items, using the - /// optional parameter name and message. + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. /// - [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."); + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBeZero(this TimeSpan parameter, Func exceptionFactory) + { + if (parameter == TimeSpan.Zero) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// - /// Throws the default indicating that a collection has more than a - /// maximum number of items, using the optional parameter name and message. + /// Ensures that the collection does not contain the specified item, or otherwise throws an . /// - [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()}."); + /// 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; + } + /// - /// Throws an using the optional message. + /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidState(string? message = null) => throw new InvalidStateException(message); + /// 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; + } + /// - /// Throws the default indicating that a collection has less than a - /// minimum number of items, using the optional parameter name and message. + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// - [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()}."); + /// 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; + } + /// - /// 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. + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. /// - [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()); + /// 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; + } + /// - /// Throws the default indicating that a string ends with another one, using the - /// optional parameter name and message. + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// - [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()}."); + /// 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; + } + + /// + /// Ensures that the does not contain the specified item, or otherwise throws an . + /// + /// The to be checked. + /// The item that must not be part of the . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// + /// The default instance of cannot contain any items, so this method will not throw for default instances. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.IsDefault && parameter.Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the does not contain the specified item, or otherwise throws your custom exception. + /// + /// The to be checked. + /// The item that must not be part of the . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . + /// + /// The default instance of cannot contain any items, so this method will not throw for default instances. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) + { + if (!parameter.IsDefault && parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + 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 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; + } + + /// + /// 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(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)] + [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 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; + } + + /// + /// 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; + } + } + + /// + /// 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() + { + EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); + EnumConstants = new ReadOnlyMemory(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; + } + + /// + /// 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 enum value to be checked. + /// True if either the enum value is + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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 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."); + } + + /// + /// Represents an that uses + /// to compare types. This check works like the normal type equality comparison, but when two + /// generic types are compared, they are regarded as equal when one of them is a constructed generic type + /// and the other one is the corresponding generic type definition. + /// + internal sealed class EquivalentTypeComparer : IEqualityComparer + { + /// + /// Gets a singleton instance of the equality comparer. + /// + 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 + /// generic types are compared, they are regarded as equal when one of them is a constructed generic type + /// and the other one is the corresponding generic type definition. + /// + /// The first type. + /// The second type. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Type? x, Type? y) => x.IsEquivalentTypeTo(y); + /// + /// Returns the hash code of the given type. When the specified type is a constructed generic type, + /// the hash code of the generic type definition is returned instead. + /// + /// The type whose hash code is requested. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); + } + + /// + /// Overlays the stable sequential GUID field layout so UUID structural fields can be inspected without allocations. + /// + [StructLayout(LayoutKind.Explicit, Size = 16)] + internal readonly struct GuidLayout + { + [FieldOffset(0)] + private readonly Guid _value; + [FieldOffset(6)] + public readonly ushort TimeHighAndVersion; + [FieldOffset(8)] + public readonly byte ClockSequenceHighAndReserved; + public GuidLayout(Guid value) => _value = value; + } + + /// + /// 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. + /// + /// The type that the range should be applied to. + internal readonly struct Range : IEquatable> where T : IComparable + { + /// + /// Gets the lower boundary of the range. + /// + public readonly T From; + /// + /// Gets the upper boundary of the range. + /// + public readonly T To; + /// + /// Gets the value indicating whether the From value is included in the range. + /// + public readonly bool IsFromInclusive; + /// + /// Gets the value indicating whether the To value is included in the range. + /// + public readonly bool IsToInclusive; + private readonly int _expectedLowerBoundaryResult; + private readonly int _expectedUpperBoundaryResult; + /// + /// Creates a new instance of . + /// + /// The lower boundary of the range. + /// The upper boundary of the range. + /// The value indicating whether is part of the range. + /// The value indicating whether is part of the range. + /// Thrown when is less than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Range(T from, T to, bool isFromInclusive = true, bool isToInclusive = true) + { + From = from.MustNotBeNullReference(nameof(from)); + To = to.MustNotBeLessThan(from, nameof(to)); + IsFromInclusive = isFromInclusive; + IsToInclusive = isToInclusive; + _expectedLowerBoundaryResult = isFromInclusive ? 0 : 1; + _expectedUpperBoundaryResult = isToInclusive ? 0 : -1; + } + + /// + /// Checks if the specified is within range. + /// + /// The value to be checked. + /// True if value is within range, otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsValueWithinRange(T value) => value.MustNotBeNullReference(nameof(value)).CompareTo(From) >= _expectedLowerBoundaryResult && value.CompareTo(To) <= _expectedUpperBoundaryResult; + /// + /// Use this method to create a range in a fluent style using method chaining. + /// Defines the lower boundary as an inclusive 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(value, true); + /// + /// Use this method to create a range in a fluent style using method chaining. + /// Defines the lower boundary as an exclusive 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(value, false); + /// + /// The nested can be used to fluently create a . + /// + public readonly struct RangeFromInfo + { + private readonly T _from; + private readonly bool _isFromInclusive; + /// + /// Creates a new RangeFromInfo. + /// + /// The lower boundary of the range. + /// The value indicating whether is part of the range. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RangeFromInfo(T from, bool isFromInclusive) + { + _from = from; + _isFromInclusive = isFromInclusive; + } + + /// + /// Use this method to create a range in a fluent style using method chaining. + /// Defines the upper boundary as an exclusive value. + /// + /// The value that indicates the exclusive upper boundary of the resulting range. + /// A new range with the specified upper and lower boundaries. + /// + /// Thrown when is less than the lower boundary value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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. + /// + /// The value that indicates the inclusive upper boundary of the resulting range. + /// A new range with the specified upper and lower boundaries. + /// + /// Thrown when is less than the lower boundary value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Range ToInclusive(T value) => new(_from, value, _isFromInclusive); + } + + /// + public override string ToString() => $"Range from {CreateRangeDescriptionText()}"; + /// + /// 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 on whether is true or false. + /// + public string UpperBoundaryText {[MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetBoundaryText(IsToInclusive); } + + /// + /// Returns a text description of this range with the following pattern: From (inclusive | exclusive) to To (inclusive | exclusive). + /// + public string CreateRangeDescriptionText(string fromToConnectionWord = "to") => From + " (" + LowerBoundaryText + ") " + fromToConnectionWord + ' ' + To + " (" + UpperBoundaryText + ")"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string GetBoundaryText(bool isInclusive) => isInclusive ? "inclusive" : "exclusive"; + /// + 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); + } + + /// + public override bool Equals(object? other) + { + if (other is null) + { + return false; + } + + return other is Range range && Equals(range); + } + + /// + public override int GetHashCode() => MultiplyAddHash.CreateHashCode(From, To, IsFromInclusive, IsToInclusive); + /// + /// Checks if two ranges are equal. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Range first, Range second) => first.Equals(second); + /// + /// Checks if two ranges are not equal. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Range first, Range second) => first.Equals(second) == false; + } + + /// + /// Provides methods to simplify the creation of instances. + /// + internal static class Range + { + /// + /// Use this method to create a range in a fluent style using method chaining. + /// Defines the lower boundary as an inclusive 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 Range.RangeFromInfo FromInclusive(T value) + where T : IComparable => 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. + /// + /// 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 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. + /// + /// + /// 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. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range For(IEnumerable enumerable) => new(0, enumerable.Count(), true, false); + /// + /// Creates a range for the specified enumerable that encompasses all valid indexes. + /// + /// + /// 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. + public static Range For(IEnumerable enumerable) => new(0, enumerable.GetCount(), true, false); + /// + /// Creates a range for the specified span that encompasses all valid indexes. + /// + /// + /// 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, true, false); + /// + /// Creates a range for the specified span that encompasses all valid indexes. + /// + /// + /// 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, true, false); + /// + /// Creates a range for the specified memory that encompasses all valid indexes. + /// + /// + /// 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, true, false); + /// + /// Creates a range for the specified memory that encompasses all valid indexes. + /// + /// + /// 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, true, 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, true, false); + } + + /// + /// Provides regular expressions that are used in string assertions. + /// + internal 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 = @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\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(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); + } + + /// + /// 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, + } + + /// + /// 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); + } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class ValidatedNotNullAttribute : Attribute + { + } +} + +namespace Light.GuardClauses.Exceptions +{ + /// + /// This exception indicates that an URI is absolute instead of relative. + /// + [Serializable] + internal class AbsoluteUriException : UriException + { + /// + /// 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) + { + } + + /// + protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a value of a value type is the default value. + /// + [Serializable] + internal class ArgumentDefaultException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that the state of a collection is invalid. + /// + [Serializable] + internal class CollectionException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a collection has no items. + /// + [Serializable] + internal class EmptyCollectionException : InvalidCollectionCountException + { + /// + /// 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) + { + } + + /// + protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that that a GUID is empty. + /// + [Serializable] + internal class EmptyGuidException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// 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) + { + } + + /// + protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a value is not defined in the corresponding enum type. + /// + [Serializable] + internal class EnumValueNotDefinedException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a collection contains an item that must not be part of it. + /// + [Serializable] + internal class ExistingItemException : CollectionException + { + /// + /// 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) + { + } + + /// + protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a collection has an invalid number of items. + /// + [Serializable] + internal class InvalidCollectionCountException : CollectionException + { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public InvalidCollectionCountException(string? parameterName = null, string? message = null) : base(parameterName, message) + { + } + + /// + protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that configuration data is invalid. + /// + [Serializable] + internal class InvalidConfigurationException : Exception + { + /// + /// Initializes a new instance of . + /// + /// 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) + { + } + + /// + protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a or value is invalid. + /// + [Serializable] + internal class InvalidDateTimeException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that an Email address is invalid. + /// + [Serializable] + internal class InvalidEmailAddressException : StringException + { + /// + /// 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) + { + } + + /// + protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// 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) + { + } + + /// + protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that an URI has an invalid scheme. + /// + [Serializable] + internal class InvalidUriSchemeException : UriException + { + /// + /// Creates a new instance of . + /// + /// The name of the parameter (optional). + /// The message of the exception (optional). + public InvalidUriSchemeException(string? parameterName = null, string? message = null) : base(parameterName, message) + { + } + + /// + protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that an item is not present in a collection. + /// + [Serializable] + internal class MissingItemException : CollectionException + { + /// + /// 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) + { + } + + /// + protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a has no value. + /// + [Serializable] + internal class NullableHasNoValueException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// 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) + { + } + + /// + protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that two references point to the same object. + /// + [Serializable] + internal class SameObjectReferenceException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a string is not matching a regular expression. + /// + [Serializable] + internal class StringDoesNotMatchException : StringException + { + /// + /// 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) + { + } + + /// + protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a string is in an invalid state. + /// + [Serializable] + internal class StringException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a string has an invalid length. + /// + [Serializable] + internal class StringLengthException : StringException + { + /// + /// 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) + { + } + + /// + protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a string is in an invalid state. + /// + [Serializable] + internal class SubstringException : StringException + { + /// + /// 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) + { + } + + /// + protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// 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) + { + } + } + + /// + /// This exception indicates that an item is part of a collection. + /// + [Serializable] + internal class ValueIsOneOfException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that two values are equal. + /// + [Serializable] + internal class ValuesEqualException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that two values are not equal. + /// + [Serializable] + internal class ValuesNotEqualException : ArgumentException + { + /// + /// 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) + { + } + + /// + protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } + + /// + /// This exception indicates that a string contains only white space. + /// + [Serializable] + internal class WhiteSpaceStringException : StringException + { + /// + /// 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) + { + } + + /// + protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} + +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 + { + /// + /// 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 the default indicating that a comparable value must be greater - /// than the given boundary value, 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 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 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 two references point to the same - /// object, using the optional parameter name and message. + /// Throws the default , 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 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 date time is not using - /// , using the optional parameter name and message. + /// Throws the default indicating that a collection count is outside a range. /// [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 CollectionCountNotInRange(IEnumerable parameter, int actualCount, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have its count between {range.CreateRangeDescriptionText("and")}, but it actually has count {actualCount}."); /// - /// Throws the default indicating that a date time offset does not use - /// as its offset, using the optional parameter name and message. + /// Throws the exception that is returned by . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeUtcDateTimeOffset(DateTimeOffset parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time offset"} must use offset \"{TimeSpan.Zero}\", but it actually uses \"{parameter.Offset}\" and is \"{parameter:O}\"."); + public static void CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); /// - /// Throws the default indicating that a date time is not using - /// , using the optional parameter name and message. + /// Throws the exception that is returned by . is + /// passed to . /// [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 CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); /// - /// Throws the default indicating that a date time is not using - /// , using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [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 CustomException(Func exceptionFactory, T1 first, T2 second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a URI is relative instead of absolute, - /// using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [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 CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); /// - /// Throws the default indicating that a URI is absolute instead of relative, - /// using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [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 CustomSpanException(SpanExceptionFactory exceptionFactory, Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); /// - /// Throws the default indicating that a URI has an unexpected scheme, - /// using the optional parameter name and message. + /// Throws the exception that is returned by . is + /// passed to . /// [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 CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); /// - /// 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 exception that is returned by . and + /// are passed to . /// [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 CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); /// - /// 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 CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a string does not end with another one, - /// using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [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 CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second, T third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); /// - /// Throws the default indicating that a string is not trimmed at the end. + /// Throws the default indicating that a date time is not using + /// , using the optional parameter name and 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 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 is not shorter than the given - /// length, using the optional parameter name and message. + /// Throws the default indicating that a date time offset does not use + /// as its offset, 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}."); + public static void MustBeUtcDateTimeOffset(DateTimeOffset parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time offset"} must use offset \"{TimeSpan.Zero}\", but it actually uses \"{parameter.Offset}\" and is \"{parameter:O}\"."); /// - /// 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 date time is not using + /// , 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 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 has a different length than the - /// specified one, 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 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 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 is not longer than the given - /// length, using the optional parameter name and message. + /// Throws the default indicating that a collection has no items, 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}."); + 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 longer than or equal to - /// the given length, using the optional parameter name and message. + /// Throws the default indicating that a GUID is empty, 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 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 string's length is not in within the + /// 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 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 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 an 's length is not 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}."); + public static void ImmutableArrayLengthNotInRange(ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has length {parameter.Length}")}."); /// /// Throws the default indicating that a collection has an invalid /// number of items, using the optional parameter name and message. @@ -7919,19 +9073,36 @@ public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgument [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 an 's length is not within the - /// given range, using the optional parameter name and message. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ImmutableArrayLengthNotInRange(ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"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 value must not be approximately - /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// Throws an using the optional 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}."); + 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 an immutable array has an invalid length, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + var actualLength = parameter.IsDefault ? 0 : parameter.Length; + throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have length {length}, but it actually has length {actualLength}."); + } + + /// + /// 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 an has more than a /// maximum number of items, using the optional parameter name and message. @@ -7944,711 +9115,526 @@ public static void InvalidMaximumImmutableArrayLength(ImmutableArray param } /// - /// 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. + /// 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 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}."); + 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 an immutable array has an invalid length, - /// using the optional parameter name and message. + /// Throws the default indicating that an has less than a + /// minimum number of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static void InvalidMinimumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var actualLength = parameter.IsDefault ? 0 : parameter.Length; - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have length {length}, but it actually has length {actualLength}."); + throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at least a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); } /// - /// 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 an using the optional 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 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 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 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 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}."); + /// + /// 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 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}."); + /// + /// 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 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 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}."); + /// + /// 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 string is not a substring of another one, + /// Throws the default indicating that a numeric value must be negative, /// 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()}."); + public static void MustBeNegative(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be negative, but it actually is {parameter}."); /// - /// Throws the default indicating that a string is not a substring of another one, + /// Throws the default indicating that a numeric value must be positive, /// 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()}."); + public static void MustBePositive(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be positive, but it actually is {parameter}."); /// - /// 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 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 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()}."); + 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 string is a substring of another one, - /// using the optional parameter name and message. + /// 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 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()}."); + 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 string does start with another one, using - /// the optional parameter name and message. + /// 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 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 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 string does start with another one, 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 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()}."); + 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 an 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 Argument(string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); + 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 string does not match a regular - /// expression, using the optional parameter name and message. + /// Throws the default indicating that a numeric value must not be + /// negative, 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 MustNotBeNegative(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be negative, but it actually is {parameter}."); /// - /// Throws the default indicating that an has less than a - /// minimum number of items, using the optional parameter name and message. + /// Throws the default indicating that a numeric value must not be + /// positive, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidMinimumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at least a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); - } - + public static void MustNotBePositive(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be positive, but it actually is {parameter}."); /// - /// Throws the default indicating that a span has an invalid length, + /// Throws the default indicating that a numeric value must not be zero, /// 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}."); + public static void MustNotBeZero(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be zero, but it actually is {parameter}."); /// - /// Throws the default indicating that a span length is outside a range. + /// Throws the default indicating that a string is not a valid file extension. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SpanLengthNotInRange(ReadOnlySpan parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must have its length between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); + 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 span is not longer than the - /// specified length. + /// Throws the default indicating that a string is not a valid file extension. /// [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}."); + 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 span is not longer than and - /// not equal to the specified length. + /// Throws an indicating that a floating-point value is not finite. /// [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}."); + public static void NotFinite(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be finite, 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 string is not equal to "\n" or "\r\n". /// [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}."); + 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 span is not shorter than the - /// specified length. + /// Throws the default indicating that a string is not trimmed. /// [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}."); + 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 comparable value must not be - /// greater than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a string is not trimmed at the end. /// [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 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. + /// Throws the default indicating that a string is not trimmed at the start. /// [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()}."); + 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 a valid file extension. + /// Throws the default indicating that a has + /// no value, using the optional parameter name and message. /// [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()}."); + 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 two values are not equal, 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 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()}."); + 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 two values are equal, 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 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()}."); + 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 , using the optional parameter name and message. + /// 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 ArgumentNull(string? parameterName = null, string? message = null) => throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); + 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 collection is not containing the - /// specified item, 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 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 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 not contain another string as - /// a substring, using the optional parameter name and message. + /// Throws the default indicating that a span length is outside a range. /// [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 SpanLengthNotInRange(ReadOnlySpan parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must have its length between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); /// - /// 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 span is not longer than the + /// specified length. /// [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 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 contain another string as a - /// substring, 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 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 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 does contain another string as a - /// substring, 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 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 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 trimmed. + /// Throws the default indicating that a span is not shorter than the + /// specified length. /// [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 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 an using the optional message. + /// 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 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 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 an using the optional message. + /// 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 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()}\"."); + 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 value must be greater than or approximately - /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// 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 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}."); + 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 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 string does contain another string as a + /// substring, 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 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 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 exception that is returned by . + /// 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 CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); + 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 exception that is returned by . is - /// 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 CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); + 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 exception that is returned by . and - /// are passed to . + /// Throws the default indicating that a string ends with another one, 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 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 exception that is returned by . , - /// , and are passed to . + /// 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 CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + 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 exception that is returned by . and - /// are passed to . + /// 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 CustomSpanException(SpanExceptionFactory exceptionFactory, Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); + 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 exception that is returned by . is - /// passed to . + /// 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 CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); + 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 exception that is returned by . and - /// are passed to . + /// 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 CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + 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 exception that is returned by . and - /// are passed to . + /// 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 CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + 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 exception that is returned by . , - /// , and are passed to . + /// 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 CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second, T third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); - } - - /// - /// 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 produces an exception. - /// - internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span); - /// - /// 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); - /// - /// Represents a delegate that receives two spans and produces an exception. - /// - 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 -{ - /// - /// Provides extension methods for and to easily assembly error messages. - /// - internal static class TextExtensions - { + [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}."); /// - /// Gets the default NewLineSeparator. This value is $",{Environment.NewLine}". + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. /// - public static readonly string DefaultNewLineSeparator = ',' + Environment.NewLine; + [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()}."); /// - /// Gets the list of types that will not be surrounded by quotation marks in error messages. + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. /// - 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; - } - + [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()}."); /// - /// Returns the string representation of , or if is null. - /// If the type of is not one of , then quotation marks will be put around the string representation. + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. /// - /// The item whose string representation should be returned. - /// 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; + [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()}."); /// - /// Returns the string representation of . This is done by calling . If the type of - /// is not one of , then the resulting string will be wrapped in quotation marks. + /// Throws the default indicating that a string is not a substring of another one, + /// using the optional parameter name and message. /// - /// The value whose string representation is requested. - [ContractAnnotation("value:null => halt; value:notnull => notnull")] - 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) - { - Span span = stackalloc char[content.Length + 2]; - span[0] = span[span.Length - 1] = '"'; - content.AsSpan().CopyTo(span.Slice(1, content.Length)); - return span.ToString(); - } - - var contentWithQuotationMarks = new char[content.Length + 2]; - contentWithQuotationMarks[0] = contentWithQuotationMarks[contentWithQuotationMarks.Length - 1] = '"'; - // ReSharper restore UseIndexFromEndExpression - content.CopyTo(0, contentWithQuotationMarks, 1, content.Length); - return new string (contentWithQuotationMarks); - } - + [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()}."); /// - /// Appends the content of the collection with the specified header line to the string builder. - /// Each item is on a new line. + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. /// - /// The item type of the collection. - /// The string builder that the content is appended to. - /// The collection whose items will be appended to the string builder. - /// The string that will be placed before the actual items as a header. - /// The value indicating if a new line is added after the last item. This value defaults to true. - /// Thrown when or is null. - [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); - // ReSharper restore RedundantNullableFlowAttribute + [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()}."); /// - /// Appends the string representations of the specified items to the string builder. + /// Throws the default indicating that a string is a substring of another one, + /// using the optional parameter name and message. /// - /// The string builder where the items will be appended to. - /// The items to be appended. - /// The characters used to separate the items. Defaults to ", " and is not appended after the last item. - /// 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")] - // 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(); - 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; - } - } - + [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()}."); /// - /// Appends the string representations of the specified items to the string builder. Each item is on its own line. + /// Throws the default indicating that a URI is relative instead of absolute, + /// using the optional parameter name and message. /// - /// The string builder where the items will be appended to. - /// The items to be appended. - /// The text that is appended to the string builder when is empty. Defaults to "empty collection". - /// The value indicating if a new line is added after the last item. This value defaults to true. - /// Thrown when or is null. - [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); - // ReSharper restore RedundantNullableFlowAttribute + [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}\"."); /// - /// Appends the value to the specified string builder if the condition is true. + /// Throws the default indicating that a URI is absolute instead of relative, + /// using the optional parameter name and message. /// - /// The string builder where will be appended to. - /// 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(// ReSharper disable once RedundantNullableFlowAttribute - [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) - { - if (condition) - { - stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); - } - - return stringBuilder; - } - + [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}\"."); /// - /// Appends the value followed by a new line separator to the specified string builder if the condition is true. + /// Throws the default indicating that a URI has an unexpected scheme, + /// using the optional parameter name and message. /// - /// The string builder where will be appended to. - /// 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(// ReSharper disable once RedundantNullableFlowAttribute - [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") - { - if (condition) - { - stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); - } - - return stringBuilder; - } - + [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()); /// - /// Appends the messages of the and its nested exceptions to the - /// specified . + /// Throws the default indicating that a value is one of a specified collection + /// of items, using the optional parameter name and message. /// - /// Thrown when any parameter is null. - // 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)); - while (true) - { - // ReSharper disable once PossibleNullReferenceException - stringBuilder.AppendLine(exception.Message); - if (exception.InnerException is null) - { - return stringBuilder; - } - - stringBuilder.AppendLine(); - exception = exception.InnerException; - } - } - + [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()); /// - /// Formats all messages of the and its nested exceptions into - /// a single string. + /// Throws the default indicating that a value is not one of a specified + /// collection of items, using the optional parameter name and message. /// - /// Thrown when is null. - // ReSharper disable once RedundantNullableFlowAttribute - public static string GetAllExceptionMessages([NotNull][ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); + [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()); /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space - /// of the provided strings. + /// Throws the default indicating that two values are not equal, using the + /// optional parameter name and message. /// - 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; - 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 - // the desired result. - while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) - { - if (x[indexX++] != y[indexY++]) - { - return false; - } - } - - return wasXSuccessful == wasYSuccessful; - } - + [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()}."); /// - /// Checks if the two strings are equal using ordinal sorting rules, ignoring the case of the letters - /// as well as ignoring the white space of the provided strings. + /// Throws the default indicating that two values are equal, using the optional + /// parameter name and message. /// - 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; - 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 - // the desired result. - 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; - } - - private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, ref int currentIndex) - { - while (currentIndex < @string.Length) - { - if (!char.IsWhiteSpace(@string[currentIndex])) - { - return true; - } - - ++currentIndex; - } - - return false; - } - } - - internal static partial class StringExtensions - { + [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()}."); /// - /// Checks if the string contains the specified value using the given comparison type. + /// Throws the default indicating that a string contains only white space, + /// using the optional parameter name and message. /// - /// 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; + [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 character span contains only + /// white space, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void WhiteSpaceSpan(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The character span"} must not contain only white space, but it actually has length {parameter.Length}."); } + /// + /// 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 produces an exception. + /// + internal delegate Exception ReadOnlySpanExceptionFactory(ReadOnlySpan span); + /// + /// 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); + /// + /// Represents a delegate that receives two spans and produces an exception. + /// + 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 +{ /// /// Provides extension methods for the interface. /// @@ -8997,14 +9983,6 @@ public static FieldInfo ExtractField(// ReSharper disable once Redund } } - /// - /// Provides extension methods for the class. - /// - // ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation - internal static partial class StringExtensions - { - } - /// /// The class represents a simple non-cryptographic hash function that uses a prime number /// as seed and then manipulates this value by constantly performing hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); @@ -9151,10 +10129,73 @@ public static int CreateHashCode(T1 value1, } /// - /// Creates a hash code from the ten specified values. + /// 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) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + return hash; + } + + /// + /// 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) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + return hash; + } + + /// + /// 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) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + return hash; + } + + /// + /// Creates a hash code from the thirteen 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, T11 value11, T12 value12, T13 value13) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -9167,14 +10208,17 @@ public static int CreateHashCode(T1 val CombineIntoHash(ref hash, value8); CombineIntoHash(ref hash, value9); CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); return hash; } /// - /// Creates a hash code from the eleven specified values. + /// Creates a hash code from the fourteen 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, T12 value12, T13 value13, T14 value14) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -9188,14 +10232,17 @@ public static int CreateHashCode(T CombineIntoHash(ref hash, value9); CombineIntoHash(ref hash, value10); CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); + CombineIntoHash(ref hash, value14); return hash; } /// - /// Creates a hash code from the eleven specified values. + /// Creates a hash code from the fifteen 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, T13 value13, T14 value14, T15 value15) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -9210,14 +10257,17 @@ public static int CreateHashCode - /// Creates a hash code from the thirteen specified values. + /// Creates a hash code from the sixteen 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, 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, T14 value14, T15 value15, T16 value16) { var hash = FirstPrime; CombineIntoHash(ref hash, value1); @@ -9233,124 +10283,429 @@ public static int CreateHashCode - /// Creates a hash code from the fourteen specified values. + /// Mutates the given hash with the specified value using the following statement: + /// hash = 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); + } + + /// + /// 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 struct MultiplyAddHashBuilder + { + private int _hash; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; + /// + /// Combines the given value into the hash using the method. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiplyAddHashBuilder CombineIntoHash(T value) + { + MultiplyAddHash.CombineIntoHash(ref _hash, value); + return this; + } + + /// + /// 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(initialHash); + } + + internal static partial class StringExtensions + { + /// + /// 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; + } + + /// + /// Provides extension methods for the class. + /// + // ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation + internal static partial class StringExtensions + { + } + + /// + /// Provides extension methods for and to easily assembly error messages. + /// + internal static class TextExtensions + { + /// + /// Gets the default NewLineSeparator. This value is $",{Environment.NewLine}". + /// + public static readonly string DefaultNewLineSeparator = ',' + Environment.NewLine; + /// + /// 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), ]); + 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; + } + + /// + /// Returns the string representation of , or if is null. + /// If the type of is not one of , then quotation marks will be put around the string representation. + /// + /// The item whose string representation should be returned. + /// 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; + /// + /// Returns the string representation of . This is done by calling . If the type of + /// is not one of , then the resulting string will be wrapped in quotation marks. + /// + /// The value whose string representation is requested. + [ContractAnnotation("value:null => halt; value:notnull => notnull")] + 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) + { + Span span = stackalloc char[content.Length + 2]; + span[0] = span[span.Length - 1] = '"'; + content.AsSpan().CopyTo(span.Slice(1, content.Length)); + return span.ToString(); + } + + var contentWithQuotationMarks = new char[content.Length + 2]; + contentWithQuotationMarks[0] = contentWithQuotationMarks[contentWithQuotationMarks.Length - 1] = '"'; + // ReSharper restore UseIndexFromEndExpression + content.CopyTo(0, contentWithQuotationMarks, 1, content.Length); + return new string (contentWithQuotationMarks); + } + + /// + /// Appends the content of the collection with the specified header line to the string builder. + /// Each item is on a new line. + /// + /// The item type of the collection. + /// The string builder that the content is appended to. + /// The collection whose items will be appended to the string builder. + /// The string that will be placed before the actual items as a header. + /// The value indicating if a new line is added after the last item. This value defaults to true. + /// Thrown when or is null. + [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); + // ReSharper restore RedundantNullableFlowAttribute + /// + /// Appends the string representations of the specified items to the string builder. + /// + /// The string builder where the items will be appended to. + /// The items to be appended. + /// The characters used to separate the items. Defaults to ", " and is not appended after the last item. + /// 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")] + // 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(); + 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; + } + } + + /// + /// Appends the string representations of the specified items to the string builder. Each item is on its own line. /// + /// The string builder where the items will be appended to. + /// The items to be appended. + /// The text that is appended to the string builder when is empty. Defaults to "empty collection". + /// The value indicating if a new line is added after the last item. This value defaults to true. + /// Thrown when or is null. [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) + [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); + // 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 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(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - return hash; + if (condition) + { + stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); + } + + return stringBuilder; } /// - /// Creates a hash code from the fifteen specified values. + /// 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 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)] - 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) + [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] + public static StringBuilder AppendLineIf(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - CombineIntoHash(ref hash, value15); - return hash; + if (condition) + { + stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); + } + + return stringBuilder; } /// - /// Creates a hash code from the sixteen specified values. + /// Appends the messages of the and its nested exceptions to the + /// specified . /// - [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) + /// Thrown when any parameter is null. + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendExceptionMessages([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] Exception exception) + // ReSharper restore RedundantNullableFlowAttribute { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - CombineIntoHash(ref hash, value15); - CombineIntoHash(ref hash, value16); - return hash; + stringBuilder.MustNotBeNull(nameof(stringBuilder)); + exception.MustNotBeNull(nameof(exception)); + while (true) + { + // ReSharper disable once PossibleNullReferenceException + stringBuilder.AppendLine(exception.Message); + if (exception.InnerException is null) + { + return stringBuilder; + } + + stringBuilder.AppendLine(); + exception = exception.InnerException; + } } /// - /// Mutates the given hash with the specified value using the following statement: - /// hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. + /// Formats all messages of the and its nested exceptions into + /// a single string. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CombineIntoHash(ref int hash, T value) => hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); - } - - /// - /// 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 struct MultiplyAddHashBuilder - { - private int _hash; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; + /// Thrown when is null. + // ReSharper disable once RedundantNullableFlowAttribute + public static string GetAllExceptionMessages([NotNull][ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// - /// Combines the given value into the hash using the method. + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space + /// of the provided strings. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiplyAddHashBuilder CombineIntoHash(T value) + public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) { - MultiplyAddHash.CombineIntoHash(ref _hash, value); - return this; + 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; + 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 + // the desired result. + while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) + { + if (x[indexX++] != y[indexY++]) + { + return false; + } + } + + return wasXSuccessful == wasYSuccessful; } /// - /// Returns the calculated hash code. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int BuildHash() => _hash; - /// - /// Initializes a new instance of with the specified initial hash. + /// Checks if the two strings are equal using ordinal sorting rules, ignoring the case of the letters + /// as well as ignoring the white space of the provided strings. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new(initialHash); + 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; + 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 + // the desired result. + 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; + } + + private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, ref int currentIndex) + { + while (currentIndex < @string.Length) + { + if (!char.IsWhiteSpace(@string[currentIndex])) + { + return true; + } + + ++currentIndex; + } + + return false; + } } } diff --git a/ai-plans/0147-numeric-sign-guards.md b/ai-plans/0147-numeric-sign-guards.md new file mode 100644 index 00000000..de30edad --- /dev/null +++ b/ai-plans/0147-numeric-sign-guards.md @@ -0,0 +1,55 @@ +# Numeric Sign Guards + +## Rationale + +Guarding a numeric parameter against zero or a wrong sign is one of the most frequent preconditions in Line-of-Business code — quantities, monetary amounts, page sizes, retry counts, timeouts, and durations. Today callers must express these checks as `MustBeGreaterThan(0)` or `MustNotBeLessThan(TimeSpan.Zero)`, which works but states the boundary instead of the intent. Add dedicated sign guards that name the invariant directly: `MustBePositive`, `MustBeNegative`, `MustNotBePositive`, `MustNotBeNegative`, and `MustNotBeZero`. + +The additions must preserve the library's fluent return values, exception-factory overloads, nullable annotations, broad target-framework support, Native AOT compatibility, and the customizable single-file source distribution. + +## Acceptance Criteria + +- [x] The five throwing guard families `MustBePositive`, `MustBeNegative`, `MustNotBePositive`, `MustNotBeNegative`, and `MustNotBeZero` are available for `int`, `long`, `decimal`, `float`, `double`, and `TimeSpan` on .NET Standard 2.0, .NET Standard 2.1, and .NET 10. +- [x] The .NET 10 asset additionally provides generic overloads of all five families constrained to `INumber`, so types without concrete overloads (such as `short`, `byte`, or `Half`) are covered on the modern target. +- [x] Sign semantics are defined by comparing the value against zero with the type's comparison operators: `MustBePositive` accepts only values greater than zero, `MustBeNegative` only values less than zero, `MustNotBePositive` only values less than or equal to zero, `MustNotBeNegative` only values greater than or equal to zero, and `MustNotBeZero` only values not equal to zero. +- [x] For IEEE 754 floating-point inputs, `NaN` is rejected by all four sign guards and accepted by `MustNotBeZero`, positive and negative infinity satisfy the guards that match their sign, and negative zero — including `decimal`'s signed zero representations — is treated exactly like zero; these outcomes are identical on all targets and for both concrete and generic overloads. +- [x] A failed guard throws `ArgumentOutOfRangeException` by default and reports the violated sign requirement and the actual value. +- [x] Every new guard returns the successfully validated input, captures the guarded expression for the default exception, accepts an optional custom message, and provides an exception-factory overload consistent with the existing API. +- [x] Automated tests cover successful values, failing values, zero and negative-zero boundaries (floating-point and `decimal`), `NaN`, infinity, and subnormal handling, `TimeSpan.Zero`, default exceptions, custom messages, custom exception factories, caller argument expressions, return values, and the .NET 10 generic overloads including at least one type without a concrete overload. +- [x] The source-export whitelist catalog and committed settings contain the five new assertion families, and focused source-export tests cover the new entries. +- [x] The committed .NET Standard 2.0 single-file distribution is regenerated and validates with the new portable API surface. +- [x] The assertion overview documents the new families, including their zero, negative-zero, and `NaN` semantics and the .NET 10-only generic overloads. +- [x] The complete solution restores and builds without warnings in Release configuration, and all automated tests pass on the pinned SDK. + +## Technical Details + +Follow the `IsFinite`/`MustBeFinite` pattern: one `Check..cs` file per family containing concrete overloads for all targets, with the generic overloads added under the repository's existing modern-framework conditional (`NET8_0_OR_GREATER`). Illustrative shape of one family: + +```csharp +public static int MustBePositive( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null +); + +public static int MustBePositive(this int parameter, Func exceptionFactory); + +#if NET8_0_OR_GREATER +public static T MustBePositive( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null +) where T : INumber; +#endif +``` + +On the modern target the concrete overloads coexist with the generic ones; overload resolution prefers the concrete overload for its exact type, matching the established `IsFinite` and `IsApproximately` behavior. `TimeSpan` keeps concrete overloads on every target because it does not implement `INumber`; compare it against `TimeSpan.Zero`. + +**Semantics.** Implement every check with the type's comparison operators against zero (`T.Zero` in the generic overloads). Do not use `IComparable.CompareTo`, whose total order classifies `NaN` as less than zero and would make `MustBeNegative(double.NaN)` pass. Do not use `INumberBase.IsPositive`/`IsNegative`, which use sign-bit semantics and disagree with the operator-based definition for negative zero and `NaN`. With operators, all four sign guards naturally reject `NaN` because every IEEE 754 comparison with `NaN` is false, and `-0.0` compares equal to zero. `MustNotBeZero` uses exact equality; document that tolerance-based comparisons remain the domain of the approximation guards. Positive and negative infinity are valid inputs that satisfy the guards matching their sign — rejecting non-finite values remains the job of `MustBeFinite`, and the two guards compose (`value.MustBeFinite().MustBePositive()`). `decimal`'s signed zero representations compare equal to zero and therefore behave exactly like zero. Unsigned types are only reachable through the generic overloads and need no special handling, even though `MustBeNegative` can never succeed for them. + +**Boolean predicates are intentionally omitted.** The comparison operator itself is the predicate, and extension methods named `IsPositive`/`IsNegative` would collide with the framework's static `double.IsPositive`/`INumberBase.IsNegative`, which follow the diverging sign-bit semantics — same name with different results for `-0.0` invites subtle bugs. + +**Exceptions.** Add `Throw` helpers following the `Throw.NotFinite` convention: generic, `[DoesNotReturn]`, throwing `ArgumentOutOfRangeException` with a message of the form `"{parameterName ?? "The value"} must be positive, but it actually is {parameter}."` (adjusted per family). No new public exception types are required. + +**Portable scope.** The concrete overload set (`int`, `long`, `decimal`, `float`, `double`, `TimeSpan`) is deliberate: extension-method receivers do not apply implicit numeric conversions, so smaller integer types are simply not covered on the portable targets. This is accepted; the .NET 10 generic overloads close that gap on the modern target. + +Update `AssertionWhitelist`, `settings.json`, and the source-export whitelist tests for the five new families with their exception-factory overloads, regenerate `Light.GuardClauses.SingleFile.cs` as the .NET Standard 2.0 output, and verify both portable and .NET 10 generated-source validation. Add the families to the comparable/range section of `docs/assertion-overview.md` and list the generic `INumber` overloads under the target-specific API section. diff --git a/docs/assertion-overview.md b/docs/assertion-overview.md index 901122c9..3d570b6a 100644 --- a/docs/assertion-overview.md +++ b/docs/assertion-overview.md @@ -13,6 +13,7 @@ The package has .NET Standard 2.0, .NET Standard 2.1, and .NET 10 assets. The co The .NET 10 asset additionally provides: - generic `INumber` overloads for `IsApproximately`, `MustBeApproximately`, `MustNotBeApproximately`, `IsGreaterThanOrApproximately`, `MustBeGreaterThanOrApproximately`, `IsLessThanOrApproximately`, and `MustBeLessThanOrApproximately`; +- generic `INumber` overloads for `MustBePositive`, `MustBeNegative`, `MustNotBePositive`, `MustNotBeNegative`, and `MustNotBeZero`, covering numeric types without concrete overloads (such as `short`, `byte`, or `Half`); - generic `IFloatingPointIeee754` overloads for `IsFinite` and `MustBeFinite`, including `Half` but excluding `decimal`; - `Span`, `ReadOnlySpan`, `Memory`, and `ReadOnlyMemory` overloads for `IsEmailAddress` and `MustBeEmailAddress`; and - trimming annotations on the type-relation helpers where supported by the framework. @@ -60,6 +61,9 @@ UUIDv7 validation checks the version-7 nibble and RFC/IETF `10xx` variant bits d | `MustBeLessThan`, `MustBeLessThanOrEqualTo` | Require the value to be below a boundary | | `MustNotBeGreaterThan`, `MustNotBeGreaterThanOrEqualTo` | Reject values above or at an upper boundary | | `MustNotBeLessThan`, `MustNotBeLessThanOrEqualTo` | Reject values below or at a lower boundary | +| `MustBePositive`, `MustBeNegative` | Require a value greater than, or less than, zero | +| `MustNotBePositive`, `MustNotBeNegative` | Require a value less than or equal to, or greater than or equal to, zero | +| `MustNotBeZero` | Rejects a value that compares equal to zero | | `IsIn`, `MustBeIn` | Test or require membership in a `Range` | | `IsNotIn`, `MustNotBeIn` | Test or require non-membership in a `Range` | | `IsApproximately`, `MustBeApproximately`, `MustNotBeApproximately` | Compare floating-point values using a tolerance | @@ -67,6 +71,8 @@ UUIDv7 validation checks the version-7 nibble and RFC/IETF `10xx` variant bits d | `IsGreaterThanOrApproximately`, `MustBeGreaterThanOrApproximately` | Accept values greater than or within tolerance of the comparison value | | `IsLessThanOrApproximately`, `MustBeLessThanOrApproximately` | Accept values less than or within tolerance of the comparison value | +The five sign guard families have concrete overloads for `int`, `long`, `decimal`, `float`, `double`, and `TimeSpan` on all package targets; the .NET 10 asset adds the generic `INumber` overloads listed above. All checks compare the value against zero with the type's comparison operators. Consequently, `NaN` is rejected by the four sign guards and accepted by `MustNotBeZero`, positive and negative infinity satisfy the guards matching their sign (compose with `MustBeFinite` to reject non-finite values), and negative zero — including `decimal`'s signed zero representations — behaves exactly like zero. `MustNotBeZero` uses exact equality; tolerance-based comparisons remain the domain of the approximation guards. + Create ranges with the `Range` fluent API: ```csharp diff --git a/src/Light.GuardClauses/Check.MustBeNegative.cs b/src/Light.GuardClauses/Check.MustBeNegative.cs new file mode 100644 index 00000000..b813eb1a --- /dev/null +++ b/src/Light.GuardClauses/Check.MustBeNegative.cs @@ -0,0 +1,353 @@ +using System; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustBeNegative( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustBeNegative(this int parameter, Func exceptionFactory) + { + if (!(parameter < 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustBeNegative( + this long parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0L)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustBeNegative(this long parameter, Func exceptionFactory) + { + if (!(parameter < 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustBeNegative( + this decimal parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0m)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustBeNegative(this decimal parameter, Func exceptionFactory) + { + if (!(parameter < 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 zero (including negative zero), positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeNegative( + this float parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0f)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustBeNegative(this float parameter, Func exceptionFactory) + { + if (!(parameter < 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 zero (including negative zero), positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBeNegative( + this double parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0d)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustBeNegative(this double parameter, Func exceptionFactory) + { + if (!(parameter < 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// 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 or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustBeNegative( + this TimeSpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < TimeSpan.Zero)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustBeNegative(this TimeSpan parameter, Func exceptionFactory) + { + if (!(parameter < TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + +#if NET8_0_OR_GREATER + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// 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 zero, positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBeNegative( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!(parameter < T.Zero)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is zero, positive, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBeNegative(this T parameter, Func exceptionFactory) + where T : INumber + { + if (!(parameter < T.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +#endif +} diff --git a/src/Light.GuardClauses/Check.MustBePositive.cs b/src/Light.GuardClauses/Check.MustBePositive.cs new file mode 100644 index 00000000..0efd376e --- /dev/null +++ b/src/Light.GuardClauses/Check.MustBePositive.cs @@ -0,0 +1,353 @@ +using System; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustBePositive( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > 0)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustBePositive(this int parameter, Func exceptionFactory) + { + if (!(parameter > 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustBePositive( + this long parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > 0L)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustBePositive(this long parameter, Func exceptionFactory) + { + if (!(parameter > 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustBePositive( + this decimal parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > 0m)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustBePositive(this decimal parameter, Func exceptionFactory) + { + if (!(parameter > 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 zero (including negative zero), negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBePositive( + this float parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > 0f)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustBePositive(this float parameter, Func exceptionFactory) + { + if (!(parameter > 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 zero (including negative zero), negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustBePositive( + this double parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > 0d)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero (including negative zero), negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustBePositive(this double parameter, Func exceptionFactory) + { + if (!(parameter > 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// 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 or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustBePositive( + this TimeSpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter > TimeSpan.Zero)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is or negative. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustBePositive(this TimeSpan parameter, Func exceptionFactory) + { + if (!(parameter > TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + +#if NET8_0_OR_GREATER + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// 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 zero, negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustBePositive( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!(parameter > T.Zero)) + { + Throw.MustBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is positive (greater than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is zero, negative, or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBePositive(this T parameter, Func exceptionFactory) + where T : INumber + { + if (!(parameter > T.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +#endif +} diff --git a/src/Light.GuardClauses/Check.MustNotBeNegative.cs b/src/Light.GuardClauses/Check.MustNotBeNegative.cs new file mode 100644 index 00000000..c1f941e8 --- /dev/null +++ b/src/Light.GuardClauses/Check.MustNotBeNegative.cs @@ -0,0 +1,353 @@ +using System; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustNotBeNegative( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBeNegative(this int parameter, Func exceptionFactory) + { + if (!(parameter >= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBeNegative( + this long parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0L)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBeNegative(this long parameter, Func exceptionFactory) + { + if (!(parameter >= 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBeNegative( + this decimal parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0m)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBeNegative(this decimal parameter, Func exceptionFactory) + { + if (!(parameter >= 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeNegative( + this float parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0f)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBeNegative(this float parameter, Func exceptionFactory) + { + if (!(parameter >= 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeNegative( + this double parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0d)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBeNegative(this double parameter, Func exceptionFactory) + { + if (!(parameter >= 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// 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 less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBeNegative( + this TimeSpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= TimeSpan.Zero)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBeNegative(this TimeSpan parameter, Func exceptionFactory) + { + if (!(parameter >= TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + +#if NET8_0_OR_GREATER + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// 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 less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBeNegative( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!(parameter >= T.Zero)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is less than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustNotBeNegative(this T parameter, Func exceptionFactory) + where T : INumber + { + if (!(parameter >= T.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +#endif +} diff --git a/src/Light.GuardClauses/Check.MustNotBePositive.cs b/src/Light.GuardClauses/Check.MustNotBePositive.cs new file mode 100644 index 00000000..e0e356d0 --- /dev/null +++ b/src/Light.GuardClauses/Check.MustNotBePositive.cs @@ -0,0 +1,353 @@ +using System; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustNotBePositive( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBePositive(this int parameter, Func exceptionFactory) + { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBePositive( + this long parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0L)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBePositive(this long parameter, Func exceptionFactory) + { + if (!(parameter <= 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBePositive( + this decimal parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0m)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBePositive(this decimal parameter, Func exceptionFactory) + { + if (!(parameter <= 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBePositive( + this float parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0f)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBePositive(this float parameter, Func exceptionFactory) + { + if (!(parameter <= 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBePositive( + this double parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0d)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBePositive(this double parameter, Func exceptionFactory) + { + if (!(parameter <= 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// 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 greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBePositive( + this TimeSpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBePositive(this TimeSpan parameter, Func exceptionFactory) + { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + +#if NET8_0_OR_GREATER + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// 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 greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBePositive( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (!(parameter <= T.Zero)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustNotBePositive(this T parameter, Func exceptionFactory) + where T : INumber + { + if (!(parameter <= T.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +#endif +} diff --git a/src/Light.GuardClauses/Check.MustNotBeZero.cs b/src/Light.GuardClauses/Check.MustNotBeZero.cs new file mode 100644 index 00000000..3e6ac565 --- /dev/null +++ b/src/Light.GuardClauses/Check.MustNotBeZero.cs @@ -0,0 +1,353 @@ +using System; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif +using System.Runtime.CompilerServices; +using JetBrains.Annotations; +using Light.GuardClauses.ExceptionFactory; + +namespace Light.GuardClauses; + +public static partial class Check +{ + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MustNotBeZero( + this int parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static int MustNotBeZero(this int parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long MustNotBeZero( + this long parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0L) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBeZero(this long parameter, Func exceptionFactory) + { + if (parameter == 0L) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// 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 zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBeZero( + this decimal parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0m) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBeZero(this decimal parameter, Func exceptionFactory) + { + if (parameter == 0m) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeZero( + this float parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0f) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBeZero(this float parameter, Func exceptionFactory) + { + if (parameter == 0f) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeZero( + this double parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0d) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when compares equal to zero (including negative zero). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBeZero(this double parameter, Func exceptionFactory) + { + if (parameter == 0d) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// 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 . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBeZero( + this TimeSpan parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == TimeSpan.Zero) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBeZero(this TimeSpan parameter, Func exceptionFactory) + { + if (parameter == TimeSpan.Zero) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + +#if NET8_0_OR_GREATER + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// 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 compares equal to zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBeZero( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) where T : INumber + { + if (parameter == T.Zero) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// The type that implements the interface. + /// + /// Your custom exception thrown when compares equal to zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustNotBeZero(this T parameter, Func exceptionFactory) + where T : INumber + { + if (parameter == T.Zero) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } +#endif +} diff --git a/src/Light.GuardClauses/ExceptionFactory/Throw.MustBeNegative.cs b/src/Light.GuardClauses/ExceptionFactory/Throw.MustBeNegative.cs new file mode 100644 index 00000000..606aaaad --- /dev/null +++ b/src/Light.GuardClauses/ExceptionFactory/Throw.MustBeNegative.cs @@ -0,0 +1,25 @@ +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 numeric value must be negative, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeNegative( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be negative, but it actually is {parameter}." + ); +} diff --git a/src/Light.GuardClauses/ExceptionFactory/Throw.MustBePositive.cs b/src/Light.GuardClauses/ExceptionFactory/Throw.MustBePositive.cs new file mode 100644 index 00000000..0399b2b1 --- /dev/null +++ b/src/Light.GuardClauses/ExceptionFactory/Throw.MustBePositive.cs @@ -0,0 +1,25 @@ +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 numeric value must be positive, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBePositive( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must be positive, but it actually is {parameter}." + ); +} diff --git a/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeNegative.cs b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeNegative.cs new file mode 100644 index 00000000..54250241 --- /dev/null +++ b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeNegative.cs @@ -0,0 +1,25 @@ +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 numeric value must not be + /// negative, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeNegative( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must not be negative, but it actually is {parameter}." + ); +} diff --git a/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBePositive.cs b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBePositive.cs new file mode 100644 index 00000000..2b8d2d5f --- /dev/null +++ b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBePositive.cs @@ -0,0 +1,25 @@ +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 numeric value must not be + /// positive, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBePositive( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must not be positive, but it actually is {parameter}." + ); +} diff --git a/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeZero.cs b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeZero.cs new file mode 100644 index 00000000..2461eac7 --- /dev/null +++ b/src/Light.GuardClauses/ExceptionFactory/Throw.MustNotBeZero.cs @@ -0,0 +1,25 @@ +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 numeric value must not be zero, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeZero( + T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) => + throw new ArgumentOutOfRangeException( + parameterName, + message ?? $"{parameterName ?? "The value"} must not be zero, but it actually is {parameter}." + ); +} diff --git a/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs b/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs index 59a303f6..ace322ed 100644 --- a/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs +++ b/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs @@ -193,6 +193,45 @@ public static void FiniteWhitelistUsesTargetSpecificSurface() File.ReadAllText(modernFile).Should().Contain("IFloatingPointIeee754"); } + [Fact] + public static void SignGuardWhitelistsUseTargetSpecificSurface() + { + using var temporaryDirectory = new TemporaryDirectory(); + var portableFile = Path.Combine(temporaryDirectory.DirectoryPath, "SignGuardsPortable.cs"); + var modernFile = Path.Combine(temporaryDirectory.DirectoryPath, "SignGuardsModern.cs"); + var whitelist = CreateWhitelist( + includedAssertions: + [ + new ("MustBePositive", true), + new ("MustBeNegative", true), + new ("MustNotBePositive", true), + new ("MustNotBeNegative", true), + new ("MustNotBeZero", false), + ] + ); + + SourceFileMerger.CreateSingleSourceFile(CreateOptions(portableFile, whitelist)); + SourceFileMerger.CreateSingleSourceFile( + CreateOptions(modernFile, whitelist, SourceTargetFramework.Net10_0) + ); + var portableCode = File.ReadAllText(portableFile); + var modernCode = File.ReadAllText(modernFile); + + portableCode.Should().Contain("public static int MustBePositive("); + portableCode.Should().Contain("public static decimal MustBeNegative("); + portableCode.Should().Contain("public static TimeSpan MustNotBeNegative("); + portableCode.Should().Contain("public static double MustNotBeZero("); + portableCode.Should().Contain("MustBePositive(this int parameter, Func exceptionFactory)"); + portableCode.Should().NotContain("MustNotBeZero(this int parameter, Func exceptionFactory)"); + portableCode.Should().NotContain("INumber"); + modernCode.Should().Contain("MustBePositive"); + modernCode.Should().Contain("MustBeNegative"); + modernCode.Should().Contain("MustNotBePositive"); + modernCode.Should().Contain("MustNotBeNegative"); + modernCode.Should().Contain("MustNotBeZero"); + modernCode.Should().Contain("INumber"); + } + private static SourceFileMergeOptions CreateOptions( string targetFile, AssertionWhitelist assertionWhitelist = null, diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs new file mode 100644 index 00000000..f083f757 --- /dev/null +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs @@ -0,0 +1,180 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustBeNegativeTests +{ + [Theory] + [InlineData(-1)] + [InlineData(-42)] + [InlineData(int.MinValue)] + public static void NegativeInt32sAreAccepted(int value) => value.MustBeNegative().Should().Be(value); + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(int.MaxValue)] + public static void NonNegativeInt32sAreRejected(int value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } + + [Theory] + [InlineData(-1L)] + [InlineData(long.MinValue)] + public static void NegativeInt64sAreAccepted(long value) => value.MustBeNegative().Should().Be(value); + + [Theory] + [InlineData(0L)] + [InlineData(1L)] + [InlineData(long.MaxValue)] + public static void NonNegativeInt64sAreRejected(long value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } + + [Fact] + public static void NegativeDecimalsAreAccepted() + { + (-0.00001m).MustBeNegative().Should().Be(-0.00001m); + decimal.MinValue.MustBeNegative().Should().Be(decimal.MinValue); + } + + [Fact] + public static void NonNegativeDecimalsAreRejected() + { + CheckDecimalIsRejected(0m); + CheckDecimalIsRejected(0.5m); + CheckDecimalIsRejected(decimal.MaxValue); + } + + [Theory] + [InlineData(-float.Epsilon)] + [InlineData(-1f)] + [InlineData(float.MinValue)] + [InlineData(float.NegativeInfinity)] + public static void NegativeFloatsAreAccepted(float value) => value.MustBeNegative().Should().Be(value); + + [Theory] + [InlineData(0f)] + [InlineData(float.Epsilon)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NaN)] + public static void NonNegativeFloatsAreRejected(float value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } + + [Theory] + [InlineData(-double.Epsilon)] + [InlineData(-1d)] + [InlineData(double.MinValue)] + [InlineData(double.NegativeInfinity)] + public static void NegativeDoublesAreAccepted(double value) => value.MustBeNegative().Should().Be(value); + + [Theory] + [InlineData(0d)] + [InlineData(double.Epsilon)] + [InlineData(double.PositiveInfinity)] + [InlineData(double.NaN)] + public static void NonNegativeDoublesAreRejected(double value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } + + [Fact] + public static void NegativeZerosAreRejectedLikeZero() + { + var negativeZeroFloat = () => (-0f).MustBeNegative(); + var negativeZeroDouble = () => (-0d).MustBeNegative(); + negativeZeroFloat.Should().Throw(); + negativeZeroDouble.Should().Throw(); + + CheckDecimalIsRejected(new decimal(0, 0, 0, true, 0)); + CheckDecimalIsRejected(0.000m); + } + + [Fact] + public static void NegativeTimeSpansAreAccepted() + { + TimeSpan.FromTicks(-1).MustBeNegative().Should().Be(TimeSpan.FromTicks(-1)); + TimeSpan.MinValue.MustBeNegative().Should().Be(TimeSpan.MinValue); + } + + [Fact] + public static void NonNegativeTimeSpansAreRejected() + { + CheckTimeSpanIsRejected(TimeSpan.Zero); + CheckTimeSpanIsRejected(TimeSpan.FromTicks(1)); + CheckTimeSpanIsRejected(TimeSpan.MaxValue); + } + + [Fact] + public static void DefaultExceptionCapturesExpressionAndValue() + { + const int invalidValue = 7; + + var act = () => invalidValue.MustBeNegative(); + + act.Should().Throw() + .WithParameterName(nameof(invalidValue)) + .WithMessage("*must be negative, but it actually is 7*"); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => 1.MustBeNegative(message: message)); + + [Fact] + public static void CustomFactoriesReceiveValues() + { + Test.CustomException(0, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException(1L, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException(0m, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException(float.NaN, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException(double.PositiveInfinity, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException(TimeSpan.Zero, (value, factory) => value.MustBeNegative(factory)); + } + +#if NET8_0_OR_GREATER + [Fact] + public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + { + ((short) -5).MustBeNegative().Should().Be((short) -5); + ((Half) (-1.5f)).MustBeNegative().Should().Be((Half) (-1.5f)); + + var zeroShort = () => ((short) 0).MustBeNegative(); + var unsignedByte = () => ((byte) 3).MustBeNegative(); + var nanHalf = () => Half.NaN.MustBeNegative(); + zeroShort.Should().Throw().WithMessage("*must be negative*"); + unsignedByte.Should().Throw().WithMessage("*must be negative*"); + nanHalf.Should().Throw().WithMessage("*must be negative*"); + + Test.CustomException((short) 3, (value, factory) => value.MustBeNegative(factory)); + } +#endif + + private static void CheckDecimalIsRejected(decimal value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } + + private static void CheckTimeSpanIsRejected(TimeSpan value) + { + var act = () => value.MustBeNegative(); + + act.Should().Throw().WithMessage("*must be negative*"); + } +} diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBePositiveTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBePositiveTests.cs new file mode 100644 index 00000000..88ea1372 --- /dev/null +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBePositiveTests.cs @@ -0,0 +1,179 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustBePositiveTests +{ + [Theory] + [InlineData(1)] + [InlineData(42)] + [InlineData(int.MaxValue)] + public static void PositiveInt32sAreAccepted(int value) => value.MustBePositive().Should().Be(value); + + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(int.MinValue)] + public static void NonPositiveInt32sAreRejected(int value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } + + [Theory] + [InlineData(1L)] + [InlineData(long.MaxValue)] + public static void PositiveInt64sAreAccepted(long value) => value.MustBePositive().Should().Be(value); + + [Theory] + [InlineData(0L)] + [InlineData(-1L)] + [InlineData(long.MinValue)] + public static void NonPositiveInt64sAreRejected(long value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } + + [Fact] + public static void PositiveDecimalsAreAccepted() + { + 0.00001m.MustBePositive().Should().Be(0.00001m); + decimal.MaxValue.MustBePositive().Should().Be(decimal.MaxValue); + } + + [Fact] + public static void NonPositiveDecimalsAreRejected() + { + CheckDecimalIsRejected(0m); + CheckDecimalIsRejected(-0.5m); + CheckDecimalIsRejected(decimal.MinValue); + } + + [Theory] + [InlineData(float.Epsilon)] + [InlineData(1f)] + [InlineData(float.MaxValue)] + [InlineData(float.PositiveInfinity)] + public static void PositiveFloatsAreAccepted(float value) => value.MustBePositive().Should().Be(value); + + [Theory] + [InlineData(0f)] + [InlineData(-float.Epsilon)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.NaN)] + public static void NonPositiveFloatsAreRejected(float value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } + + [Theory] + [InlineData(double.Epsilon)] + [InlineData(1d)] + [InlineData(double.MaxValue)] + [InlineData(double.PositiveInfinity)] + public static void PositiveDoublesAreAccepted(double value) => value.MustBePositive().Should().Be(value); + + [Theory] + [InlineData(0d)] + [InlineData(-double.Epsilon)] + [InlineData(double.NegativeInfinity)] + [InlineData(double.NaN)] + public static void NonPositiveDoublesAreRejected(double value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } + + [Fact] + public static void NegativeZerosAreRejectedLikeZero() + { + var negativeZeroFloat = () => (-0f).MustBePositive(); + var negativeZeroDouble = () => (-0d).MustBePositive(); + negativeZeroFloat.Should().Throw(); + negativeZeroDouble.Should().Throw(); + + CheckDecimalIsRejected(new decimal(0, 0, 0, true, 0)); + CheckDecimalIsRejected(0.000m); + } + + [Fact] + public static void PositiveTimeSpansAreAccepted() + { + TimeSpan.FromTicks(1).MustBePositive().Should().Be(TimeSpan.FromTicks(1)); + TimeSpan.MaxValue.MustBePositive().Should().Be(TimeSpan.MaxValue); + } + + [Fact] + public static void NonPositiveTimeSpansAreRejected() + { + CheckTimeSpanIsRejected(TimeSpan.Zero); + CheckTimeSpanIsRejected(TimeSpan.FromTicks(-1)); + CheckTimeSpanIsRejected(TimeSpan.MinValue); + } + + [Fact] + public static void DefaultExceptionCapturesExpressionAndValue() + { + const int invalidValue = -5; + + var act = () => invalidValue.MustBePositive(); + + act.Should().Throw() + .WithParameterName(nameof(invalidValue)) + .WithMessage("*must be positive, but it actually is -5*"); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => (-1).MustBePositive(message: message)); + + [Fact] + public static void CustomFactoriesReceiveValues() + { + Test.CustomException(0, (value, factory) => value.MustBePositive(factory)); + Test.CustomException(-1L, (value, factory) => value.MustBePositive(factory)); + Test.CustomException(0m, (value, factory) => value.MustBePositive(factory)); + Test.CustomException(float.NaN, (value, factory) => value.MustBePositive(factory)); + Test.CustomException(double.NegativeInfinity, (value, factory) => value.MustBePositive(factory)); + Test.CustomException(TimeSpan.Zero, (value, factory) => value.MustBePositive(factory)); + } + +#if NET8_0_OR_GREATER + [Fact] + public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + { + ((short) 5).MustBePositive().Should().Be((short) 5); + ((byte) 3).MustBePositive().Should().Be((byte) 3); + ((Half) 1.5f).MustBePositive().Should().Be((Half) 1.5f); + + var zeroShort = () => ((short) 0).MustBePositive(); + var nanHalf = () => Half.NaN.MustBePositive(); + zeroShort.Should().Throw().WithMessage("*must be positive*"); + nanHalf.Should().Throw().WithMessage("*must be positive*"); + + Test.CustomException((short) -3, (value, factory) => value.MustBePositive(factory)); + } +#endif + + private static void CheckDecimalIsRejected(decimal value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } + + private static void CheckTimeSpanIsRejected(TimeSpan value) + { + var act = () => value.MustBePositive(); + + act.Should().Throw().WithMessage("*must be positive*"); + } +} diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs new file mode 100644 index 00000000..4b191e42 --- /dev/null +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs @@ -0,0 +1,174 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustNotBeNegativeTests +{ + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(int.MaxValue)] + public static void NonNegativeInt32sAreAccepted(int value) => value.MustNotBeNegative().Should().Be(value); + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public static void NegativeInt32sAreRejected(int value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } + + [Theory] + [InlineData(0L)] + [InlineData(1L)] + [InlineData(long.MaxValue)] + public static void NonNegativeInt64sAreAccepted(long value) => value.MustNotBeNegative().Should().Be(value); + + [Theory] + [InlineData(-1L)] + [InlineData(long.MinValue)] + public static void NegativeInt64sAreRejected(long value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } + + [Fact] + public static void NonNegativeDecimalsAreAccepted() + { + 0m.MustNotBeNegative().Should().Be(0m); + 0.5m.MustNotBeNegative().Should().Be(0.5m); + decimal.MaxValue.MustNotBeNegative().Should().Be(decimal.MaxValue); + } + + [Fact] + public static void NegativeDecimalsAreRejected() + { + CheckDecimalIsRejected(-0.00001m); + CheckDecimalIsRejected(decimal.MinValue); + } + + [Theory] + [InlineData(0f)] + [InlineData(float.Epsilon)] + [InlineData(float.MaxValue)] + [InlineData(float.PositiveInfinity)] + public static void NonNegativeFloatsAreAccepted(float value) => value.MustNotBeNegative().Should().Be(value); + + [Theory] + [InlineData(-float.Epsilon)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.NaN)] + public static void NegativeFloatsAreRejected(float value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } + + [Theory] + [InlineData(0d)] + [InlineData(double.Epsilon)] + [InlineData(double.MaxValue)] + [InlineData(double.PositiveInfinity)] + public static void NonNegativeDoublesAreAccepted(double value) => value.MustNotBeNegative().Should().Be(value); + + [Theory] + [InlineData(-double.Epsilon)] + [InlineData(double.NegativeInfinity)] + [InlineData(double.NaN)] + public static void NegativeDoublesAreRejected(double value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } + + [Fact] + public static void NegativeZerosAreAcceptedLikeZero() + { + (-0f).MustNotBeNegative().Should().Be(-0f); + (-0d).MustNotBeNegative().Should().Be(-0d); + new decimal(0, 0, 0, true, 0).MustNotBeNegative().Should().Be(0m); + 0.000m.MustNotBeNegative().Should().Be(0m); + } + + [Fact] + public static void NonNegativeTimeSpansAreAccepted() + { + TimeSpan.Zero.MustNotBeNegative().Should().Be(TimeSpan.Zero); + TimeSpan.FromTicks(1).MustNotBeNegative().Should().Be(TimeSpan.FromTicks(1)); + TimeSpan.MaxValue.MustNotBeNegative().Should().Be(TimeSpan.MaxValue); + } + + [Fact] + public static void NegativeTimeSpansAreRejected() + { + CheckTimeSpanIsRejected(TimeSpan.FromTicks(-1)); + CheckTimeSpanIsRejected(TimeSpan.MinValue); + } + + [Fact] + public static void DefaultExceptionCapturesExpressionAndValue() + { + const int invalidValue = -3; + + var act = () => invalidValue.MustNotBeNegative(); + + act.Should().Throw() + .WithParameterName(nameof(invalidValue)) + .WithMessage("*must not be negative, but it actually is -3*"); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => (-1).MustNotBeNegative(message: message)); + + [Fact] + public static void CustomFactoriesReceiveValues() + { + Test.CustomException(-1, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException(-1L, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException(-0.5m, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException(float.NaN, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException(double.NegativeInfinity, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException(TimeSpan.FromTicks(-1), (value, factory) => value.MustNotBeNegative(factory)); + } + +#if NET8_0_OR_GREATER + [Fact] + public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + { + ((short) 0).MustNotBeNegative().Should().Be((short) 0); + ((short) 5).MustNotBeNegative().Should().Be((short) 5); + ((byte) 3).MustNotBeNegative().Should().Be((byte) 3); + Half.Zero.MustNotBeNegative().Should().Be(Half.Zero); + + var negativeShort = () => ((short) -3).MustNotBeNegative(); + var nanHalf = () => Half.NaN.MustNotBeNegative(); + negativeShort.Should().Throw().WithMessage("*must not be negative*"); + nanHalf.Should().Throw().WithMessage("*must not be negative*"); + + Test.CustomException((short) -3, (value, factory) => value.MustNotBeNegative(factory)); + } +#endif + + private static void CheckDecimalIsRejected(decimal value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } + + private static void CheckTimeSpanIsRejected(TimeSpan value) + { + var act = () => value.MustNotBeNegative(); + + act.Should().Throw().WithMessage("*must not be negative*"); + } +} diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs new file mode 100644 index 00000000..f2e60d84 --- /dev/null +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs @@ -0,0 +1,174 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustNotBePositiveTests +{ + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(int.MinValue)] + public static void NonPositiveInt32sAreAccepted(int value) => value.MustNotBePositive().Should().Be(value); + + [Theory] + [InlineData(1)] + [InlineData(int.MaxValue)] + public static void PositiveInt32sAreRejected(int value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } + + [Theory] + [InlineData(0L)] + [InlineData(-1L)] + [InlineData(long.MinValue)] + public static void NonPositiveInt64sAreAccepted(long value) => value.MustNotBePositive().Should().Be(value); + + [Theory] + [InlineData(1L)] + [InlineData(long.MaxValue)] + public static void PositiveInt64sAreRejected(long value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } + + [Fact] + public static void NonPositiveDecimalsAreAccepted() + { + 0m.MustNotBePositive().Should().Be(0m); + (-0.5m).MustNotBePositive().Should().Be(-0.5m); + decimal.MinValue.MustNotBePositive().Should().Be(decimal.MinValue); + } + + [Fact] + public static void PositiveDecimalsAreRejected() + { + CheckDecimalIsRejected(0.00001m); + CheckDecimalIsRejected(decimal.MaxValue); + } + + [Theory] + [InlineData(0f)] + [InlineData(-float.Epsilon)] + [InlineData(float.MinValue)] + [InlineData(float.NegativeInfinity)] + public static void NonPositiveFloatsAreAccepted(float value) => value.MustNotBePositive().Should().Be(value); + + [Theory] + [InlineData(float.Epsilon)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NaN)] + public static void PositiveFloatsAreRejected(float value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } + + [Theory] + [InlineData(0d)] + [InlineData(-double.Epsilon)] + [InlineData(double.MinValue)] + [InlineData(double.NegativeInfinity)] + public static void NonPositiveDoublesAreAccepted(double value) => value.MustNotBePositive().Should().Be(value); + + [Theory] + [InlineData(double.Epsilon)] + [InlineData(double.PositiveInfinity)] + [InlineData(double.NaN)] + public static void PositiveDoublesAreRejected(double value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } + + [Fact] + public static void NegativeZerosAreAcceptedLikeZero() + { + (-0f).MustNotBePositive().Should().Be(-0f); + (-0d).MustNotBePositive().Should().Be(-0d); + new decimal(0, 0, 0, true, 0).MustNotBePositive().Should().Be(0m); + 0.000m.MustNotBePositive().Should().Be(0m); + } + + [Fact] + public static void NonPositiveTimeSpansAreAccepted() + { + TimeSpan.Zero.MustNotBePositive().Should().Be(TimeSpan.Zero); + TimeSpan.FromTicks(-1).MustNotBePositive().Should().Be(TimeSpan.FromTicks(-1)); + TimeSpan.MinValue.MustNotBePositive().Should().Be(TimeSpan.MinValue); + } + + [Fact] + public static void PositiveTimeSpansAreRejected() + { + CheckTimeSpanIsRejected(TimeSpan.FromTicks(1)); + CheckTimeSpanIsRejected(TimeSpan.MaxValue); + } + + [Fact] + public static void DefaultExceptionCapturesExpressionAndValue() + { + const int invalidValue = 5; + + var act = () => invalidValue.MustNotBePositive(); + + act.Should().Throw() + .WithParameterName(nameof(invalidValue)) + .WithMessage("*must not be positive, but it actually is 5*"); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => 1.MustNotBePositive(message: message)); + + [Fact] + public static void CustomFactoriesReceiveValues() + { + Test.CustomException(1, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(1L, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(0.5m, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(float.NaN, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(double.PositiveInfinity, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(TimeSpan.FromTicks(1), (value, factory) => value.MustNotBePositive(factory)); + } + +#if NET8_0_OR_GREATER + [Fact] + public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + { + ((short) 0).MustNotBePositive().Should().Be((short) 0); + ((short) -5).MustNotBePositive().Should().Be((short) -5); + ((byte) 0).MustNotBePositive().Should().Be((byte) 0); + Half.Zero.MustNotBePositive().Should().Be(Half.Zero); + + var positiveShort = () => ((short) 3).MustNotBePositive(); + var nanHalf = () => Half.NaN.MustNotBePositive(); + positiveShort.Should().Throw().WithMessage("*must not be positive*"); + nanHalf.Should().Throw().WithMessage("*must not be positive*"); + + Test.CustomException((short) 3, (value, factory) => value.MustNotBePositive(factory)); + } +#endif + + private static void CheckDecimalIsRejected(decimal value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } + + private static void CheckTimeSpanIsRejected(TimeSpan value) + { + var act = () => value.MustNotBePositive(); + + act.Should().Throw().WithMessage("*must not be positive*"); + } +} diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs new file mode 100644 index 00000000..08d8f328 --- /dev/null +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs @@ -0,0 +1,169 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Light.GuardClauses.Tests.ComparableAssertions; + +public static class MustNotBeZeroTests +{ + [Theory] + [InlineData(1)] + [InlineData(-1)] + [InlineData(int.MinValue)] + [InlineData(int.MaxValue)] + public static void NonZeroInt32sAreAccepted(int value) => value.MustNotBeZero().Should().Be(value); + + [Theory] + [InlineData(1L)] + [InlineData(-1L)] + [InlineData(long.MinValue)] + [InlineData(long.MaxValue)] + public static void NonZeroInt64sAreAccepted(long value) => value.MustNotBeZero().Should().Be(value); + + [Fact] + public static void NonZeroDecimalsAreAccepted() + { + 0.5m.MustNotBeZero().Should().Be(0.5m); + (-0.5m).MustNotBeZero().Should().Be(-0.5m); + decimal.MinValue.MustNotBeZero().Should().Be(decimal.MinValue); + decimal.MaxValue.MustNotBeZero().Should().Be(decimal.MaxValue); + } + + [Theory] + [InlineData(float.Epsilon)] + [InlineData(-float.Epsilon)] + [InlineData(1f)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public static void NonZeroFloatsAreAccepted(float value) => value.MustNotBeZero().Should().Be(value); + + [Theory] + [InlineData(double.Epsilon)] + [InlineData(-double.Epsilon)] + [InlineData(-1d)] + [InlineData(double.PositiveInfinity)] + [InlineData(double.NegativeInfinity)] + public static void NonZeroDoublesAreAccepted(double value) => value.MustNotBeZero().Should().Be(value); + + [Fact] + public static void NaNIsAccepted() + { + float.NaN.MustNotBeZero().Should().Be(float.NaN); + double.NaN.MustNotBeZero().Should().Be(double.NaN); + } + + [Fact] + public static void ZerosAreRejected() + { + CheckInt32IsRejected(0); + CheckInt64IsRejected(0L); + CheckDecimalIsRejected(0m); + CheckFloatIsRejected(0f); + CheckDoubleIsRejected(0d); + CheckTimeSpanIsRejected(TimeSpan.Zero); + } + + [Fact] + public static void NegativeZerosAreRejectedLikeZero() + { + CheckFloatIsRejected(-0f); + CheckDoubleIsRejected(-0d); + CheckDecimalIsRejected(new decimal(0, 0, 0, true, 0)); + CheckDecimalIsRejected(0.000m); + } + + [Fact] + public static void NonZeroTimeSpansAreAccepted() + { + TimeSpan.FromTicks(1).MustNotBeZero().Should().Be(TimeSpan.FromTicks(1)); + TimeSpan.FromTicks(-1).MustNotBeZero().Should().Be(TimeSpan.FromTicks(-1)); + } + + [Fact] + public static void DefaultExceptionCapturesExpressionAndValue() + { + const int invalidValue = 0; + + var act = () => invalidValue.MustNotBeZero(); + + act.Should().Throw() + .WithParameterName(nameof(invalidValue)) + .WithMessage("*must not be zero, but it actually is 0*"); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => 0.MustNotBeZero(message: message)); + + [Fact] + public static void CustomFactoriesReceiveValues() + { + Test.CustomException(0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0L, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0m, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0f, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0d, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(TimeSpan.Zero, (value, factory) => value.MustNotBeZero(factory)); + } + +#if NET8_0_OR_GREATER + [Fact] + public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + { + ((short) 5).MustNotBeZero().Should().Be((short) 5); + ((byte) 3).MustNotBeZero().Should().Be((byte) 3); + Half.NaN.MustNotBeZero().Should().Be(Half.NaN); + + var zeroShort = () => ((short) 0).MustNotBeZero(); + var zeroHalf = () => Half.Zero.MustNotBeZero(); + var negativeZeroHalf = () => Half.NegativeZero.MustNotBeZero(); + zeroShort.Should().Throw().WithMessage("*must not be zero*"); + zeroHalf.Should().Throw().WithMessage("*must not be zero*"); + negativeZeroHalf.Should().Throw().WithMessage("*must not be zero*"); + + Test.CustomException((short) 0, (value, factory) => value.MustNotBeZero(factory)); + } +#endif + + private static void CheckInt32IsRejected(int value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } + + private static void CheckInt64IsRejected(long value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } + + private static void CheckDecimalIsRejected(decimal value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } + + private static void CheckFloatIsRejected(float value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } + + private static void CheckDoubleIsRejected(double value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } + + private static void CheckTimeSpanIsRejected(TimeSpan value) + { + var act = () => value.MustNotBeZero(); + + act.Should().Throw().WithMessage("*must not be zero*"); + } +} diff --git a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/AssertionWhitelist.cs b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/AssertionWhitelist.cs index 66efe13a..ebb08627 100644 --- a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/AssertionWhitelist.cs +++ b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/AssertionWhitelist.cs @@ -125,12 +125,16 @@ public sealed class AssertionWhitelist public AssertionEntry MustBeLongerThanOrEqualTo { get; init; } = new(); + public AssertionEntry MustBeNegative { get; init; } = new(); + public AssertionEntry MustBeNewLine { get; init; } = new(); public AssertionEntry MustBeOfType { get; init; } = new(); public AssertionEntry MustBeOneOf { get; init; } = new(); + public AssertionEntry MustBePositive { get; init; } = new(); + public AssertionEntry MustBeRelativeUri { get; init; } = new(); public AssertionEntry MustBeShorterThan { get; init; } = new(); @@ -203,6 +207,8 @@ public sealed class AssertionWhitelist public AssertionEntry MustNotBeLessThanOrEqualTo { get; init; } = new(); + public AssertionEntry MustNotBeNegative { get; init; } = new(); + public AssertionEntry MustNotBeNull { get; init; } = new(); public AssertionEntry MustNotBeNullOrEmpty { get; init; } = new(); @@ -213,10 +219,14 @@ public sealed class AssertionWhitelist public AssertionEntry MustNotBeOneOf { get; init; } = new(); + public AssertionEntry MustNotBePositive { get; init; } = new(); + public AssertionEntry MustNotBeSameAs { get; init; } = new(); public AssertionEntry MustNotBeSubstringOf { get; init; } = new(); + public AssertionEntry MustNotBeZero { get; init; } = new(); + public AssertionEntry MustNotContain { get; init; } = new(); public AssertionEntry MustNotEndWith { get; init; } = new(); diff --git a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs index e55be5d3..633b965f 100644 --- a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs +++ b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs @@ -368,11 +368,13 @@ public CallerArgumentExpressionAttribute(string parameterName) replacedNodes.Add(jetBrainsNamespace, jetBrainsNamespace); } + // GetFiles returns entries in filesystem-dependent order - sort by name so the merged output is deterministic var allSourceFiles = new DirectoryInfo(options.SourceFolder).GetFiles("*.cs", SearchOption.AllDirectories) .Where( f => !f.FullName.Contains("obj") && !f.FullName.Contains("bin") ) + .OrderBy(f => f.Name, StringComparer.Ordinal) .ToDictionary(f => f.Name); SourceReachabilityAnalysis? reachabilityAnalysis = null; if (options.AssertionWhitelist.IsEnabled) diff --git a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/settings.json b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/settings.json index 4e235a9f..c19f91e1 100644 --- a/tools/source-export/Light.GuardClauses.SourceCodeTransformation/settings.json +++ b/tools/source-export/Light.GuardClauses.SourceCodeTransformation/settings.json @@ -76,9 +76,11 @@ "MustBeLocal": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeLongerThan": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeLongerThanOrEqualTo": { "Include": true, "IncludeExceptionFactoryOverload": true }, + "MustBeNegative": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeNewLine": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeOfType": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeOneOf": { "Include": true, "IncludeExceptionFactoryOverload": true }, + "MustBePositive": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeRelativeUri": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeShorterThan": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustBeShorterThanOrEqualTo": { "Include": true, "IncludeExceptionFactoryOverload": true }, @@ -115,13 +117,16 @@ "MustNotBeIn": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeLessThan": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeLessThanOrEqualTo": { "Include": true, "IncludeExceptionFactoryOverload": true }, + "MustNotBeNegative": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeNull": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeNullOrEmpty": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeNullOrWhiteSpace": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeNullReference": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeOneOf": { "Include": true, "IncludeExceptionFactoryOverload": true }, + "MustNotBePositive": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeSameAs": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotBeSubstringOf": { "Include": true, "IncludeExceptionFactoryOverload": true }, + "MustNotBeZero": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotContain": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotEndWith": { "Include": true, "IncludeExceptionFactoryOverload": true }, "MustNotStartWith": { "Include": true, "IncludeExceptionFactoryOverload": true },