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

Adds NullOrOutOfRange clauses, adding support for nullable classes/structs to OutOfRange #143

Merged
merged 35 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a96be57
Rename File and Class
rafaelsc Oct 24, 2021
71917fc
Add Tests for Class that Implements IComparable
rafaelsc Oct 24, 2021
165255e
Bump C# Version to 9, for better Nullable Checks with Generics
rafaelsc Oct 24, 2021
3bb9e77
Add NullOrOutOfRange to Check for Null or OutOfRange for References T…
rafaelsc Oct 24, 2021
0dd488f
Fix Test class implementation
rafaelsc Oct 24, 2021
9bafa7d
Add Support to Nullable<int> for OutOfRange
rafaelsc Oct 24, 2021
73f1db4
Better Nullable<T> Implementation for NullOrOutOfRange
rafaelsc Oct 25, 2021
78e42c8
Add NullOrOutOfSQLDateRange Supporting DateTime? was input
rafaelsc Oct 25, 2021
667a045
Add More Nullable Unit Tests for NullOrOutOfRange
rafaelsc Oct 25, 2021
097cbc9
Merge branch 'main' into outOfRangeForNullable
ardalis Oct 30, 2021
9a84bd2
Merge
rafaelsc Oct 31, 2021
5126c8f
Merge branch 'outOfRangeForNullable' of https://github.com/rafaelsc/G…
rafaelsc Oct 31, 2021
b9fa68e
Update to CI/CD use Net5 with C#9
rafaelsc Oct 31, 2021
055513b
Update Test to use Net5
rafaelsc Oct 31, 2021
74334f3
Fix QA Errors
rafaelsc Oct 31, 2021
b117b11
Merge
rafaelsc Jan 21, 2022
804e975
Merge
rafaelsc Mar 15, 2022
9521590
Fix Compilation
rafaelsc Mar 15, 2022
1076a73
Update
rafaelsc Mar 15, 2022
adc991c
Update
rafaelsc Mar 15, 2022
9332d46
Fix Warning
rafaelsc Mar 15, 2022
a9a8a06
Fix Warnings
rafaelsc Mar 16, 2022
1c751bb
Add Mising Attributes
rafaelsc Mar 16, 2022
ed9bd59
Update Tests
rafaelsc Mar 16, 2022
88646d6
Merge branch 'main' into outOfRangeForNullable
ardalis May 16, 2022
48b90d4
Format
rafaelsc Feb 22, 2023
38b1793
Merge
rafaelsc Feb 22, 2023
e5c412f
Update
rafaelsc Feb 22, 2023
edf9b13
Fix Tests
rafaelsc Feb 22, 2023
8e3fbd2
Rollback Change
rafaelsc Feb 22, 2023
16fe4c9
CleanUp
rafaelsc Feb 23, 2023
14890a4
Format
rafaelsc Feb 23, 2023
9c8b0f4
Merge branch 'main' into outOfRangeForNullable
ardalis Feb 28, 2023
1c63d19
Merge branch 'main' into outOfRangeForNullable
ardalis Feb 28, 2023
e9929e0
Merge branch 'main' into outOfRangeForNullable
ardalis Jul 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 108 additions & 6 deletions src/GuardClauses/GuardAgainstOutOfRangeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Ardalis.GuardClauses;
Expand Down Expand Up @@ -88,7 +89,11 @@
/// <returns><paramref name="input" /> if any item is not out of range.</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IEnumerable<T> OutOfRange<T>(this IGuardClause guardClause, IEnumerable<T> input, string parameterName, T rangeFrom, T rangeTo, string? message = null) where T : IComparable, IComparable<T>
public static IEnumerable<T> OutOfRange<T>(this IGuardClause guardClause,
IEnumerable<T> input,
string parameterName,
T rangeFrom, T rangeTo,
string? message = null) where T : IComparable, IComparable<T>
{
if (rangeFrom.CompareTo(rangeTo) > 0)
{
Expand All @@ -106,6 +111,34 @@

return input;
}

/// <summary>
/// Throws an <see cref="ArgumentNullException" /> if <paramref name="input" /> is null.
/// Throws an <see cref="ArgumentOutOfRangeException" /> if <paramref name="input" /> is not in the range of valid SqlDateTime values.
/// </summary>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="message">Optional. Custom error message</param>
/// <returns><paramref name="input" /> if the value is in the range of valid SqlDateTime values.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
#if NETSTANDARD || NETFRAMEWORK
public static DateTime NullOrOutOfSQLDateRange(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] DateTime? input,
string parameterName,
string? message = null)
#else
public static DateTime NullOrOutOfSQLDateRange(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] DateTime? input,
[CallerArgumentExpression("input")] string parameterName = null,

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

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
string? message = null)
#endif
{
guardClause.Null(input, nameof(input));
return OutOfSQLDateRange(guardClause, input.Value, parameterName, message);
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException" /> if <paramref name="input" /> is not in the range of valid SqlDateTime values.
/// </summary>
Expand All @@ -131,7 +164,7 @@
const long sqlMinDateTicks = 552877920000000000;
const long sqlMaxDateTicks = 3155378975999970000;

return OutOfRange<DateTime>(guardClause, input, parameterName!, new DateTime(sqlMinDateTicks), new DateTime(sqlMaxDateTicks), message);
return NullOrOutOfRangeInternal<DateTime>(guardClause, input, parameterName, new DateTime(sqlMinDateTicks), new DateTime(sqlMaxDateTicks), message);
}

/// <summary>
Expand All @@ -145,12 +178,82 @@
/// <param name="message">Optional. Custom error message</param>
/// <returns><paramref name="input" /> if the value is not out of range.</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static T OutOfRange<T>(this IGuardClause guardClause, T input,
public static T OutOfRange<T>(this IGuardClause guardClause,
T input,
string parameterName,
T rangeFrom,
T rangeTo,
[NotNull][ValidatedNotNull] T rangeFrom,
[NotNull][ValidatedNotNull] T rangeTo,
string? message = null) where T : IComparable, IComparable<T>
{
return NullOrOutOfRangeInternal<T>(guardClause, input, parameterName, rangeFrom, rangeTo, message);
}


/// <summary>
/// Throws an <see cref="ArgumentNullException" /> if <paramref name="input" /> is null.
/// Throws an <see cref="ArgumentOutOfRangeException" /> if <paramref name="input"/> 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>
/// <returns><paramref name="input" /> if the value is not not null or out of range.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static T NullOrOutOfRange<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] T? input,
string parameterName,
[NotNull][ValidatedNotNull] T rangeFrom,
[NotNull][ValidatedNotNull] T rangeTo,
string? message = null) where T : IComparable<T>
{
guardClause.Null(input, nameof(input));
return NullOrOutOfRangeInternal(guardClause, input, parameterName, rangeFrom, rangeTo, message);
}

/// <summary>
/// Throws an <see cref="ArgumentNullException" /> if <paramref name="input" /> is null.
/// Throws an <see cref="ArgumentOutOfRangeException" /> if <paramref name="input"/> 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>
/// <returns><paramref name="input" /> if the value is not not null or out of range.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static T NullOrOutOfRange<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] T? input,
string parameterName,
[NotNull][ValidatedNotNull] T rangeFrom,
[NotNull][ValidatedNotNull] T rangeTo,
string? message = null) where T : struct, IComparable<T>
{
guardClause.Null(input, nameof(input));
return NullOrOutOfRangeInternal<T>(guardClause, input.Value, parameterName, rangeFrom, rangeTo, message);
}

