diff --git a/README.md b/README.md index d943eed..37f4e60 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,29 @@ Task.FromException(new ArgumentException()) )) ``` +#### InvokeIf + +`InvokeIf` can be used to conditionally invoke a function. This is most useful for side effects. + +```c# +Task.FromResult(someUrl) + .Then(httpClient.GetAsync) + .IfFulfilled(TaskExtras.InvokeIf( + httpResponse => !httpResponse.IsSuccessStatusCode, + httpResponse => _logger.LogWarning("Got '{StatusCode}' response from server", httpResponse.StatusCode) + ); +``` + +However, it can be used with `.Then` as well: + +```c# +Task.FromResult(4) + .Then(InvokeIf( + value => value % 2 == 0, + value => value + 1 + ); +``` + [bluebird]: http://bluebirdjs.com/docs/getting-started.html [MONADS.md]: ./MONADS.md [nuget.org]: https://www.nuget.org/packages/RLC.TaskChaining/ diff --git a/src/TaskExtras.cs b/src/TaskExtras.cs index 43ffdb3..4a15233 100644 --- a/src/TaskExtras.cs +++ b/src/TaskExtras.cs @@ -131,6 +131,138 @@ Func> resolutionSupplier ? resolutionSupplier(value) : Task.FromException(value); + /// + /// Invokes if succeeds. + /// + /// This is useful for conditionally executing a side effect. + /// + /// Task.FromResult(someUrl) + /// .Then(httpClient.GetAsync) + /// .IfFulfilled(InvokeIf( + /// httpResponse => !httpResponse.IsSuccessStatusCode, + /// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode) + /// ); + /// + /// + /// A predicate to evaluate with the 's value. + /// The function to invoke if returns true. + /// The type passed into InvokeIf. + /// A function that conditionally invokes another function. + public static Func> InvokeIf( + Predicate predicate, + Action action + ) + { + return value => + { + if (predicate(value)) + { + action(value); + } + + return Task.FromResult(value); + }; + } + + /// + /// Invokes if succeeds. + /// + /// This is useful for conditionally executing a side effect. + /// + /// Task.FromResult(someUrl) + /// .Then(httpClient.GetAsync) + /// .IfFulfilled(InvokeIf( + /// httpResponse => !httpResponse.IsSuccessStatusCode, + /// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode) + /// ); + /// + /// + /// A predicate to evaluate with the 's value. + /// The function to invoke if returns true. + /// The type passed into InvokeIf. + /// A function that conditionally invokes another function. + public static Func> InvokeIf( + Predicate predicate, + Func func + ) + { + return value => + { + if (predicate(value)) + { + return Task.FromResult(func(value)); + } + + return Task.FromResult(value); + }; + } + + /// + /// Invokes if succeeds. + /// + /// This is useful for conditionally executing a side effect. + /// + /// Task.FromResult(someUrl) + /// .Then(httpClient.GetAsync) + /// .IfFulfilled(InvokeIf( + /// httpResponse => !httpResponse.IsSuccessStatusCode, + /// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode) + /// ); + /// + /// + /// A predicate to evaluate with the 's value. + /// The function to invoke if returns true. + /// The type passed into InvokeIf. + /// A function that conditionally invokes another function. + public static Func> InvokeIf( + Predicate predicate, + Func> func + ) + { + return value => + { + if (predicate(value)) + { + return func(value); + } + + return Task.FromResult(value); + }; + } + + /// + /// Invokes if succeeds. + /// + /// This is useful for conditionally executing a side effect. + /// + /// Task.FromResult(someUrl) + /// .Then(httpClient.GetAsync) + /// .IfFulfilled(InvokeIf( + /// httpResponse => !httpResponse.IsSuccessStatusCode, + /// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode) + /// ); + /// + /// + /// A predicate to evaluate with the 's value. + /// The function to invoke if returns true. + /// The type passed into InvokeIf. + /// A function that conditionally invokes another function. + public static Func> InvokeIf( + Predicate predicate, + Func func + ) + { + return value => + { + if (predicate(value)) + { + return Task.FromResult(value).Then(func).Then(_ => value); + } + + return Task.FromResult(value); + }; + } + /// /// A function that executes the after has elapsed. /// diff --git a/tests/unit/InvokeIf/WithAction.cs b/tests/unit/InvokeIf/WithAction.cs new file mode 100644 index 0000000..7a8de83 --- /dev/null +++ b/tests/unit/InvokeIf/WithAction.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.InvokeIf; + +public class WithAction +{ + [Fact] + public async Task ItShouldInvokeIfPredicateSucceeds() + { + int actualValue = 0; + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Action action = _ => { actualValue = 5; }; + + await Task.FromResult(4) + .Then(TaskExtras.InvokeIf(predicate, action)); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotInvokeIfPredicateFails() + { + int actualValue = 0; + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Action action = _ => { actualValue = 5; }; + + await Task.FromResult(3) + .Then(TaskExtras.InvokeIf(predicate, action)); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/InvokeIf/WithFullTaskFunc.cs b/tests/unit/InvokeIf/WithFullTaskFunc.cs new file mode 100644 index 0000000..fd73335 --- /dev/null +++ b/tests/unit/InvokeIf/WithFullTaskFunc.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.InvokeIf; + +public class WithFullTaskFunc +{ + [Fact] + public async Task ItShouldInvokeIfPredicateSucceeds() + { + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Func> func = _ => Task.FromResult(5); + + int actualValue = await Task.FromResult(4) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotInvokeIfPredicateFails() + { + int expectedValue = 3; + Predicate predicate = value => value % 2 == 0; + Func> func = _ => Task.FromResult(5); + + int actualValue = await Task.FromResult(3) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.Equal(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/InvokeIf/WithRawTaskFunc.cs b/tests/unit/InvokeIf/WithRawTaskFunc.cs new file mode 100644 index 0000000..d919f7c --- /dev/null +++ b/tests/unit/InvokeIf/WithRawTaskFunc.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.InvokeIf; + +public class WithRawTaskFunc +{ + [Fact] + public async Task ItShouldInvokeIfPredicateSucceeds() + { + int actualValue = 0; + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Func func = value => + { + actualValue = 5; + return Task.CompletedTask; + }; + + await Task.FromResult(4) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotInvokeIfPredicateFails() + { + int actualValue = 0; + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Func func = value => + { + actualValue = 5; + return Task.CompletedTask; + }; + + await Task.FromResult(3) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/InvokeIf/WithTFunc.cs b/tests/unit/InvokeIf/WithTFunc.cs new file mode 100644 index 0000000..c363714 --- /dev/null +++ b/tests/unit/InvokeIf/WithTFunc.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.InvokeIf; + +public class WithTFunc +{ + [Fact] + public async Task ItShouldInvokeIfPredicateSucceeds() + { + int expectedValue = 5; + Predicate predicate = value => value % 2 == 0; + Func func = _ => 5; + + int actualValue = await Task.FromResult(4) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotInvokeIfPredicateFails() + { + int expectedValue = 3; + Predicate predicate = value => value % 2 == 0; + Func func = _ => 5; + + int actualValue = await Task.FromResult(3) + .Then(TaskExtras.InvokeIf(predicate, func)); + + Assert.Equal(expectedValue, actualValue); + } +} \ No newline at end of file