From 43d20e878003ca90805d3da2417cf1a14c614977 Mon Sep 17 00:00:00 2001 From: Dobromir Nikolov Date: Tue, 18 Dec 2018 10:33:10 +0200 Subject: [PATCH 1/2] Added some new async extensions. --- .../OptionTaskExtensions_Either.cs | 68 +++++++++++++++++++ .../OptionTaskExtensions_Maybe.cs | 24 +++++++ 2 files changed, 92 insertions(+) diff --git a/src/Optional.Async/OptionTaskExtensions_Either.cs b/src/Optional.Async/OptionTaskExtensions_Either.cs index ff90197..8840e31 100644 --- a/src/Optional.Async/OptionTaskExtensions_Either.cs +++ b/src/Optional.Async/OptionTaskExtensions_Either.cs @@ -5,6 +5,74 @@ namespace Optional.Async { public static partial class OptionTaskExtensions { + public static Task> FlatMapAsync(this Option option, Func>> mapping, TException exception) => + option.MatchAsync( + some: async val => await mapping(val), + none: async () => Option.None(exception)); + + public static async Task> FilterAsync(this Task> optionTask, Func> predicate, TException exception) => + await (await optionTask).FilterAsync(predicate).WithException(exception); + + public static Task MatchAsync(this Option option, Func> some, Func> none) => + option.Match(some, none); + + public static async Task MatchAsync(this Task> option, Func> some, Func> none) => + await (await option).Match(some, none); + + public static Task MatchAsync(this Option option, Func> some, Func none) => + option.Match( + some: x => some(x), + none: e => Task.FromResult(none(e))); + + public static async Task> SomeNotNullAsync(this Task task, TException exception) => + (await task).SomeNotNull(exception); + + public static Task MatchSomeAsync(this Option option, Func some) => + Task.Run(() => option.MatchSome(v => some(v))); + + public static Task MatchNoneAsync(this Option option, Func none) => + Task.Run(() => option.MatchNone(e => none(e))); + + public static async Task> SomeWhenAsync( + this Task valueTask, + Func predicate, + Func exceptionFactory) + { + var value = await valueTask; + var result = predicate(value); + + return result ? + value.Some() : + Option.None(exceptionFactory(value)); + } + + public static async Task> SomeWhenAsync(this Task task, Func predicate, TException exception) => + (await task).SomeWhen(predicate, exception); + + public static async Task> SomeWhenAsync( + this T value, + Func> predicate, + TException exception) + { + var result = await predicate(value); + + return result ? + value.Some() : + Option.None(exception); + } + + public static async Task> SomeWhenAsync( + this T value, + Func> predicate, + Func exceptionFactory) + { + var result = await predicate(value); + + return result ? + value.Some() : + Option.None(exceptionFactory(value)); + } + public static Task> MapAsync(this Option option, Func> mapping) { if (mapping == null) throw new ArgumentNullException(nameof(mapping)); diff --git a/src/Optional.Async/OptionTaskExtensions_Maybe.cs b/src/Optional.Async/OptionTaskExtensions_Maybe.cs index ae70a15..83ef93e 100644 --- a/src/Optional.Async/OptionTaskExtensions_Maybe.cs +++ b/src/Optional.Async/OptionTaskExtensions_Maybe.cs @@ -5,6 +5,30 @@ namespace Optional.Async { public static partial class OptionTaskExtensions { + public static async Task MatchAsync(this Task> option, Func> some, Func> none) => + await (await option).Match(some, none); + + public static Task MatchAsync(this Option option, Func> some, Func none) => + option.Match(some, none: () => Task.FromResult(none())); + + public static async Task> SomeNotNullAsync(this Task task) => + (await task).SomeNotNull(); + + public static Task MatchSomeAsync(this Option option, Func some) => + Task.Run(() => option.MatchSome(v => some(v))); + + public static Task MatchNoneAsync(this Option option, Func none) => + Task.Run(() => option.MatchNone(() => none())); + + public static Task MatchAsync(this Option option, Func> some, Func> none) => + option.Match(some, none); + + public static Task MatchAsync(this Option option, Func some, Func none) => + Task.Run(() => option.Match(some, none)); + + public static Task MatchAsync(this Option option, Func some, Action none) => + Task.Run(() => option.Match(v => some(v), none)); + public static Task> MapAsync(this Option option, Func> mapping) { if (mapping == null) throw new ArgumentNullException(nameof(mapping)); From 306448d83a44bccd9bbd49938dc83011cd3a1d54 Mon Sep 17 00:00:00 2001 From: Dobromir Nikolov Date: Thu, 20 Dec 2018 14:57:37 +0200 Subject: [PATCH 2/2] Removed the MatchAsync extensions. Added ToAsync. Sorted alphabetically. Added tests. --- src/Optional.Async.Tests/AsyncEitherTests.cs | 72 ++++- src/Optional.Async.Tests/AsyncMaybeTests.cs | 84 ++++++ src/Optional.Async.Tests/ValueGenerator.cs | 7 +- .../OptionTaskExtensions_Either.cs | 258 +++++++----------- .../OptionTaskExtensions_Maybe.cs | 163 ++++++----- src/Optional.sln | 4 +- 6 files changed, 346 insertions(+), 242 deletions(-) diff --git a/src/Optional.Async.Tests/AsyncEitherTests.cs b/src/Optional.Async.Tests/AsyncEitherTests.cs index 11ceaa6..de7565c 100644 --- a/src/Optional.Async.Tests/AsyncEitherTests.cs +++ b/src/Optional.Async.Tests/AsyncEitherTests.cs @@ -1,10 +1,80 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Threading.Tasks; namespace Optional.Async.Tests { [TestClass] public class AsyncEitherTests { + [TestMethod] + public async Task AsyncEither_SomeNotNullAsync() + { + // Arrange + var task = Task.FromResult(null); + var exception = "A truly exceptional string :)"; + + // Act + var result = await task.SomeNotNullAsync(exception); + + // Assert + var expectedResult = (await task).SomeNotNull(exception); + + result.Should().Be(expectedResult); + } + + [TestMethod] + public async Task AsyncEither_SomeWhenAsync() + { + // Arrange + var value = ValueGenerator.RandomString(); + var task = Task.FromResult(value); + + Func predicate = s => s == "This should definitely return false"; + var error = "Error"; + + // Act + var result = await task.SomeWhenAsync(predicate, error); + + // Assert + var expectedResult = (await task).SomeWhen(predicate, error); + + result.Should().Be(expectedResult); + } + + [TestMethod] + public async Task AsyncEither_SomeWhenAsync_With_ExceptionFactory() + { + // Arrange + var value = ValueGenerator.RandomString(); + var task = Task.FromResult(value); + + Func predicate = s => s == "This should definitely return false"; + Func exceptionFactory = _ => 10; + + // Act + var result = await task.SomeWhenAsync(predicate, exceptionFactory); + + // Assert + var expectedResult = (await task).SomeWhen(predicate, exceptionFactory); + + result.Should().Be(expectedResult); + } + + [TestMethod] + public async Task AsyncEither_ToAsync() + { + // Arrange + var option = 10.Some(); + + // Act + var result = option.ToAsync(); + + // Assert + (await result).Should().Be(option); + } + [TestMethod] public void AsyncEither_MapAsync() { diff --git a/src/Optional.Async.Tests/AsyncMaybeTests.cs b/src/Optional.Async.Tests/AsyncMaybeTests.cs index 38223a6..fcb0474 100644 --- a/src/Optional.Async.Tests/AsyncMaybeTests.cs +++ b/src/Optional.Async.Tests/AsyncMaybeTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Optional.Unsafe; +using System; using System.Threading; using System.Threading.Tasks; @@ -9,6 +10,89 @@ namespace Optional.Async.Tests [TestClass] public class AsyncMaybeTests { + [TestMethod] + public async Task AsyncMaybe_ToAsync() + { + // Arrange + var option = 10.Some(); + + // Act + var result = option.ToAsync(); + + // Assert + (await result).Should().Be(option); + } + + [TestMethod] + public async Task AsyncMaybe_FlatMapAsync_To_Exceptional() + { + // Arrange + var option = Option.None(); + var exceptionalOption = Option.None(ValueGenerator.RandomString()); + var error = "Error"; + + // Act + var result = await option.FlatMapAsync(_ => Task.FromResult(exceptionalOption), error); + + // Assert + var expected = Option.None(error); + + result.Should().Be(expected); + } + + [TestMethod] + public async Task AsyncMaybe_FilterAsync_Should_Return_Exception() + { + // Arrange + var optionTask = ValueGenerator.DelayedSome(10); + + Func> predicate = _ => Task.FromResult(false); + var exception = "Error"; + + // Act + var result = await optionTask.FilterAsync(predicate, exception); + + // Assert + var expected = Option.None(exception); + + result.Should().Be(expected); + } + + [TestMethod] + public async Task AsyncMaybe_FilterAsync_Should_Return_Value() + { + // Arrange + var value = 10; + var optionTask = ValueGenerator.DelayedSome(value); + + Func> predicate = _ => Task.FromResult(true); + var exception = "Error"; + + // Act + var result = await optionTask.FilterAsync(predicate, exception); + + // Assert + var expected = Option.Some(value); + + result.Should().Be(expected); + } + + [TestMethod] + public async Task AsyncMaybe_SomeNotNull() + { + // Arrange + object nullObject = null; + var nullTask = Task.FromResult(nullObject); + + // Act + var result = await nullTask.SomeNotNullAsync(); + + // Assert + var expected = nullObject.SomeNotNull(); + + result.Should().Be(expected); + } + [TestMethod] public async Task AsyncMaybe_CanMapAsync() { diff --git a/src/Optional.Async.Tests/ValueGenerator.cs b/src/Optional.Async.Tests/ValueGenerator.cs index d90c244..9ca29e6 100644 --- a/src/Optional.Async.Tests/ValueGenerator.cs +++ b/src/Optional.Async.Tests/ValueGenerator.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Optional.Async.Tests { @@ -10,6 +11,10 @@ public static async Task DelayedValue(T value) return value; } + public static string RandomString() => Guid.NewGuid().ToString(); + public static Func> AsyncOperation(TResult result) => _ => Task.FromResult(result); + public static Func SynchronousOperation(TResult result) => _ => result; + public static Task> DelayedSome(T value) => DelayedValue(value.Some()); public static Task> DelayedSome(T value) => DelayedValue(value.Some()); diff --git a/src/Optional.Async/OptionTaskExtensions_Either.cs b/src/Optional.Async/OptionTaskExtensions_Either.cs index 8840e31..e94a62d 100644 --- a/src/Optional.Async/OptionTaskExtensions_Either.cs +++ b/src/Optional.Async/OptionTaskExtensions_Either.cs @@ -5,136 +5,81 @@ namespace Optional.Async { public static partial class OptionTaskExtensions { - public static Task> FlatMapAsync(this Option option, Func>> mapping, TException exception) => - option.MatchAsync( - some: async val => await mapping(val), - none: async () => Option.None(exception)); - - public static async Task> FilterAsync(this Task> optionTask, Func> predicate, TException exception) => - await (await optionTask).FilterAsync(predicate).WithException(exception); - - public static Task MatchAsync(this Option option, Func> some, Func> none) => - option.Match(some, none); - - public static async Task MatchAsync(this Task> option, Func> some, Func> none) => - await (await option).Match(some, none); - - public static Task MatchAsync(this Option option, Func> some, Func none) => - option.Match( - some: x => some(x), - none: e => Task.FromResult(none(e))); - - public static async Task> SomeNotNullAsync(this Task task, TException exception) => - (await task).SomeNotNull(exception); - - public static Task MatchSomeAsync(this Option option, Func some) => - Task.Run(() => option.MatchSome(v => some(v))); - - public static Task MatchNoneAsync(this Option option, Func none) => - Task.Run(() => option.MatchNone(e => none(e))); - - public static async Task> SomeWhenAsync( - this Task valueTask, - Func predicate, - Func exceptionFactory) - { - var value = await valueTask; - var result = predicate(value); - - return result ? - value.Some() : - Option.None(exceptionFactory(value)); - } - - public static async Task> SomeWhenAsync(this Task task, Func predicate, TException exception) => - (await task).SomeWhen(predicate, exception); - - public static async Task> SomeWhenAsync( - this T value, - Func> predicate, - TException exception) - { - var result = await predicate(value); - - return result ? - value.Some() : - Option.None(exception); - } - - public static async Task> SomeWhenAsync( - this T value, - Func> predicate, - Func exceptionFactory) + public static async Task> ElseAsync(this Option option, Func>> alternativeOptionFactory) { - var result = await predicate(value); + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - return result ? - value.Some() : - Option.None(exceptionFactory(value)); - } + if (option.HasValue) return option; - public static Task> MapAsync(this Option option, Func> mapping) - { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + var alternativeOptionTask = alternativeOptionFactory(); + if (alternativeOptionTask == null) throw new InvalidOperationException($"{nameof(alternativeOptionFactory)} must not return a null task."); - return option.Map(mapping).Match( - some: async valueTask => - { - if (valueTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); - var value = await valueTask.ConfigureAwait(continueOnCapturedContext: false); - return value.Some(); - }, - none: exception => Task.FromResult(Option.None(exception)) - ); + return await alternativeOptionTask.ConfigureAwait(continueOnCapturedContext: false); } - public static async Task> MapAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) + public static async Task> ElseAsync(this Task> optionTask, Func> alternativeOptionFactory, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Map(mapping); + return option.Else(alternativeOptionFactory); } - public static async Task> MapAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) + public static async Task> ElseAsync(this Task> optionTask, Func>> alternativeOptionFactory, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.MapAsync(mapping).ConfigureAwait(continueOnCapturedContext: false); + return await option.ElseAsync(alternativeOptionFactory).ConfigureAwait(continueOnCapturedContext: false); } - public static Task> MapExceptionAsync(this Option option, Func> mapping) + public static Task> FilterAsync(this Option option, Func> predicate, TException exception) => + option.FilterAsync(predicate, () => exception); + + public static Task> FilterAsync(this Option option, Func> predicate, Func exceptionFactory) { - return option.MapException(mapping).Match( - some: value => Task.FromResult(Option.Some(value)), - none: async exceptionTask => + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); + + return option.Match( + some: async value => { - if (exceptionTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); - var exception = await exceptionTask.ConfigureAwait(continueOnCapturedContext: false); - return Option.None(exception); - } + var predicateTask = predicate(value); + if (predicateTask == null) throw new InvalidOperationException($"{nameof(predicate)} must not return a null task."); + + var condition = await predicateTask.ConfigureAwait(continueOnCapturedContext: false); + return option.Filter(condition, exceptionFactory); + }, + none: _ => Task.FromResult(option) ); } - public static async Task> MapExceptionAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) + public static Task> FilterAsync(this Task> optionTask, Func predicate, TException exception, bool executeOnCapturedContext = false) => + optionTask.FilterAsync(predicate, () => exception, executeOnCapturedContext); + + public static async Task> FilterAsync(this Task> optionTask, Func predicate, Func exceptionFactory, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.MapException(mapping); + return option.Filter(predicate, exceptionFactory); } - public static async Task> MapExceptionAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) + public static Task> FilterAsync(this Task> optionTask, Func> predicate, TException exception, bool executeOnCapturedContext = false) => + optionTask.FilterAsync(predicate, () => exception, executeOnCapturedContext); + + public static async Task> FilterAsync(this Task> optionTask, Func> predicate, Func exceptionFactory, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.MapExceptionAsync(mapping).ConfigureAwait(false); + return await option.FilterAsync(predicate, exceptionFactory).ConfigureAwait(continueOnCapturedContext: false); } public static Task> FlatMapAsync(this Option option, Func>> mapping) @@ -211,51 +156,76 @@ public static async Task> FlatMapAsync> FilterAsync(this Option option, Func> predicate, TException exception) => - option.FilterAsync(predicate, () => exception); + public static async Task> FlattenAsync(this Task, TException>> optionTask) + { + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - public static Task> FilterAsync(this Option option, Func> predicate, Func exceptionFactory) + var option = await optionTask.ConfigureAwait(continueOnCapturedContext: false); + return option.Flatten(); + } + + public static Task> MapAsync(this Option option, Func> mapping) { - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - return option.Match( - some: async value => + return option.Map(mapping).Match( + some: async valueTask => { - var predicateTask = predicate(value); - if (predicateTask == null) throw new InvalidOperationException($"{nameof(predicate)} must not return a null task."); - - var condition = await predicateTask.ConfigureAwait(continueOnCapturedContext: false); - return option.Filter(condition, exceptionFactory); + if (valueTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); + var value = await valueTask.ConfigureAwait(continueOnCapturedContext: false); + return value.Some(); }, - none: _ => Task.FromResult(option) + none: exception => Task.FromResult(Option.None(exception)) ); } - public static Task> FilterAsync(this Task> optionTask, Func predicate, TException exception, bool executeOnCapturedContext = false) => - optionTask.FilterAsync(predicate, () => exception, executeOnCapturedContext); + public static async Task> MapAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) + { + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - public static async Task> FilterAsync(this Task> optionTask, Func predicate, Func exceptionFactory, bool executeOnCapturedContext = false) + var option = await optionTask.ConfigureAwait(executeOnCapturedContext); + return option.Map(mapping); + } + + public static async Task> MapAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Filter(predicate, exceptionFactory); + return await option.MapAsync(mapping).ConfigureAwait(continueOnCapturedContext: false); } - public static Task> FilterAsync(this Task> optionTask, Func> predicate, TException exception, bool executeOnCapturedContext = false) => - optionTask.FilterAsync(predicate, () => exception, executeOnCapturedContext); + public static Task> MapExceptionAsync(this Option option, Func> mapping) + { + return option.MapException(mapping).Match( + some: value => Task.FromResult(Option.Some(value)), + none: async exceptionTask => + { + if (exceptionTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); + var exception = await exceptionTask.ConfigureAwait(continueOnCapturedContext: false); + return Option.None(exception); + } + ); + } - public static async Task> FilterAsync(this Task> optionTask, Func> predicate, Func exceptionFactory, bool executeOnCapturedContext = false) + public static async Task> MapExceptionAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - if (exceptionFactory == null) throw new ArgumentNullException(nameof(exceptionFactory)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.FilterAsync(predicate, exceptionFactory).ConfigureAwait(continueOnCapturedContext: false); + return option.MapException(mapping); + } + + public static async Task> MapExceptionAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) + { + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + + var option = await optionTask.ConfigureAwait(executeOnCapturedContext); + return await option.MapExceptionAsync(mapping).ConfigureAwait(false); } public static Task> NotNullAsync(this Task> optionTask, TException exception) => @@ -299,35 +269,23 @@ public static async Task> OrAsync(this Task return await option.OrAsync(alternativeFactory).ConfigureAwait(continueOnCapturedContext: false); } - public static async Task> ElseAsync(this Option option, Func>> alternativeOptionFactory) - { - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - - if (option.HasValue) return option; - - var alternativeOptionTask = alternativeOptionFactory(); - if (alternativeOptionTask == null) throw new InvalidOperationException($"{nameof(alternativeOptionFactory)} must not return a null task."); - - return await alternativeOptionTask.ConfigureAwait(continueOnCapturedContext: false); - } + public static async Task> SomeNotNullAsync(this Task task, TException exception) => + (await task).SomeNotNull(exception); - public static async Task> ElseAsync(this Task> optionTask, Func> alternativeOptionFactory, bool executeOnCapturedContext = false) + public static async Task> SomeWhenAsync(this Task valueTask, Func predicate, Func exceptionFactory) { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); + var value = await valueTask; + var result = predicate(value); - var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Else(alternativeOptionFactory); + return result ? + value.Some() : + Option.None(exceptionFactory(value)); } - public static async Task> ElseAsync(this Task> optionTask, Func>> alternativeOptionFactory, bool executeOnCapturedContext = false) - { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); + public static async Task> SomeWhenAsync(this Task task, Func predicate, TException exception) => + (await task).SomeWhen(predicate, exception); - var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.ElseAsync(alternativeOptionFactory).ConfigureAwait(continueOnCapturedContext: false); - } + public static Task> ToAsync(this Option option) => Task.FromResult(option); public static async Task> WithoutExceptionAsync(this Task> optionTask) { @@ -336,13 +294,5 @@ public static async Task> WithoutExceptionAsync(this Ta var option = await optionTask.ConfigureAwait(false); return option.WithoutException(); } - - public static async Task> FlattenAsync(this Task, TException>> optionTask) - { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - - var option = await optionTask.ConfigureAwait(continueOnCapturedContext: false); - return option.Flatten(); - } } -} +} \ No newline at end of file diff --git a/src/Optional.Async/OptionTaskExtensions_Maybe.cs b/src/Optional.Async/OptionTaskExtensions_Maybe.cs index 83ef93e..b4d9a43 100644 --- a/src/Optional.Async/OptionTaskExtensions_Maybe.cs +++ b/src/Optional.Async/OptionTaskExtensions_Maybe.cs @@ -5,61 +5,86 @@ namespace Optional.Async { public static partial class OptionTaskExtensions { - public static async Task MatchAsync(this Task> option, Func> some, Func> none) => - await (await option).Match(some, none); + public static async Task> ElseAsync(this Option option, Func>> alternativeOptionFactory) + { + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - public static Task MatchAsync(this Option option, Func> some, Func none) => - option.Match(some, none: () => Task.FromResult(none())); + if (option.HasValue) return option; - public static async Task> SomeNotNullAsync(this Task task) => - (await task).SomeNotNull(); + var alternativeOptionTask = alternativeOptionFactory(); + if (alternativeOptionTask == null) throw new InvalidOperationException($"{nameof(alternativeOptionFactory)} must not return a null task."); - public static Task MatchSomeAsync(this Option option, Func some) => - Task.Run(() => option.MatchSome(v => some(v))); + return await alternativeOptionTask.ConfigureAwait(continueOnCapturedContext: false); + } - public static Task MatchNoneAsync(this Option option, Func none) => - Task.Run(() => option.MatchNone(() => none())); + public static async Task> ElseAsync(this Task> optionTask, Func> alternativeOptionFactory, bool executeOnCapturedContext = false) + { + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - public static Task MatchAsync(this Option option, Func> some, Func> none) => - option.Match(some, none); + var option = await optionTask.ConfigureAwait(executeOnCapturedContext); + return option.Else(alternativeOptionFactory); + } - public static Task MatchAsync(this Option option, Func some, Func none) => - Task.Run(() => option.Match(some, none)); + public static async Task> ElseAsync(this Task> optionTask, Func>> alternativeOptionFactory, bool executeOnCapturedContext = false) + { + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); + if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - public static Task MatchAsync(this Option option, Func some, Action none) => - Task.Run(() => option.Match(v => some(v), none)); + var option = await optionTask.ConfigureAwait(executeOnCapturedContext); + return await option.ElseAsync(alternativeOptionFactory).ConfigureAwait(continueOnCapturedContext: false); + } - public static Task> MapAsync(this Option option, Func> mapping) + public static async Task> FilterAsync(this Task> optionTask, Func> predicate, TException exception) => + await (await optionTask).FilterAsync(predicate).WithExceptionAsync(exception); + + public static Task> FilterAsync(this Option option, Func> predicate) { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - return option.Map(mapping).Match( - some: async valueTask => + return option.Match( + some: async value => { - if (valueTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); - var value = await valueTask.ConfigureAwait(continueOnCapturedContext: false); - return value.Some(); + var predicateTask = predicate(value); + if (predicateTask == null) throw new InvalidOperationException($"{nameof(predicate)} must not return a null task."); + + var condition = await predicateTask.ConfigureAwait(continueOnCapturedContext: false); + return option.Filter(condition); }, - none: () => Task.FromResult(Option.None()) + none: () => Task.FromResult(option) ); } - public static async Task> MapAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) + public static async Task> FilterAsync(this Task> optionTask, Func predicate, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Map(mapping); + return option.Filter(predicate); } - public static async Task> MapAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) + public static async Task> FilterAsync(this Task> optionTask, Func> predicate, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.MapAsync(mapping).ConfigureAwait(continueOnCapturedContext: false); + return await option.FilterAsync(predicate).ConfigureAwait(continueOnCapturedContext: false); + } + + public static Task> FlatMapAsync(this Option option, Func>> mapping, TException exception) + { + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + + return option.Map(mapping).Match( + some: resultOptionTask => + { + if (resultOptionTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); + return resultOptionTask; + }, + none: () => Task.FromResult(Option.None(exception)) + ); } public static Task> FlatMapAsync(this Option option, Func>> mapping) @@ -124,39 +149,45 @@ public static async Task> FlatMapAsync(t return await option.FlatMapAsync(mapping).ConfigureAwait(continueOnCapturedContext: false); } - public static Task> FilterAsync(this Option option, Func> predicate) + public static async Task> FlattenAsync(this Task>> optionTask) { - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - return option.Match( - some: async value => - { - var predicateTask = predicate(value); - if (predicateTask == null) throw new InvalidOperationException($"{nameof(predicate)} must not return a null task."); + var option = await optionTask.ConfigureAwait(continueOnCapturedContext: false); + return option.Flatten(); + } - var condition = await predicateTask.ConfigureAwait(continueOnCapturedContext: false); - return option.Filter(condition); + public static Task> MapAsync(this Option option, Func> mapping) + { + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + + return option.Map(mapping).Match( + some: async valueTask => + { + if (valueTask == null) throw new InvalidOperationException($"{nameof(mapping)} must not return a null task."); + var value = await valueTask.ConfigureAwait(continueOnCapturedContext: false); + return value.Some(); }, - none: () => Task.FromResult(option) + none: () => Task.FromResult(Option.None()) ); } - public static async Task> FilterAsync(this Task> optionTask, Func predicate, bool executeOnCapturedContext = false) + public static async Task> MapAsync(this Task> optionTask, Func mapping, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Filter(predicate); + return option.Map(mapping); } - public static async Task> FilterAsync(this Task> optionTask, Func> predicate, bool executeOnCapturedContext = false) + public static async Task> MapAsync(this Task> optionTask, Func> mapping, bool executeOnCapturedContext = false) { if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.FilterAsync(predicate).ConfigureAwait(continueOnCapturedContext: false); + return await option.MapAsync(mapping).ConfigureAwait(continueOnCapturedContext: false); } public static Task> NotNullAsync(this Task> optionTask) @@ -196,35 +227,9 @@ public static async Task> OrAsync(this Task> optionTask, return await option.OrAsync(alternativeFactory).ConfigureAwait(continueOnCapturedContext: false); } - public static async Task> ElseAsync(this Option option, Func>> alternativeOptionFactory) - { - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - - if (option.HasValue) return option; - - var alternativeOptionTask = alternativeOptionFactory(); - if (alternativeOptionTask == null) throw new InvalidOperationException($"{nameof(alternativeOptionFactory)} must not return a null task."); - - return await alternativeOptionTask.ConfigureAwait(continueOnCapturedContext: false); - } - - public static async Task> ElseAsync(this Task> optionTask, Func> alternativeOptionFactory, bool executeOnCapturedContext = false) - { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - - var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return option.Else(alternativeOptionFactory); - } + public static async Task> SomeNotNullAsync(this Task task) => (await task).SomeNotNull(); - public static async Task> ElseAsync(this Task> optionTask, Func>> alternativeOptionFactory, bool executeOnCapturedContext = false) - { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - if (alternativeOptionFactory == null) throw new ArgumentNullException(nameof(alternativeOptionFactory)); - - var option = await optionTask.ConfigureAwait(executeOnCapturedContext); - return await option.ElseAsync(alternativeOptionFactory).ConfigureAwait(continueOnCapturedContext: false); - } + public static Task> ToAsync(this Option option) => Task.FromResult(option); public static Task> WithExceptionAsync(this Task> optionTask, TException exception) => optionTask.WithExceptionAsync(() => exception); @@ -237,13 +242,5 @@ public static async Task> WithExceptionAsync> FlattenAsync(this Task>> optionTask) - { - if (optionTask == null) throw new ArgumentNullException(nameof(optionTask)); - - var option = await optionTask.ConfigureAwait(continueOnCapturedContext: false); - return option.Flatten(); - } } -} +} \ No newline at end of file diff --git a/src/Optional.sln b/src/Optional.sln index 12eb394..ecbcc98 100644 --- a/src/Optional.sln +++ b/src/Optional.sln @@ -17,7 +17,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Optional.Sandbox", "Optiona EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Optional.Samples", "Optional.Samples\Optional.Samples.csproj", "{02A7D729-3C90-45E2-8183-FC10BA760A80}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Optional.Async.Tests", "Optional.Async.Tests\Optional.Async.Tests.csproj", "{89D005BD-46B2-4E79-8267-AAB77C818998}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Optional.Async.Tests", "Optional.Async.Tests\Optional.Async.Tests.csproj", "{89D005BD-46B2-4E79-8267-AAB77C818998}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,7 +34,6 @@ Global {A61CE069-370F-474A-B925-C17B74F9198C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A61CE069-370F-474A-B925-C17B74F9198C}.Release|Any CPU.Build.0 = Release|Any CPU {F00BED6E-4808-48EE-B14E-092182080EB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F00BED6E-4808-48EE-B14E-092182080EB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {F00BED6E-4808-48EE-B14E-092182080EB3}.Release|Any CPU.ActiveCfg = Release|Any CPU {F00BED6E-4808-48EE-B14E-092182080EB3}.Release|Any CPU.Build.0 = Release|Any CPU {12606143-9C9B-4A92-81D5-CEA814AC64AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -46,7 +45,6 @@ Global {9ADFB283-560D-4FD1-981C-3348BF1DDAF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {9ADFB283-560D-4FD1-981C-3348BF1DDAF4}.Release|Any CPU.Build.0 = Release|Any CPU {02A7D729-3C90-45E2-8183-FC10BA760A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02A7D729-3C90-45E2-8183-FC10BA760A80}.Debug|Any CPU.Build.0 = Debug|Any CPU {02A7D729-3C90-45E2-8183-FC10BA760A80}.Release|Any CPU.ActiveCfg = Release|Any CPU {02A7D729-3C90-45E2-8183-FC10BA760A80}.Release|Any CPU.Build.0 = Release|Any CPU {89D005BD-46B2-4E79-8267-AAB77C818998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU