diff --git a/Source/FunicularSwitch/Option.cs b/Source/FunicularSwitch/Option.cs index ecd2988..7f275b4 100644 --- a/Source/FunicularSwitch/Option.cs +++ b/Source/FunicularSwitch/Option.cs @@ -48,11 +48,13 @@ public interface IOption public Option Bind(Func> map) => Match(map, Option.None); public Task> Bind(Func>> bind) => Match(bind, () => Option.None); + + public Option OrElse(Option other) => OrElse(() => other); + public Option OrElse(Func> other) => Match(Option.Some, other); + public Task> OrElse(Task> other) => OrElse(() => other); + public Task> OrElse(Func>> other) => Match(Option.Some, other); - public void Match(Action some, Action? none = null) - { - Match(some.ToFunc(), none?.ToFunc() ?? (() => 42)); - } + public void Match(Action some, Action? none = null) => Match(some.ToFunc(), none?.ToFunc() ?? (() => 42)); public async Task Match(Func some, Func? none = null) { @@ -79,6 +81,16 @@ public async Task Match(Func> some, Func Match(Func some, Func> none) + { + if (_isSome) + { + return some(_value); + } + + return await none().ConfigureAwait(false); + } public async Task Match(Func> some, Func none) { @@ -104,15 +116,15 @@ public async Task Match(Func> some, TResult n IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerator GetEnumerator() => Match(v => new[] { v }, Enumerable.Empty).GetEnumerator(); + public IEnumerator GetEnumerator() => Match(v => [v], Enumerable.Empty).GetEnumerator(); - public T? GetValueOrDefault() => Match(v => (T?)v, () => default); + public T? GetValueOrDefault() => Match(v => v, default(T?)); public T GetValueOrDefault(Func defaultValue) => Match(v => v, defaultValue); public T GetValueOrDefault(T defaultValue) => Match(v => v, () => defaultValue); - public T GetValueOrThrow(string? errorMessage = null) => Match(v => v, () => throw new InvalidOperationException(errorMessage ?? "Cannot access value of none option")); + public T GetValueOrThrow(string? errorMessage = null) => Match(v => v, T () => throw new InvalidOperationException(errorMessage ?? "Cannot access value of none option")); public Option Convert() => Match(s => Option.Some((TOther)(object)s!), Option.None); @@ -181,6 +193,30 @@ public static async Task> Bind(this Task> bind, var result = await bind.ConfigureAwait(false); return await result.Bind(convert).ConfigureAwait(false); } + + public static async Task> OrElse(this Task> option, Option other) + { + var result = await option.ConfigureAwait(false); + return result.OrElse(other); + } + + public static async Task> OrElse(this Task> option, Func> other) + { + var result = await option.ConfigureAwait(false); + return result.OrElse(other); + } + + public static async Task> OrElse(this Task> option, Task> other) + { + var result = await option.ConfigureAwait(false); + return await result.OrElse(other).ConfigureAwait(false); + } + + public static async Task> OrElse(this Task> option, Func>> other) + { + var result = await option.ConfigureAwait(false); + return await result.OrElse(other).ConfigureAwait(false); + } public static IEnumerable Choose(this IEnumerable items, Func> choose) => items.SelectMany(i => choose(i)); @@ -188,7 +224,7 @@ public static async Task> Bind(this Task> bind, public static Option ToOption(this Result result, Action? logError) => result.Match( - ok => Option.Some(ok), + Option.Some, error => { logError?.Invoke(error); @@ -196,7 +232,7 @@ public static Option ToOption(this Result result, Action? logEr }); public static Result ToResult(this Option option, Func errorIfNone) => - option.Match(s => Result.Ok(s), () => Result.Error(errorIfNone())); + option.Match(Result.Ok, () => Result.Error(errorIfNone())); #region query-expression pattern diff --git a/Source/Tests/FunicularSwitch.Test/OptionSpecs.cs b/Source/Tests/FunicularSwitch.Test/OptionSpecs.cs index cb5b667..44d4613 100644 --- a/Source/Tests/FunicularSwitch.Test/OptionSpecs.cs +++ b/Source/Tests/FunicularSwitch.Test/OptionSpecs.cs @@ -49,6 +49,23 @@ public void NullCoalescingWithResultBoolBehavesAsExpected() result.Should().BeError().Subject.Should().Be("Value is missing"); } + [TestMethod] + public async Task OrElse() + { + var none = None(); + none.OrElse(Option.None).Should().BeNone(); + ShouldBeSome42(none.OrElse(42)); + + var some = Some(42); + ShouldBeSome42(some.OrElse(Option.None)); + ShouldBeSome42(some.OrElse(2)); + + ShouldBeSome42(await none.OrElse(() => Task.FromResult(Some(42)))); + return; + + void ShouldBeSome42(Option option) => option.Should().BeSome().Subject.Should().Be(42); + } + [TestMethod] public void QueryExpressionSelect() {