/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
private static T NullOrOutOfRangeInternal<T>(this IGuardClause guardClause,
[NotNull][ValidatedNotNull] T? input,
string? parameterName,
[NotNull][ValidatedNotNull] T? rangeFrom,
[NotNull][ValidatedNotNull] T? rangeTo,
string? message = null) where T : IComparable<T>?
{
Guard.Against.Null(input, nameof(input));
Guard.Against.Null(parameterName, nameof(parameterName));
Guard.Against.Null(rangeFrom, nameof(rangeFrom));
Guard.Against.Null(rangeTo, nameof(rangeTo));

if (rangeFrom.CompareTo(rangeTo) > 0)
{
throw new ArgumentException(message ?? $"{nameof(rangeFrom)} should be less or equal than {nameof(rangeTo)}", parameterName);
Expand All @@ -167,5 +270,4 @@

return input;
}

}
12 changes: 6 additions & 6 deletions test/GuardClauses.UnitTests/GuardAgainstNullOrInvalidInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ public IEnumerator<object[]> GetEnumerator()
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class ArgumentNullExceptionClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
public class ArgumentNullExceptionClassData : IEnumerable<object?[]>
{
yield return new object[] { null!, (Func<string, bool>)(x => x.Length > 10) };
public IEnumerator<object?[]> GetEnumerator()
{
yield return new object?[] { null, (Func<string, bool>)(x => x.Length > 10) };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class ArgumentExceptionClassData : IEnumerable<object[]>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Ardalis.GuardClauses;
using Xunit;

namespace GuardClauses.UnitTests
{
/// <summary>
/// Every type that implements IComparable and IComparable<T> can use OutOfRange.
/// Here for example tuples are used.
/// </summary>
public class GuardAgainstNullOrOutOfRangeForClassIComparable
{
private class TestObj : IComparable<TestObj?>, IEquatable<TestObj?>
{
private readonly int _internalValue;

public TestObj(int internalValue)
{
_internalValue = internalValue;
}

public int CompareTo([AllowNull] TestObj? other) => _internalValue.CompareTo(other?._internalValue);


public override bool Equals(object? obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(TestObj)) return false;
return Equals((TestObj)obj);
}
public bool Equals([AllowNull] TestObj? other) => ReferenceEquals(this, other) || _internalValue == other?._internalValue;

public override int GetHashCode() => _internalValue.GetHashCode();
}

[Fact]
public void DoesNothingGivenInRangeValue()
{
Guard.Against.NullOrOutOfRange(new TestObj(1), "index", new TestObj(1), new TestObj(1));
Guard.Against.NullOrOutOfRange(new TestObj(2), "index", new TestObj(1), new TestObj(3));
Guard.Against.NullOrOutOfRange(new TestObj(3), "index", new TestObj(1), new TestObj(3));
}

[Fact]
public void ThrowsGivenOutOfRangeValue()
{
Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(new TestObj(-1), "index", new TestObj(1), new TestObj(3)));
Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(new TestObj(0), "index", new TestObj(1), new TestObj(3)));
Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(new TestObj(4), "index", new TestObj(1), new TestObj(3)));
}

[Fact]
public void ThrowsGivenInvalidArgumentValue()
{
Assert.Throws<ArgumentException>(() => Guard.Against.NullOrOutOfRange(new TestObj(-1), "index", new TestObj(3), new TestObj(1)));
Assert.Throws<ArgumentException>(() => Guard.Against.NullOrOutOfRange(new TestObj(0), "index", new TestObj(3), new TestObj(1)));
Assert.Throws<ArgumentException>(() => Guard.Against.NullOrOutOfRange(new TestObj(4), "index", new TestObj(3), new TestObj(1)));
}

[Fact]
public void ThrowsGivenInvalidNullArgumentValue()
{
#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.

Assert.Throws<ArgumentNullException>(() => Guard.Against.NullOrOutOfRange(null, "index", new TestObj(3), new TestObj(1)));
Assert.Throws<ArgumentNullException>(() => Guard.Against.NullOrOutOfRange(new TestObj(0), "index", null, new TestObj(1)));
Assert.Throws<ArgumentNullException>(() => Guard.Against.NullOrOutOfRange(new TestObj(4), "index", new TestObj(3), null));

#pragma warning restore CS8631
}

