Skip to content

Commit

Permalink
WIP: Converting Exception params to exceptionCreator funcs.
Browse files Browse the repository at this point in the history
  • Loading branch information
ardalis committed Apr 19, 2024
1 parent 9ee058e commit 6186c68
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 77 deletions.
66 changes: 39 additions & 27 deletions src/GuardClauses/GuardAgainstNullExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ public static partial class GuardClauseExtensions
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not null.</returns>
/// <exception cref="Exception"></exception>
public static T Null<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull]T? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null)
Func<Exception>? exceptionCreator = null)
{
if (input is null)
{
Exception? exception = exceptionCreator?.Invoke();

if (string.IsNullOrEmpty(message))
{
throw exception ?? new ArgumentNullException(parameterName);
Expand All @@ -51,17 +53,19 @@ public static T Null<T>(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not null.</returns>
/// <exception cref="Exception"></exception>
public static T Null<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull]T? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null) where T : struct
Func<Exception>? exceptionCreator = null) where T : struct
{
if (input is null)
{
Exception? exception = exceptionCreator?.Invoke();

if (string.IsNullOrEmpty(message))
{
throw exception ?? new ArgumentNullException(parameterName);
Expand All @@ -80,7 +84,7 @@ public static T Null<T>(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not an empty string or null.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
Expand All @@ -89,12 +93,13 @@ public static string NullOrEmpty(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] string? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null)
Func<Exception>? exceptionCreator = null)
{
Guard.Against.Null(input, parameterName, message, exception);
Guard.Against.Null(input, parameterName, message, exceptionCreator);
if (input == string.Empty)
{
throw exception ?? new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
}

return input;
Expand All @@ -108,20 +113,22 @@ public static string NullOrEmpty(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not an empty guid or null.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static Guid NullOrEmpty(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] Guid? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null, Exception? exception = null)
string? message = null,
Func<Exception>? exceptionCreator = null)
{
Guard.Against.Null(input, parameterName, message, exception);
Guard.Against.Null(input, parameterName, message, exceptionCreator);
if (input == Guid.Empty)
{
throw exception ?? new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
}

return input.Value;
Expand All @@ -135,25 +142,27 @@ public static Guid NullOrEmpty(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not an empty enumerable or null.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static IEnumerable<T> NullOrEmpty<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] IEnumerable<T>? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null, Exception? exception = null)
string? message = null,
Func<Exception>? exceptionCreator = null)
{
Guard.Against.Null(input, parameterName, message, exception);
Guard.Against.Null(input, parameterName, message, exceptionCreator: exceptionCreator);

if (input is Array and { Length: 0 } //Try checking first with pattern matching because it's faster than TryGetNonEnumeratedCount on Array
#if NET6_0_OR_GREATER
|| (input.TryGetNonEnumeratedCount(out var count) && count == 0)
#endif
|| !input.Any())
{
throw exception ?? new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
}

return input;
Expand All @@ -167,20 +176,22 @@ public static IEnumerable<T> NullOrEmpty<T>(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not an empty or whitespace string.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static string NullOrWhiteSpace(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] string? input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null, Exception? exception = null)
string? message = null,
Func<Exception>? exceptionCreator = null)
{
Guard.Against.NullOrEmpty(input, parameterName, message, exception);
Guard.Against.NullOrEmpty(input, parameterName, message, exceptionCreator);
if (String.IsNullOrWhiteSpace(input))
{
throw exception ?? new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"Required input {parameterName} was empty.", parameterName);
}

return input;
Expand All @@ -193,19 +204,20 @@ public static string NullOrWhiteSpace(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not default for that type.</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static T Default<T>(this IGuardClause guardClause,
[AllowNull, NotNull]T input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null)
Func<Exception>? exceptionCreator = null)
{
if (EqualityComparer<T>.Default.Equals(input, default(T)!) || input is null)
{
throw exception ?? new ArgumentException(message ?? $"Parameter [{parameterName}] is default value for type {typeof(T).Name}", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"Parameter [{parameterName}] is default value for type {typeof(T).Name}", parameterName);
}

return input;
Expand All @@ -221,7 +233,7 @@ public static T Default<T>(this IGuardClause guardClause,
/// <param name="parameterName"></param>
/// <param name="predicate"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
Expand All @@ -232,10 +244,10 @@ public static T NullOrInvalidInput<T>(this IGuardClause guardClause,
string parameterName,
Func<T, bool> predicate,
string? message = null,
Exception? exception = null)
Func<Exception>? exceptionCreator = null)
{
Guard.Against.Null(input, parameterName, message, exception);
Guard.Against.Null(input, parameterName, message, exceptionCreator: exceptionCreator);

return Guard.Against.InvalidInput(input, parameterName, predicate, message, exception);
return Guard.Against.InvalidInput(input, parameterName, predicate, message, exceptionCreator?.Invoke());
}
}
43 changes: 24 additions & 19 deletions src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static partial class GuardClauseExtensions
/// <param name="maxLength"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not negative.</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
Expand All @@ -28,12 +28,12 @@ public static string LengthOutOfRange(this IGuardClause guardClause,
int maxLength,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null)
Func<Exception>? exceptionCreator = null)
{
Guard.Against.Negative<int>(maxLength - minLength, parameterName: "min or max length",
message: "Min length must be equal or less than max length.", exception: exception);
Guard.Against.StringTooShort(input, minLength, nameof(minLength), exception: exception);
Guard.Against.StringTooLong(input, maxLength, nameof(maxLength), exception: exception);
message: "Min length must be equal or less than max length.", exception: exceptionCreator?.Invoke());
Guard.Against.StringTooShort(input, minLength, nameof(minLength), exception: exceptionCreator?.Invoke());
Guard.Against.StringTooLong(input, maxLength, nameof(maxLength), exception: exceptionCreator?.Invoke());

return input;
}
Expand All @@ -46,18 +46,19 @@ public static string LengthOutOfRange(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not out of range.</returns>
/// <exception cref="InvalidEnumArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static int EnumOutOfRange<T>(this IGuardClause guardClause,
int input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null) where T : struct, Enum
Func<Exception>? exceptionCreator = null) where T : struct, Enum
{
if (!Enum.IsDefined(typeof(T), input))
{
var exception = exceptionCreator?.Invoke();
if (string.IsNullOrEmpty(message))
{
throw exception ?? new InvalidEnumArgumentException(parameterName, input, typeof(T));
Expand All @@ -76,18 +77,19 @@ public static int EnumOutOfRange<T>(this IGuardClause guardClause,
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// /// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if the value is not out of range.</returns>
/// <exception cref="InvalidEnumArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static T EnumOutOfRange<T>(this IGuardClause guardClause,
T input,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null,
Exception? exception = null) where T : struct, Enum
Func<Exception>? exceptionCreator = null) where T : struct, Enum
{
if (!Enum.IsDefined(typeof(T), input))
{
var exception = exceptionCreator?.Invoke();
if (string.IsNullOrEmpty(message))
{
throw exception ?? new InvalidEnumArgumentException(parameterName, Convert.ToInt32(input), typeof(T));
Expand All @@ -99,15 +101,16 @@ public static T EnumOutOfRange<T>(this IGuardClause guardClause,
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException" /> or a custom <see cref="Exception" /> if any <paramref name="input"/>'s item is less than <paramref name="rangeFrom"/> or greater than <paramref name="rangeTo"/>.
/// Throws an <see cref="ArgumentOutOfRangeException" /> or a custom <see cref="Exception" /> if
/// any <paramref name="input"/>'s item is less than <paramref name="rangeFrom"/> or greater than <paramref name="rangeTo"/>.
/// </summary>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="rangeFrom"></param>
/// <param name="rangeTo"></param>
/// <param name="message">Optional. Custom error message</param>
/// <param name="exception"></param>
/// <param name="exceptionCreator"></param>
/// <returns><paramref name="input" /> if any item is not out of range.</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
Expand All @@ -117,20 +120,22 @@ public static IEnumerable<T> OutOfRange<T>(this IGuardClause guardClause,
string parameterName,
T rangeFrom, T rangeTo,
string? message = null,
Exception? exception = null) where T : IComparable, IComparable<T>
Func<Exception>? exceptionCreator = null) where T : IComparable, IComparable<T>
{
if (rangeFrom.CompareTo(rangeTo) > 0)
{
throw new ArgumentException(message ?? $"{nameof(rangeFrom)} should be less or equal than {nameof(rangeTo)}", parameterName);
throw exceptionCreator?.Invoke() ??
new ArgumentException(message ?? $"{nameof(rangeFrom)} should be less or equal than {nameof(rangeTo)}", parameterName);
}

if (input.Any(x => x.CompareTo(rangeFrom) < 0 || x.CompareTo(rangeTo) > 0))
{
if (string.IsNullOrEmpty(message))
{
throw exception ?? new ArgumentOutOfRangeException(parameterName, message ?? $"Input {parameterName} had out of range item(s)");
throw exceptionCreator?.Invoke() ??
new ArgumentOutOfRangeException(parameterName, message ?? $"Input {parameterName} had out of range item(s)");
}
throw exception ?? new ArgumentOutOfRangeException(parameterName, message);
throw exceptionCreator?.Invoke() ?? new ArgumentOutOfRangeException(parameterName, message);
}

return input;
Expand All @@ -154,7 +159,7 @@ public static DateTime NullOrOutOfSQLDateRange(this IGuardClause guardClause,
[CallerArgumentExpression("input")] string? parameterName = null,
string? message = null, Exception? exception = null)
{
guardClause.Null(input, nameof(input),exception: exception);
guardClause.Null(input, nameof(input), exceptionCreator: () =>exception);

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 162 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.
return OutOfSQLDateRange(guardClause, input.Value, parameterName, message, exception);
}

Expand Down Expand Up @@ -228,8 +233,8 @@ public static T NullOrOutOfRange<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] T rangeTo,
string? message = null, Exception? exception = null) where T : IComparable<T>
{
guardClause.Null(input, nameof(input),exception: exception);
return NullOrOutOfRangeInternal(guardClause, input, parameterName, rangeFrom, rangeTo, message,exception);
guardClause.Null(input, nameof(input),exceptionCreator: () => exception);

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 236 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.
return NullOrOutOfRangeInternal(guardClause, input, parameterName, rangeFrom, rangeTo, message, exception);
}

/// <summary>
Expand All @@ -255,7 +260,7 @@ public static T NullOrOutOfRange<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] T rangeTo,
string? message = null, Exception? exception = null) where T : struct, IComparable<T>
{
guardClause.Null(input, nameof(input), exception: exception);
guardClause.Null(input, nameof(input), exceptionCreator: () => exception);

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.

Check warning on line 263 in src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs

View workflow job for this annotation

GitHub Actions / list Ardalis.GuardClauses on nuget.org

Possible null reference return.
return NullOrOutOfRangeInternal<T>(guardClause, input.Value, parameterName, rangeFrom, rangeTo, message, exception);
}

Expand Down
15 changes: 10 additions & 5 deletions test/GuardClauses.UnitTests/GuardAgainstDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@ public void ThrowsGivenDefaultValue()
public void ThrowsCustomExceptionWhenSuppliedGivenDefaultValue()
{
Exception customException = new Exception();
Assert.Throws<Exception>( () => Guard.Against.Default(default(string), "string", exception: customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(int), "int", exception: customException));
Assert.Throws<Exception>(() => Guard.Against.Default(default(Guid), "guid", exception: customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(DateTime), "datetime", exception: customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(object), "object", exception: customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(string), "string",
exceptionCreator: () => customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(int), "int",
exceptionCreator: () => customException));
Assert.Throws<Exception>(() => Guard.Against.Default(default(Guid), "guid",
exceptionCreator: () => customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(DateTime), "datetime",
exceptionCreator: () => customException));
Assert.Throws<Exception>( () => Guard.Against.Default(default(object), "object",
exceptionCreator: () => customException));
}

[Theory]
Expand Down
Loading

0 comments on commit 6186c68

Please sign in to comment.