Skip to content

Commit

Permalink
Add async Select and Consume
Browse files Browse the repository at this point in the history
  • Loading branch information
d-nery committed Feb 3, 2023
1 parent 9894c70 commit 83345d2
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 127 deletions.
153 changes: 152 additions & 1 deletion Maybe.Test/MaybeExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions;
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;

namespace ZBRA.Maybe.Test
Expand Down Expand Up @@ -106,6 +107,15 @@ public void Select_WithNullableProperty_ReturnsSelectedProperty(Maybe<NullableIn
result.Should().Be(expected);
}

[Theory]
[MemberData(nameof(Select_WithNullablePropertyTestCases))]
public async Task SelectAsync_WithNullableProperty_ReturnsSelectedProperty(Maybe<NullableIntObj> subject, Maybe<int> expected)
{
var result = await subject.SelectAsync(async o => await Task.FromResult(o.Count));

result.Should().Be(expected);
}

public static TheoryData<Maybe<NullableIntObj>, Maybe<int>> Select_WithNullablePropertyTestCases()
{
return new TheoryData<Maybe<NullableIntObj>, Maybe<int>>
Expand Down Expand Up @@ -141,6 +151,15 @@ public void SelectMany_WithMaybeProperty_ReturnsSelectedProperty(Maybe<MaybeIntO
result.Should().Be(expected);
}

[Theory]
[MemberData(nameof(SelectMany_WithMaybePropertyTestCases))]
public async Task SelectManyAsync_WithMaybeProperty_ReturnsSelectedProperty(Maybe<MaybeIntObj> subject, Maybe<int> expected)
{
var result = await subject.SelectManyAsync(async o => await Task.FromResult(o.Count));

result.Should().Be(expected);
}

public static TheoryData<Maybe<MaybeIntObj>, Maybe<int>> SelectMany_WithMaybePropertyTestCases()
{
return new TheoryData<Maybe<MaybeIntObj>, Maybe<int>>
Expand All @@ -158,5 +177,137 @@ public void SelectMany_NullArgument_ShouldThrow()

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Fact]
public void Consume_WithValue_ShouldExecuteAction()
{
string result = null;
var expected = 1;

expected.ToMaybe()
.Consume(i => result = i.ToString());

result.Should().Be(expected.ToString());
}

[Fact]
public async Task ConsumeAsync_WithValue_ShouldExecuteAction()
{
string result = null;
const int expected = 1;

await expected.ToMaybe()
.ConsumeAsync(async i =>
{
result = i.ToString();
await Task.CompletedTask;
});

result.Should().Be(expected.ToString());
}

[Fact]
public void Consume_WithNoValue_ShouldNotExecuteAction()
{
var result = "a";

Maybe<string>.Nothing
.Consume(i => result = "b");

result.Should().Be("a");
}

[Fact]
public async Task ConsumeAsync_WithNoValue_ShouldNotExecuteAction()
{
var result = "a";

await Maybe<string>.Nothing
.ConsumeAsync(async _ =>
{
result = "b";
await Task.CompletedTask;
});

result.Should().Be("a");
}

[Fact]
public void Consume_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Consume((Action<int>)null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void Zip_WithTransformer_ShouldZipValues(int? value, double? otherValue, string expected)
{
static string transformer(int v, double o) => (v + o).ToString();

var result = value.ToMaybe()
.Zip(otherValue.ToMaybe(), transformer);

result.Should().Be(expected.ToMaybe());
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void Zip_WithMaybeTransformer_ShouldZipValues(int? value, double? otherValue, string expected)
{
static Maybe<string> transformer(int v, double o) => (v + o).ToString().ToMaybe();

var result = value.ToMaybe()
.Zip(otherValue.ToMaybe(), transformer);

result.Should().Be(expected.ToMaybe());
}

[Fact]
public void Zip_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Zip(Maybe<int>.Nothing, (Func<int, int, int>)null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Fact]
public void Zip_MaybeNullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Zip(Maybe<int>.Nothing, (Func<int, int, Maybe<int>>)null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void ZipConsume_WhenBothValuesExist_ShouldExecuteAction(int? value, double? otherValue, string expected)
{
string result = null;
void action(int v, double o) => result = (v + o).ToString();

value.ToMaybe()
.ZipAndConsume(otherValue.ToMaybe(), action);

result.Should().Be(expected);
}

[Fact]
public void ZipAndConsume_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().ZipAndConsume(Maybe<int>.Nothing, null);

subject.Should().ThrowExactly<ArgumentNullException>();
}
}
}
104 changes: 2 additions & 102 deletions Maybe.Test/MaybeTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions;
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;

namespace ZBRA.Maybe.Test
Expand Down Expand Up @@ -73,107 +74,6 @@ public void OrThrow_NullArgument_ShouldThrow()
subject.Should().ThrowExactly<ArgumentNullException>();
}

[Fact]
public void Consume_WithValue_ShouldExecuteAction()
{
string result = null;
var expected = 1;

expected.ToMaybe()
.Consume(i => result = i.ToString());

result.Should().Be(expected.ToString());
}

[Fact]
public void Consume_WithNoValue_ShouldNotExecuteAction()
{
var result = "a";

Maybe<string>.Nothing
.Consume(i => result = "b");

result.Should().Be("a");
}

