Skip to content

Commit 1ca696d

Browse files
authored
bool.Then() (#27)
* BoolExtensions * Async * Adding justfile * Test coverage
1 parent b7e0935 commit 1ca696d

15 files changed

+447
-65
lines changed

justfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Just: https://github.com/casey/just
2+
3+
# Select a target to run
4+
default:
5+
@just --choose
6+
7+
# Build the project
8+
build *FLAGS:
9+
dotnet build {{FLAGS}} src/
10+
11+
# Generate a NuGet package
12+
pack:
13+
dotnet pack -c Release src/
14+
15+
# Run tests in .NET 8.0
16+
test:
17+
dotnet test -f net8.0 src/
18+
19+
# Clean build artifacts
20+
clean:
21+
dotnet clean src/
22+
23+
# Build documentation
24+
doc:
25+
dotnet build -c Release src/
26+
docfx metadata docs/docfx.json
27+
docfx build docs/docfx.json

src/RustyOptions.FSharp/RustyOptions.FSharp.fsproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
55
<GenerateDocumentationFile>true</GenerateDocumentationFile>
66
<AssemblyName>RustyOptions.FSharp</AssemblyName>
7-
<Version>0.10.0</Version>
8-
<ReleaseVersion>0.10.0</ReleaseVersion>
7+
<Version>0.10.1</Version>
8+
<ReleaseVersion>0.10.1</ReleaseVersion>
99
<PackageId>RustyOptions.FSharp</PackageId>
1010
<Authors>Joel Mueller</Authors>
1111
<Company></Company>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using RustyOptions.Async;
2+
3+
namespace RustyOptions.Tests;
4+
5+
#pragma warning disable CA1707 // Identifiers should not contain underscores
6+
7+
public class BoolExtensionsTests
8+
{
9+
[Fact]
10+
public void Then_ReturnsOptionWithValue_WhenBooleanIsTrue()
11+
{
12+
// Arrange
13+
bool condition = true;
14+
15+
// Act
16+
var result = condition.Then(() => 42);
17+
18+
// Assert
19+
Assert.True(result.IsSome(out int resultVal));
20+
Assert.Equal(42, resultVal);
21+
}
22+
23+
[Fact]
24+
public void Then_ReturnsDefaultOption_WhenBooleanIsFalse()
25+
{
26+
// Arrange
27+
bool condition = false;
28+
29+
// Act
30+
var result = condition.Then(() => 42);
31+
32+
// Assert
33+
Assert.True(result.IsNone);
34+
}
35+
36+
[Fact]
37+
public void ThenSome_ReturnsOptionWithValue_WhenBooleanIsTrue()
38+
{
39+
// Arrange
40+
bool condition = true;
41+
int value = 42;
42+
43+
// Act
44+
var result = condition.ThenSome(value);
45+
46+
// Assert
47+
Assert.True(result.IsSome(out int resultVal));
48+
Assert.Equal(42, resultVal);
49+
}
50+
51+
[Fact]
52+
public void ThenSome_ReturnsDefaultOption_WhenBooleanIsFalse()
53+
{
54+
// Arrange
55+
bool condition = false;
56+
int value = 42;
57+
58+
// Act
59+
var result = condition.ThenSome(value);
60+
61+
// Assert
62+
Assert.True(result.IsNone);
63+
}
64+
65+
[Fact]
66+
public async Task ThenAsync_Task_ReturnsDefaultWhenFalse()
67+
{
68+
bool condition = false;
69+
var result = await condition.ThenAsync(() => Task.FromResult("Test"));
70+
Assert.True(result.IsNone);
71+
}
72+
73+
[Fact]
74+
public async Task ThenAsync_Task_ReturnsResultWhenTrue()
75+
{
76+
bool condition = true;
77+
var result = await condition.ThenAsync(() => Task.FromResult("Test"));
78+
Assert.True(result.IsSome(out var resultVal));
79+
Assert.Equal("Test", resultVal);
80+
}
81+
82+
[Fact]
83+
public async Task ThenAsync_ValueTask_ReturnsDefaultWhenFalse()
84+
{
85+
bool condition = false;
86+
var result = await condition.ThenAsync(() => new ValueTask<string>("Test"));
87+
Assert.True(result.IsNone);
88+
}
89+
90+
[Fact]
91+
public async Task ThenAsync_ValueTask_ReturnsResultWhenTrue()
92+
{
93+
bool condition = true;
94+
var result = await condition.ThenAsync(() => new ValueTask<int>(42));
95+
Assert.True(result.IsSome(out var resultVal));
96+
Assert.Equal(42, resultVal);
97+
}
98+
99+
[Fact]
100+
public async Task ThenSomeAsync_Task_WhenTrue_ShouldReturnOptionWithValue()
101+
{
102+
bool condition = true;
103+
var result = await condition.ThenSomeAsync(Task.FromResult("Hello"));
104+
105+
Assert.True(result.IsSome(out var value));
106+
Assert.Equal("Hello", value);
107+
}
108+
109+
[Fact]
110+
public async Task ThenSomeAsync_Task_NotCompleted_WhenTrue_ShouldReturnOptionWithValue()
111+
{
112+
bool condition = true;
113+
var result = await condition.ThenSomeAsync(GetTask("Hello"));
114+
115+
Assert.True(result.IsSome(out var value));
116+
Assert.Equal("Hello", value);
117+
}
118+
119+
[Fact]
120+
public async Task ThenSomeAsync_Task_WhenFalse_ShouldReturnDefaultOption()
121+
{
122+
bool condition = false;
123+
var result = await condition.ThenSomeAsync(Task.FromResult("Hello"));
124+
125+
Assert.True(result.IsNone);
126+
}
127+
128+
[Fact]
129+
public async Task ThenSomeAsync_ValueTask_WhenTrue_ShouldReturnOptionWithValue()
130+
{
131+
bool condition = true;
132+
var result = await condition.ThenSomeAsync(new ValueTask<string>("Hello"));
133+
134+
Assert.True(result.IsSome(out var value));
135+
Assert.Equal("Hello", value);
136+
}
137+
138+
[Fact]
139+
public async Task ThenSomeAsync_ValueTask_NotCompleted_WhenTrue_ShouldReturnOptionWithValue()
140+
{
141+
bool condition = true;
142+
var result = await condition.ThenSomeAsync(GetValueTask("Hello"));
143+
144+
Assert.True(result.IsSome(out var value));
145+
Assert.Equal("Hello", value);
146+
}
147+
148+
[Fact]
149+
public async Task ThenSomeAsync_ValueTask_WhenFalse_ShouldReturnDefaultOption()
150+
{
151+
bool condition = false;
152+
var result = await condition.ThenSomeAsync(new ValueTask<string>("Hello"));
153+
154+
Assert.True(result.IsNone);
155+
}
156+
157+
private static async Task<T> GetTask<T>(T value)
158+
{
159+
await Task.Delay(1);
160+
return value;
161+
}
162+
163+
private static async ValueTask<T> GetValueTask<T>(T value)
164+
{
165+
await Task.Delay(1);
166+
return value;
167+
}
168+
}
169+
170+
#pragma warning restore CA1707 // Identifiers should not contain underscores

src/RustyOptions.Tests/OptionAsyncTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Runtime.CompilerServices;
2+
using System.Diagnostics.CodeAnalysis;
23
using RustyOptions.Async;
34
using static RustyOptions.Option;
45

@@ -68,6 +69,7 @@ public async Task CanGetFirstOrNoneWithCancelAsync()
6869

6970
[Theory]
7071
[MemberData(nameof(GetMapAsyncValues))]
72+
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method", Justification = "Will not block")]
7173
public async Task CanMapAsync(object source, object mapper, Option<long> expected)
7274
{
7375
switch ((source, mapper))
@@ -80,21 +82,27 @@ public async Task CanMapAsync(object source, object mapper, Option<long> expecte
8082
break;
8183
case (ValueTask<Option<int>> src, Func<int, long> mpr):
8284
Assert.Equal(expected, await src.MapAsync(mpr));
85+
Assert.Equal(expected, await GetValueTask(src.Result).MapAsync(mpr));
8386
break;
8487
case (Task<Option<int>> src, Func<int, long> mpr):
8588
Assert.Equal(expected, await src.MapAsync(mpr));
89+
Assert.Equal(expected, await GetTask(src.Result).MapAsync(mpr));
8690
break;
8791
case (ValueTask<Option<int>> src, Func<int, ValueTask<long>> mpr):
8892
Assert.Equal(expected, await src.MapAsync(mpr));
93+
Assert.Equal(expected, await GetValueTask(src.Result).MapAsync(mpr));
8994
break;
9095
case (Task<Option<int>> src, Func<int, ValueTask<long>> mpr):
9196
Assert.Equal(expected, await src.MapAsync(mpr));
97+
Assert.Equal(expected, await GetTask(src.Result).MapAsync(mpr));
9298
break;
9399
case (ValueTask<Option<int>> src, Func<int, Task<long>> mpr):
94100
Assert.Equal(expected, await src.MapAsync(mpr));
101+
Assert.Equal(expected, await GetValueTask(src.Result).MapAsync(mpr));
95102
break;
96103
case (Task<Option<int>> src, Func<int, Task<long>> mpr):
97104
Assert.Equal(expected, await src.MapAsync(mpr));
105+
Assert.Equal(expected, await GetTask(src.Result).MapAsync(mpr));
98106
break;
99107

100108
default:
@@ -139,6 +147,7 @@ public static IEnumerable<object[]> GetMapAsyncValues()
139147

140148
[Theory]
141149
[MemberData(nameof(GetMapOrElseAsyncValues))]
150+
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method", Justification = "Will not block")]
142151
public async Task CanMapOrElseAsync(object source, object mapper, object defaultFactory, long expected)
143152
{
144153
switch ((source, mapper, defaultFactory))
@@ -151,21 +160,27 @@ public async Task CanMapOrElseAsync(object source, object mapper, object default
151160
break;
152161
case (ValueTask<Option<int>> src, Func<int, long> mpr, Func<long> dff):
153162
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
163+
Assert.Equal(expected, await GetValueTask(src.Result).MapOrElseAsync(mpr, dff));
154164
break;
155165
case (Task<Option<int>> src, Func<int, long> mpr, Func<long> dff):
156166
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
167+
Assert.Equal(expected, await GetTask(src.Result).MapOrElseAsync(mpr, dff));
157168
break;
158169
case (ValueTask<Option<int>> src, Func<int, ValueTask<long>> mpr, Func<ValueTask<long>> dff):
159170
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
171+
Assert.Equal(expected, await GetValueTask(src.Result).MapOrElseAsync(mpr, dff));
160172
break;
161173
case (Task<Option<int>> src, Func<int, ValueTask<long>> mpr, Func<ValueTask<long>> dff):
162174
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
175+
Assert.Equal(expected, await GetTask(src.Result).MapOrElseAsync(mpr, dff));
163176
break;
164177
case (ValueTask<Option<int>> src, Func<int, Task<long>> mpr, Func<Task<long>> dff):
165178
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
179+
Assert.Equal(expected, await GetValueTask(src.Result).MapOrElseAsync(mpr, dff));
166180
break;
167181
case (Task<Option<int>> src, Func<int, Task<long>> mpr, Func<Task<long>> dff):
168182
Assert.Equal(expected, await src.MapOrElseAsync(mpr, dff));
183+
Assert.Equal(expected, await GetTask(src.Result).MapOrElseAsync(mpr, dff));
169184
break;
170185

171186
default:
@@ -215,6 +230,7 @@ public static IEnumerable<object[]> GetMapOrElseAsyncValues()
215230

216231
[Theory]
217232
[MemberData(nameof(GetAndThenAsyncValues))]
233+
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method", Justification = "Will not block")]
218234
public async Task CanAndThenAsync(object source, object mapper, Option<long> expected)
219235
{
220236
switch ((source, mapper))
@@ -227,21 +243,27 @@ public async Task CanAndThenAsync(object source, object mapper, Option<long> exp
227243
break;
228244
case (ValueTask<Option<int>> src, Func<int, Option<long>> mpr):
229245
Assert.Equal(expected, await src.AndThenAsync(mpr));
246+
Assert.Equal(expected, await GetValueTask(src.Result).AndThenAsync(mpr));
230247
break;
231248
case (Task<Option<int>> src, Func<int, Option<long>> mpr):
232249
Assert.Equal(expected, await src.AndThenAsync(mpr));
250+
Assert.Equal(expected, await GetTask(src.Result).AndThenAsync(mpr));
233251
break;
234252
case (ValueTask<Option<int>> src, Func<int, ValueTask<Option<long>>> mpr):
235253
Assert.Equal(expected, await src.AndThenAsync(mpr));
254+
Assert.Equal(expected, await GetValueTask(src.Result).AndThenAsync(mpr));
236255
break;
237256
case (Task<Option<int>> src, Func<int, ValueTask<Option<long>>> mpr):
238257
Assert.Equal(expected, await src.AndThenAsync(mpr));
258+
Assert.Equal(expected, await GetTask(src.Result).AndThenAsync(mpr));
239259
break;
240260
case (ValueTask<Option<int>> src, Func<int, Task<Option<long>>> mpr):
241261
Assert.Equal(expected, await src.AndThenAsync(mpr));
262+
Assert.Equal(expected, await GetValueTask(src.Result).AndThenAsync(mpr));
242263
break;
243264
case (Task<Option<int>> src, Func<int, Task<Option<long>>> mpr):
244265
Assert.Equal(expected, await src.AndThenAsync(mpr));
266+
Assert.Equal(expected, await GetTask(src.Result).AndThenAsync(mpr));
245267
break;
246268

247269
default:
@@ -286,6 +308,7 @@ public static IEnumerable<object[]> GetAndThenAsyncValues()
286308

287309
[Theory]
288310
[MemberData(nameof(GetOrElseAsyncValues))]
311+
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method", Justification = "Will not block")]
289312
public async Task CanOrElseAsync(object source, object mapper, Option<int> expected)
290313
{
291314
switch ((source, mapper))
@@ -298,21 +321,27 @@ public async Task CanOrElseAsync(object source, object mapper, Option<int> expec
298321
break;
299322
case (ValueTask<Option<int>> src, Func<Option<int>> mpr):
300323
Assert.Equal(expected, await src.OrElseAsync(mpr));
324+
Assert.Equal(expected, await GetValueTask(src.Result).OrElseAsync(mpr));
301325
break;
302326
case (Task<Option<int>> src, Func<Option<int>> mpr):
303327
Assert.Equal(expected, await src.OrElseAsync(mpr));
328+
Assert.Equal(expected, await GetTask(src.Result).OrElseAsync(mpr));
304329
break;
305330
case (ValueTask<Option<int>> src, Func<ValueTask<Option<int>>> mpr):
306331
Assert.Equal(expected, await src.OrElseAsync(mpr));
332+
Assert.Equal(expected, await GetValueTask(src.Result).OrElseAsync(mpr));
307333
break;
308334
case (Task<Option<int>> src, Func<ValueTask<Option<int>>> mpr):
309335
Assert.Equal(expected, await src.OrElseAsync(mpr));
336+
Assert.Equal(expected, await GetTask(src.Result).OrElseAsync(mpr));
310337
break;
311338
case (ValueTask<Option<int>> src, Func<Task<Option<int>>> mpr):
312339
Assert.Equal(expected, await src.OrElseAsync(mpr));
340+
Assert.Equal(expected, await GetValueTask(src.Result).OrElseAsync(mpr));
313341
break;
314342
case (Task<Option<int>> src, Func<Task<Option<int>>> mpr):
315343
Assert.Equal(expected, await src.OrElseAsync(mpr));
344+
Assert.Equal(expected, await GetTask(src.Result).OrElseAsync(mpr));
316345
break;
317346

318347
default:
@@ -382,4 +411,16 @@ private static async IAsyncEnumerable<Option<int>> EnumerateFilteredAsync(int ex
382411
yield return predicate(i) ? Some(i) : None<int>();
383412
}
384413
}
414+
415+
private static async Task<T> GetTask<T>(T value)
416+
{
417+
await Task.Delay(1);
418+
return value;
419+
}
420+
421+
private static async ValueTask<T> GetValueTask<T>(T value)
422+
{
423+
await Task.Delay(1);
424+
return value;
425+
}
385426
}

src/RustyOptions.Tests/OptionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ public void CanAndThen()
403403
Assert.Equal(None<int>(), someOtherStr.AndThen(ParseInt));
404404

405405
static Option<int> ParseInt(string s)
406-
=> int.TryParse(s, out int parsed) ? Some(parsed) : None<int>();
406+
=> int.TryParse(s, out int parsed).ThenSome(parsed);
407407
}
408408

409409
[Fact]

0 commit comments

Comments
 (0)