Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor AgainstExpression to Expression for Clarity and Consistency #317

Merged
73 changes: 30 additions & 43 deletions src/GuardClauses/GuardAgainstExpressionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,73 +1,60 @@
using System;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace Ardalis.GuardClauses;

public static partial class GuardClauseExtensions
{
/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// Validates the <paramref name="input"/> using the specified <paramref name="func"/> and throws an <see cref="ArgumentException"/> if it evaluates to true.
/// The <paramref name="func"/> should return true to indicate an invalid or undesirable state of the input.
/// If <paramref name="func"/> returns true, an <see cref="ArgumentException"/> is thrown, signifying that the input is invalid.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
public static T AgainstExpression<T>(this IGuardClause guardClause,
/// <typeparam name="T">The type of the input parameter.</typeparam>
/// <param name="guardClause">The guard clause instance.</param>
/// <param name="func">The function that evaluates the input. It should return true if the input is considered invalid or in a negative state.</param>
/// <param name="input">The input to evaluate.</param>
/// <param name="message">The message to include in the exception if the input is invalid.</param>
/// <param name="parameterName">The name of the parameter to include in the thrown exception, captured automatically from the input expression.</param>
/// <returns>The <paramref name="input"/> if the <paramref name="func"/> evaluates to false, indicating a valid state.</returns>
/// <exception cref="ArgumentException">Thrown when the validation function returns true, indicating that the input is invalid.</exception>
public static T Expression<T>(this IGuardClause guardClause,
Func<T, bool> func,
T input,
string message) where T : struct
string message,
[CallerArgumentExpression("input")] string? parameterName = null) where T : struct
{
if (!func(input))
{
throw new ArgumentException(message);
throw new ArgumentException(message, parameterName!);
}

return input;
}

/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// Validates the <paramref name="func"/> asynchronously and throws an <see cref="ArgumentException" /> if it evaluates to false for given <paramref name="input"/>
/// The <paramref name="func"/> should return true to indicate an invalid or undesirable state.
/// If <paramref name="func"/> returns true, indicating that the input is invalid, an <see cref="ArgumentException"/> is thrown.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <typeparam name="T">The type of the input parameter.</typeparam>
/// <param name="func">The function that evaluates the input. It should return true if the input is considered invalid or in a negative state.</param>
/// <param name="guardClause">The guard clause instance.</param>
/// <param name="input">The input to evaluate.</param>
/// <param name="message">The message to include in the exception if the input is invalid.</param>
/// <param name="parameterName">The name of the parameter to include in the thrown exception, captured automatically from the input expression.</param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
public static async Task<T> AgainstExpressionAsync<T>(this IGuardClause guardClause,
/// <exception cref="ArgumentException">Thrown when the validation function returns true, indicating that the input is invalid.</exception>
public static async Task<T> ExpressionAsync<T>(this IGuardClause guardClause,
Func<T, Task<bool>> func,
T input,
string message) where T : struct
string message,
[CallerArgumentExpression("input")] string? parameterName = null) where T : struct
{
if (!await func(input))
{
throw new ArgumentException(message);
}

return input;
}

/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <param name="paramName">The name of the parameter that is invalid</param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
public static T AgainstExpression<T>(this IGuardClause guardClause, Func<T, bool> func,
T input, string message, string paramName) where T : struct
{
if (!func(input))
{
throw new ArgumentException(message, paramName);
throw new ArgumentException(message, parameterName!);
}

return input;
Expand Down
78 changes: 78 additions & 0 deletions src/GuardClauses/GuardAgainstExpressionExtensionsDeprecated.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Threading.Tasks;

namespace Ardalis.GuardClauses;

public static partial class GuardClauseExtensions
{
/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
[Obsolete("Deprecated: Switch to Expression for validation.")]
public static T AgainstExpression<T>(this IGuardClause guardClause,
Func<T, bool> func,
T input,
string message) where T : struct
{
if (!func(input))
{
throw new ArgumentException(message);
}

return input;
}

/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
[Obsolete("Deprecated: Switch to ExpressionAsync for asynchronous validation.")]
public static async Task<T> AgainstExpressionAsync<T>(this IGuardClause guardClause,
Func<T, Task<bool>> func,
T input,
string message) where T : struct
{
if (!await func(input))
{
throw new ArgumentException(message);
}

return input;
}

/// <summary>
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="message"></param>
/// <param name="paramName">The name of the parameter that is invalid</param>
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
/// <exception cref="ArgumentException"></exception>
[Obsolete("Deprecated: Switch to Expression for validation.")]
public static T AgainstExpression<T>(this IGuardClause guardClause, Func<T, bool> func,
T input, string message, string paramName) where T : struct
{
if (!func(input))
{
throw new ArgumentException(message, paramName);
}

return input;
}
}
25 changes: 7 additions & 18 deletions test/GuardClauses.UnitTests/GuardAgainstExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,60 +26,49 @@ public static IEnumerable<object[]> GetCustomStruct()
[InlineData(10)]
public void GivenIntegerWhenTheExpressionEvaluatesToTrueDoesNothing(int test)
{
Guard.Against.AgainstExpression((x) => x == 10, test, "Value is not equal to 10");
Guard.Against.Expression((x) => x == 10, test, "Value is not equal to 10");
}

[Theory]
[InlineData(10)]
public void GivenIntegerWhenTheExpressionEvaluatesToFalseThrowsException(int test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5, test, "Value is not equal to 10"));
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x == 5, test, "Value is not equal to 10"));
SimonNyvall marked this conversation as resolved.
Show resolved Hide resolved
}