[Fact]
public void Consume_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Consume(null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void Zip_WithTransformer_ShouldZipValues(int? value, double? otherValue, string expected)
{
static string transformer(int v, double o) => (v + o).ToString();

var result = value.ToMaybe()
.Zip(otherValue.ToMaybe(), transformer);

result.Should().Be(expected.ToMaybe());
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void Zip_WithMaybeTransformer_ShouldZipValues(int? value, double? otherValue, string expected)
{
static Maybe<string> transformer(int v, double o) => (v + o).ToString().ToMaybe();

var result = value.ToMaybe()
.Zip(otherValue.ToMaybe(), transformer);

result.Should().Be(expected.ToMaybe());
}

[Fact]
public void Zip_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Zip(Maybe<int>.Nothing, (Func<int, int, int>)null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Fact]
public void Zip_MaybeNullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().Zip(Maybe<int>.Nothing, (Func<int, int, Maybe<int>>)null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Theory]
[InlineData(null, null, null)]
[InlineData(null, 2, null)]
[InlineData(2, null, null)]
[InlineData(1, 2, "3")]
public void ZipConsume_WhenBothValuesExist_ShouldExecuteAction(int? value, double? otherValue, string expected)
{
string result = null;
void action(int v, double o) => result = (v + o).ToString();

value.ToMaybe()
.ZipAndConsume(otherValue.ToMaybe(), action);

result.Should().Be(expected);
}

[Fact]
public void ZipAndConsume_NullArgument_ShouldThrow()
{
Action subject = () => 1.ToMaybe().ZipAndConsume(Maybe<int>.Nothing, null);

subject.Should().ThrowExactly<ArgumentNullException>();
}

[Fact]
public void Equals_WhenObjectReferencesAreEqual()
{
Expand Down
32 changes: 9 additions & 23 deletions Maybe/Maybe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace ZBRA.Maybe
/// </summary>
public readonly struct Maybe<T> : IEquatable<Maybe<T>>, IComparable<Maybe<T>>
{
/// <value>A Maybe without a value.</value>
/// <summary>A Maybe without a value.</summary>
public static readonly Maybe<T> Nothing = new Maybe<T>(default, false);

private readonly T obj;
Expand All @@ -25,10 +25,10 @@ internal Maybe(T obj)
HasValue = true;
}

/// <value>True if there is a value present, otherwise false.</value>
/// <summary>True if there is a value present, otherwise false.</summary>
public bool HasValue { get; }

/// <value>The encapsulated value.</value>
/// <summary>The encapsulated value.</summary>
/// <exception cref="Exception">Thrown if HasValue is false.</exception>
public T Value
{
Expand All @@ -47,7 +47,7 @@ public T Value
/// Returns the value or a default.
/// </summary>
/// <returns>
/// The value if HasValue is true, otherwise returns defaultValue
/// The value if HasValue is true, otherwise returns defaultValue
/// </returns>
/// <param name="defaultValue"> The default value.</param>
public T Or(T defaultValue) => HasValue ? obj : defaultValue;
Expand Down Expand Up @@ -137,20 +137,6 @@ public T OrThrow(Func<Exception> errorSupplier)
throw errorSupplier();
}

/// <summary>
/// Applies an action to the value if it's present.
/// </summary>
/// <param name="consumer"> The action to be applied to the value.</param>
public void Consume(Action<T> consumer)
{
consumer = consumer ?? throw new ArgumentNullException(nameof(consumer));

if (HasValue)
{
consumer(obj);
}
}

/// <summary>
/// Determines if the encapsulated value matches another value using Equals.
/// </summary>
Expand Down Expand Up @@ -216,7 +202,7 @@ public bool Equals(Maybe<T> other)
/// Determines if this instance is equals to another obj instance.
/// </summary>
/// <returns>
/// True if obj is an instance of Maybe&lt;<typeparamref name="T"/>&gt;
/// True if obj is an instance of Maybe&lt;<typeparamref name="T"/>&gt;
/// and it matches the current instance using Equals(Maybe&lt;T&gt; other).
/// False otherwise
/// </returns>
Expand All @@ -240,10 +226,10 @@ public bool Equals(Maybe<T> other)
public override string ToString() => HasValue ? Value.ToString() : string.Empty;

/// <summary>
/// Comparison method responsible for ordering or sorting collections of <see cref="Maybe{T}" />. <br/>
/// A return of 0 means that both maybes are equal.<br/>
/// A return of -1 means that this instance of maybe is less than the other maybe being compared.<br/>
/// A return of 1 means that this instance of maybe is greater than the other maybe being compared.<br/>
/// Comparison method responsible for ordering or sorting collections of <see cref="Maybe{T}" />. <br/>
/// A return of 0 means that both maybes are equal.<br/>
/// A return of -1 means that this instance of maybe is less than the other maybe being compared.<br/>
/// A return of 1 means that this instance of maybe is greater than the other maybe being compared.<br/>
/// </summary>
/// <param name="other">The other <see cref="Maybe{T}" /> to compare</param>
/// <returns>
Expand Down
Loading

0 comments on commit 83345d2

Please sign in to comment.