[Fact]
public void ReturnsExpectedValueGivenInRangeValue()
{
Assert.Equal(new TestObj(1), Guard.Against.NullOrOutOfRange(new TestObj(1), "index", new TestObj(1), new TestObj(1)));
Assert.Equal(new TestObj(1), Guard.Against.NullOrOutOfRange(new TestObj(1), "index", new TestObj(1), new TestObj(3)));
Assert.Equal(new TestObj(2), Guard.Against.NullOrOutOfRange(new TestObj(2), "index", new TestObj(1), new TestObj(3)));
Assert.Equal(new TestObj(3), Guard.Against.NullOrOutOfRange(new TestObj(3), "index", new TestObj(1), new TestObj(3)));
}
}
}
58 changes: 58 additions & 0 deletions test/GuardClauses.UnitTests/GuardAgainstNullOrOutOfRangeForInt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Ardalis.GuardClauses;
using System;
using Xunit;

namespace GuardClauses.UnitTests
{
public class GuardAgainstNullOrOutOfRangeForInt
{
[Theory]
[InlineData(1, 1, 1)]
[InlineData(1, 1, 3)]
[InlineData(2, 1, 3)]
[InlineData(3, 1, 3)]
public void DoesNothingGivenInRangeValue(int input, int rangeFrom, int rangeTo)
{
Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo);
}

[Theory]
[InlineData(-1, 1, 3)]
[InlineData(0, 1, 3)]
[InlineData(4, 1, 3)]
public void ThrowsGivenOutOfRangeValue(int input, int rangeFrom, int rangeTo)
{
Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Theory]
[InlineData(-1, 3, 1)]
[InlineData(0, 3, 1)]
[InlineData(4, 3, 1)]
public void ThrowsGivenInvalidArgumentValue(int input, int rangeFrom, int rangeTo)
{
Assert.Throws<ArgumentException>(() => Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Theory]
[InlineData(1, 1, 1, 1)]
[InlineData(1, 1, 3, 1)]
[InlineData(2, 1, 3, 2)]
[InlineData(3, 1, 3, 3)]
public void ReturnsExpectedValueGivenInRangeValue(int input, int rangeFrom, int rangeTo, int expected)
{
Assert.Equal(expected, Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Theory]
[InlineData(null, "Input parameterName was out of range (Parameter 'parameterName')")]
[InlineData("Int range", "Int range (Parameter 'parameterName')")]
public void ErrorMessageMatchesExpected(string customMessage, string expectedMessage)
{
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(3, "parameterName", 0, 1, customMessage));
Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(expectedMessage, exception.Message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Ardalis.GuardClauses;
using System;
using Xunit;

namespace GuardClauses.UnitTests
{
public class GuardAgainstNullOrOutOfRangeForNullableInt
{
[Theory]
[InlineData(1, 1, 1)]
[InlineData(1, 1, 3)]
[InlineData(2, 1, 3)]
[InlineData(3, 1, 3)]
public void DoesNothingGivenInRangeValue(int? input, int rangeFrom, int rangeTo)
{
Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo);
}

[Theory]
[InlineData(-1, 1, 3)]
[InlineData(0, 1, 3)]
[InlineData(4, 1, 3)]
public void ThrowsGivenOutOfRangeValue(int? input, int rangeFrom, int rangeTo)
{
Assert.Throws<ArgumentOutOfRangeException>(() => Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Theory]
[InlineData(-1, 3, 1)]
[InlineData(0, 3, 1)]
[InlineData(4, 3, 1)]
public void ThrowsGivenInvalidArgumentValue(int? input, int rangeFrom, int rangeTo)
{
Assert.Throws<ArgumentException>(() => Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Theory]
[InlineData(1, 1, 1, 1)]
[InlineData(1, 1, 3, 1)]
[InlineData(2, 1, 3, 2)]
[InlineData(3, 1, 3, 3)]
public void ReturnsExpectedValueGivenInRangeValue(int? input, int rangeFrom, int rangeTo, int expected)
{
Assert.Equal(expected, Guard.Against.NullOrOutOfRange(input, "index", rangeFrom, rangeTo));
}

[Fact]
public void ThrowsGivenInvalidNullArgumentValue()
{
Assert.Throws<ArgumentNullException>(() => Guard.Against.NullOrOutOfRange(null, "index", -10, 10));
}
}
}
Loading