[Theory]
[InlineData(1.1)]
public void GivenDoubleWhenTheExpressionEvaluatesToTrueDoesNothing(double test)
{
Guard.Against.AgainstExpression((x) => x == 1.1, test, "Value is not equal to 1.1");
Guard.Against.Expression((x) => x == 1.1, test, "Value is not equal to 1.1");
SimonNyvall marked this conversation as resolved.
Show resolved Hide resolved
}

[Theory]
[InlineData(1.1)]
public void GivenDoubleWhenTheExpressionEvaluatesToFalseThrowsException(int test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5.0, test, "Value is not equal to 1.1"));
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x == 5.0, test, "Value is not equal to 1.1"));
}

[Theory]
[MemberData(nameof(GetCustomStruct))]
public void GivenCustomStructWhenTheExpressionEvaluatesToTrueDoesNothing(CustomStruct test)
{
Guard.Against.AgainstExpression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching");
Guard.Against.Expression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching");
}

[Theory]
[MemberData(nameof(GetCustomStruct))]
public void GivenCustomStructWhenTheExpressionEvaluatesToFalseThrowsException(CustomStruct test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching"));
}

[Theory]
[InlineData(null, "Value does not fall within the expected range.")]
[InlineData("Please provide correct value", "Please provide correct value")]
public void ErrorMessageMatchesExpected(string customMessage, string expectedMessage)
{
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, customMessage));
Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(expectedMessage, exception.Message);
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching"));
}

[Fact]
public void ErrorIncludesParamNameIfProvided()
{
string paramName = "testParamName";
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, "custom message", paramName));
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.Expression(x => x == 1, 2, "custom message", paramName));
Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(paramName, exception.ParamName);
Expand Down
87 changes: 87 additions & 0 deletions test/GuardClauses.UnitTests/GuardAgainstExpressionDeprecated.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Ardalis.GuardClauses;
using Xunit;

namespace GuardClauses.UnitTests;

public class GuardAgainstExpressionDeprecated
{
public struct CustomStruct
{
public string FieldName { get; set; }
}

public static IEnumerable<object[]> GetCustomStruct()
{
yield return new object[] {
new CustomStruct
{
FieldName = "FieldValue"
}
};
}

[Theory]
[InlineData(10)]
public void GivenIntegerWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(int test)
{
Guard.Against.AgainstExpression((x) => x == 10, test, "Value is not equal to 10");
}

[Theory]
[InlineData(10)]
public void GivenIntegerWhenTheAgainstExpressionEvaluatesToFalseThrowsException(int test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5, test, "Value is not equal to 10"));
}

[Theory]
[InlineData(1.1)]
public void GivenDoubleWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(double test)
{
Guard.Against.AgainstExpression((x) => x == 1.1, test, "Value is not equal to 1.1");
}

[Theory]
[InlineData(1.1)]
public void GivenDoubleWhenTheAgainstExpressionEvaluatesToFalseThrowsException(int test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5.0, test, "Value is not equal to 1.1"));
}

[Theory]
[MemberData(nameof(GetCustomStruct))]
public void GivenCustomStructWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(CustomStruct test)
{
Guard.Against.AgainstExpression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching");
}

[Theory]
[MemberData(nameof(GetCustomStruct))]
public void GivenCustomStructWhenTheAgainstExpressionEvaluatesToFalseThrowsException(CustomStruct test)
{
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching"));
}

[Theory]
[InlineData(null, "Value does not fall within the expected range.")]
[InlineData("Please provide correct value", "Please provide correct value")]
public void ErrorMessageMatchesAgainstExpected(string customMessage, string expectedMessage)
{
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, customMessage));
Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(expectedMessage, exception.Message);
}

[Fact]
public void ErrorIncludesParamNameIfProvidedInAgainstExpression()
{
string paramName = "testParamName";
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, "custom message", paramName));
Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(paramName, exception.ParamName);
}
}