diff --git a/src/All.Mvc/WrapModelBinder.cs b/src/All.Mvc/WrapModelBinder.cs index bef08a37..f6921d0f 100644 --- a/src/All.Mvc/WrapModelBinder.cs +++ b/src/All.Mvc/WrapModelBinder.cs @@ -82,97 +82,102 @@ string model try { - // Attempt to parse the value - var valueParsed = default(TValue) switch + // Attempt to parse value and wrap result + return Parse(value) switch { - bool => - M.ParseBool(value).Match(FNone, FSome), + TValue x => + (result, ModelBindingResult.Success(Wrap(x))), - byte => - M.ParseByte(value).Match(FNone, FSome), + _ when new MonadModelBinderProvider().GetBinder(typeof(TValue)) is IWrapModelBinder x => + x.GetValue(provider, model), - char => - M.ParseChar(value).Match(FNone, FSome), + _ when JsonSerializer.Deserialize($"\"{value}\"", WrapModelBinderHelpers.Options) is TValue x => + (result, ModelBindingResult.Success(Wrap(x))), - DateOnly => - M.ParseDateOnly(value).Match(FNone, FSome), + _ => + Nothing() + }; + } + catch + { + return Nothing(); + } + } - DateTime => - M.ParseDateTime(value).Match(FNone, FSome), + /// + /// Attempt to parse , returning null on failure. + /// + /// String value. + /// Parsed value or null. + internal object? Parse(string? value) => + default(TValue) switch + { + bool => + M.ParseBool(value).Match(FNone, FSome), - DateTimeOffset => - M.ParseDateTimeOffset(value).Match(FNone, FSome), + byte => + M.ParseByte(value).Match(FNone, FSome), - decimal => - M.ParseDecimal(value).Match(FNone, FSome), + char => + M.ParseChar(value).Match(FNone, FSome), - double => - M.ParseDouble(value).Match(FNone, FSome), + DateOnly => + M.ParseDateOnly(value).Match(FNone, FSome), - Guid => - M.ParseGuid(value).Match(FNone, FSome), + DateTime => + M.ParseDateTime(value).Match(FNone, FSome), - short => - M.ParseInt16(value).Match(FNone, FSome), + DateTimeOffset => + M.ParseDateTimeOffset(value).Match(FNone, FSome), - int => - M.ParseInt32(value).Match(FNone, FSome), + decimal => + M.ParseDecimal(value).Match(FNone, FSome), - long => - M.ParseInt64(value).Match(FNone, FSome), + double => + M.ParseDouble(value).Match(FNone, FSome), - Int128 => - M.ParseInt128(value).Match(FNone, FSome), + Guid => + M.ParseGuid(value).Match(FNone, FSome), - nint => - M.ParseIntPtr(value).Match(FNone, FSome), + short => + M.ParseInt16(value).Match(FNone, FSome), - float => - M.ParseSingle(value).Match(FNone, FSome), + int => + M.ParseInt32(value).Match(FNone, FSome), - TimeOnly => - M.ParseTimeOnly(value).Match(FNone, FSome), + long => + M.ParseInt64(value).Match(FNone, FSome), - ushort => - M.ParseUInt16(value).Match(FNone, FSome), + Int128 => + M.ParseInt128(value).Match(FNone, FSome), - uint => - M.ParseUInt32(value).Match(FNone, FSome), + nint => + M.ParseIntPtr(value).Match(FNone, FSome), - ulong => - M.ParseUInt64(value).Match(FNone, FSome), + float => + M.ParseSingle(value).Match(FNone, FSome), - UInt128 => - M.ParseUInt128(value).Match(FNone, FSome), + TimeOnly => + M.ParseTimeOnly(value).Match(FNone, FSome), - nuint => - M.ParseUIntPtr(value).Match(FNone, FSome), + ushort => + M.ParseUInt16(value).Match(FNone, FSome), - _ => - null - }; + uint => + M.ParseUInt32(value).Match(FNone, FSome), - // Wrap parsed value - return valueParsed switch - { - TValue x => - (result, ModelBindingResult.Success(Wrap(x))), + ulong => + M.ParseUInt64(value).Match(FNone, FSome), - _ when new MonadModelBinderProvider().GetBinder(typeof(TValue)) is IWrapModelBinder x => - x.GetValue(provider, model), + UInt128 => + M.ParseUInt128(value).Match(FNone, FSome), - _ when JsonSerializer.Deserialize($"\"{value}\"", WrapModelBinderHelpers.Options) is TValue x => - (result, ModelBindingResult.Success(Wrap(x))), + nuint => + M.ParseUIntPtr(value).Match(FNone, FSome), - _ => - Nothing() - }; - } - catch - { - return Nothing(); - } - } + _ => + null + }; /// /// Return a 'None' or null value. diff --git a/src/All/Extensions/Enumerable/EnumerableExtensions.ElementAtOrNone.cs b/src/All/Extensions/Enumerable/EnumerableExtensions.ElementAtOrNone.cs index 38355edc..6e8d4ea4 100644 --- a/src/All/Extensions/Enumerable/EnumerableExtensions.ElementAtOrNone.cs +++ b/src/All/Extensions/Enumerable/EnumerableExtensions.ElementAtOrNone.cs @@ -15,13 +15,16 @@ public static partial class EnumerableExtensions /// IEnumerable object. /// Index of value to return. /// Value of the element at position of , or . - public static Maybe ElementAtOrNone(this IEnumerable @this, int index) => - (@this.Count() > index) switch + public static Maybe ElementAtOrNone(this IEnumerable @this, int index) + { + var a = @this.ToArray(); + return (index < a.Length) switch { true => - @this.ElementAt(index), + a[index], false => M.None }; + } } diff --git a/src/All/Extensions/Enumerable/EnumerableExtensions.FirstOrNone.cs b/src/All/Extensions/Enumerable/EnumerableExtensions.FirstOrNone.cs index 044b25af..1a7d2f26 100644 --- a/src/All/Extensions/Enumerable/EnumerableExtensions.FirstOrNone.cs +++ b/src/All/Extensions/Enumerable/EnumerableExtensions.FirstOrNone.cs @@ -14,13 +14,16 @@ public static partial class EnumerableExtensions /// IEnumerable value type. /// IEnumerable object /// Value of the first element of , or . - public static Maybe FirstOrNone(this IEnumerable @this) => - @this.Any() switch + public static Maybe FirstOrNone(this IEnumerable @this) + { + var a = @this.ToArray(); + return (a.Length > 0) switch { true => - @this.First(), + a[0], - _ => + false => M.None }; + } } diff --git a/src/All/Extensions/Enumerable/EnumerableExtensions.LastOrNone.cs b/src/All/Extensions/Enumerable/EnumerableExtensions.LastOrNone.cs index d8cf1dd0..6cf3a793 100644 --- a/src/All/Extensions/Enumerable/EnumerableExtensions.LastOrNone.cs +++ b/src/All/Extensions/Enumerable/EnumerableExtensions.LastOrNone.cs @@ -14,13 +14,16 @@ public static partial class EnumerableExtensions /// IEnumerable value type. /// IEnumerable object /// Value of the last element of , or . - public static Maybe LastOrNone(this IEnumerable @this) => - @this.Any() switch + public static Maybe LastOrNone(this IEnumerable @this) + { + var a = @this.ToArray(); + return (a.Length > 0) switch { true => - @this.Last(), + a[^1], - _ => + false => M.None }; + } } diff --git a/src/All/Extensions/Enumerable/EnumerableExtensions.SingleOrNone.cs b/src/All/Extensions/Enumerable/EnumerableExtensions.SingleOrNone.cs index 05c13514..649cc770 100644 --- a/src/All/Extensions/Enumerable/EnumerableExtensions.SingleOrNone.cs +++ b/src/All/Extensions/Enumerable/EnumerableExtensions.SingleOrNone.cs @@ -1,7 +1,6 @@ // Wrap: Functional Monads for .NET // Copyright (c) bfren - licensed under https://mit.bfren.dev/2019 -using System; using System.Collections.Generic; using System.Linq; @@ -18,13 +17,16 @@ public static partial class EnumerableExtensions /// Value of the single element of , or if the list is empty /// or contains more than one element. /// - public static Maybe SingleOrNone(this IEnumerable @this) => - @this.Count() switch + public static Maybe SingleOrNone(this IEnumerable @this) + { + var a = @this.ToArray(); + return (a.Length == 1) switch { - 1 => - @this.Single(), + true => + a[0], - _ => + false => M.None }; + } } diff --git a/src/All/Extensions/Maybe/MaybeExtensions.Audit.cs b/src/All/Extensions/Maybe/MaybeExtensions.Audit.cs index d15df340..9ce19735 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.Audit.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.Audit.cs @@ -75,8 +75,26 @@ public static Task> AuditAsync(this Maybe @this, Func fS AuditAsync(@this, null, fSome); /// - public static Task> AuditAsync(this Maybe @this, Func? fNone, Func? fSome) => - AuditAsync(@this.AsTask(), fNone, fSome); + public static async Task> AuditAsync(this Maybe @this, Func? fNone, Func? fSome) + { + try + { + if (@this is Maybe.NoneImpl && fNone is not null) + { + await fNone(); + } + else if (@this is Some x && fSome is not null) + { + await fSome(x.Value); + } + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return @this; + } /// public static Task> AuditAsync(this Task> @this, Action fNone) => @@ -87,8 +105,28 @@ public static Task> AuditAsync(this Task> @this, Action AuditAsync(@this, null, fSome); /// - public static Task> AuditAsync(this Task> @this, Action? fNone, Action? fSome) => - AuditAsync(@this, async () => fNone?.Invoke(), async x => fSome?.Invoke(x)); + public static async Task> AuditAsync(this Task> @this, Action? fNone, Action? fSome) + { + var maybe = await @this; + + try + { + if (maybe is Maybe.NoneImpl && fNone is not null) + { + fNone(); + } + else if (maybe is Some x && fSome is not null) + { + fSome(x.Value); + } + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return maybe; + } /// public static Task> AuditAsync(this Task> @this, Func fNone) => @@ -101,15 +139,15 @@ public static Task> AuditAsync(this Task> @this, Func public static async Task> AuditAsync(this Task> @this, Func? fNone, Func? fSome) { - var result = await @this; + var maybe = await @this; try { - if (result is Maybe.NoneImpl && fNone is not null) + if (maybe is Maybe.NoneImpl && fNone is not null) { await fNone(); } - else if (result is Some x && fSome is not null) + else if (maybe is Some x && fSome is not null) { await fSome(x.Value); } @@ -119,31 +157,55 @@ public static async Task> AuditAsync(this Task> @this, Func F.LogException?.Invoke(ex); } - return result; + return maybe; } /// - public static Task> AuditAsync(this Maybe @this, Func, Task> either) => - AuditAsync(@this.AsTask(), either); + public static async Task> AuditAsync(this Maybe @this, Func, Task> either) + { + try + { + await either(@this); + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return @this; + } /// - public static Task> AuditAsync(this Task> @this, Action> either) => - AuditAsync(@this, either: async x => either(x)); + public static async Task> AuditAsync(this Task> @this, Action> either) + { + var maybe = await @this; + + try + { + either(maybe); + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return maybe; + } /// public static async Task> AuditAsync(this Task> @this, Func, Task> either) { - var result = await @this; + var maybe = await @this; try { - await either(result); + await either(maybe); } catch (Exception ex) { F.LogException?.Invoke(ex); } - return result; + return maybe; } } diff --git a/src/All/Extensions/Maybe/MaybeExtensions.BindIf.cs b/src/All/Extensions/Maybe/MaybeExtensions.BindIf.cs index e0c889e4..668e8482 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.BindIf.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.BindIf.cs @@ -22,13 +22,13 @@ public static Maybe BindIf(this Maybe @this, Func public static Task> BindIfAsync(this Maybe @this, Func fTest, Func>> f) => - BindIfAsync(@this.AsTask(), fTest, f); + IfAsync(@this, fTest, f, _ => M.None); /// public static Task> BindIfAsync(this Task> @this, Func fTest, Func> f) => - BindIfAsync(@this, fTest, async x => f(x)); + IfAsync(@this, fTest, f, _ => M.None); /// public static Task> BindIfAsync(this Task> @this, Func fTest, Func>> f) => - IfAsync(@this, fTest, f, async _ => M.None); + IfAsync(@this, fTest, f, _ => M.None); } diff --git a/src/All/Extensions/Maybe/MaybeExtensions.If.cs b/src/All/Extensions/Maybe/MaybeExtensions.If.cs index 7648d93f..bb6caaae 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.If.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.If.cs @@ -32,14 +32,45 @@ public static Maybe If(this Maybe @this, Func f /// public static Task> IfAsync(this Maybe @this, Func fTest, Func>> fTrue, Func>> fFalse) => - IfAsync(@this.AsTask(), fTest, fTrue, fFalse); + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } + ); /// - public static Task> IfAsync(this Task> @this, Func fTest, Func> fTrue, Func> fFalse) => - IfAsync(@this, fTest, async x => fTrue(x), async x => fFalse(x)); + public static Task> IfAsync(this Maybe @this, Func fTest, Func>> fTrue, Func> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } + ); /// - public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func>> fFalse) => + public static Task> IfAsync(this Maybe @this, Func fTest, Func> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } + ); + + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func> fTrue, Func> fFalse) => BindAsync(@this, x => fTest(x) switch { @@ -51,6 +82,45 @@ public static Task> IfAsync(this Task> @this } ); + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } + ); + + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } + ); + + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } + ); + #region Special Case - Act if True /// @@ -66,15 +136,15 @@ public static Maybe If(this Maybe @this, Func fTest, Func public static Task> IfAsync(this Maybe @this, Func fTest, Func>> fThen) => - IfAsync(@this.AsTask(), fTest, fThen, async x => x); + IfAsync(@this, fTest, fThen, x => x); /// public static Task> IfAsync(this Task> @this, Func fTest, Func> fThen) => - IfAsync(@this, fTest, async x => fThen(x), async x => x); + IfAsync(@this, fTest, fThen, x => x); /// public static Task> IfAsync(this Task> @this, Func fTest, Func>> fThen) => - IfAsync(@this, fTest, fThen, async x => x); + IfAsync(@this, fTest, fThen, x => x); #endregion @@ -93,15 +163,15 @@ public static Maybe IfNot(this Maybe @this, Func fTest, Func public static Task> IfNotAsync(this Maybe @this, Func fTest, Func>> fThen) => - IfAsync(@this.AsTask(), fTest, async x => x, fThen); + IfAsync(@this, fTest, x => x, fThen); /// public static Task> IfNotAsync(this Task> @this, Func fTest, Func> fThen) => - IfAsync(@this, fTest, async x => x, async x => fThen(x)); + IfAsync(@this, fTest, x => x, fThen); /// public static Task> IfNotAsync(this Task> @this, Func fTest, Func>> fThen) => - IfAsync(@this, fTest, async x => x, fThen); + IfAsync(@this, fTest, x => x, fThen); #endregion } diff --git a/src/All/Extensions/Maybe/MaybeExtensions.IfNone.cs b/src/All/Extensions/Maybe/MaybeExtensions.IfNone.cs index a4883a24..f4504f85 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.IfNone.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.IfNone.cs @@ -20,11 +20,11 @@ public static Maybe IfNone(this Maybe @this, Action f) => /// public static Task> IfNoneAsync(this Maybe @this, Func f) => - IfNoneAsync(@this.AsTask(), f); + @this.AuditAsync(fNone: f); /// public static Task> IfNoneAsync(this Task> @this, Action f) => - IfNoneAsync(@this, async () => f()); + @this.AuditAsync(fNone: f); /// public static Task> IfNoneAsync(this Task> @this, Func f) => diff --git a/src/All/Extensions/Maybe/MaybeExtensions.IfSome.cs b/src/All/Extensions/Maybe/MaybeExtensions.IfSome.cs index faa3e94a..e152d3f2 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.IfSome.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.IfSome.cs @@ -20,11 +20,11 @@ public static Maybe IfSome(this Maybe @this, Action f) => /// public static Task> IfSomeAsync(this Maybe @this, Func f) => - IfSomeAsync(@this.AsTask(), f); + @this.AuditAsync(fSome: f); /// public static Task> IfSomeAsync(this Task> @this, Action f) => - IfSomeAsync(@this, async x => f(x)); + @this.AuditAsync(fSome: f); /// public static Task> IfSomeAsync(this Task> @this, Func f) => diff --git a/src/All/Extensions/Maybe/MaybeExtensions.MapIf.cs b/src/All/Extensions/Maybe/MaybeExtensions.MapIf.cs index a1fa2a35..0884b23c 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.MapIf.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.MapIf.cs @@ -22,13 +22,13 @@ public static Maybe MapIf(this Maybe @this, Func public static Task> MapIfAsync(this Maybe @this, Func fTest, Func> f) => - MapIfAsync(@this.AsTask(), fTest, f); + IfAsync(@this, fTest, async x => M.Wrap(await f(x)), _ => M.None); /// public static Task> MapIfAsync(this Task> @this, Func fTest, Func f) => - MapIfAsync(@this, fTest, async x => f(x)); + IfAsync(@this, fTest, x => M.Wrap(f(x)), _ => M.None); /// public static Task> MapIfAsync(this Task> @this, Func fTest, Func> f) => - IfAsync(@this, fTest, async x => M.Wrap(await f(x)), async _ => M.None); + IfAsync(@this, fTest, async x => M.Wrap(await f(x)), _ => M.None); } diff --git a/src/All/Extensions/Maybe/MaybeExtensions.MatchIf.cs b/src/All/Extensions/Maybe/MaybeExtensions.MatchIf.cs index 5fa7378d..b7b20984 100644 --- a/src/All/Extensions/Maybe/MaybeExtensions.MatchIf.cs +++ b/src/All/Extensions/Maybe/MaybeExtensions.MatchIf.cs @@ -44,11 +44,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -58,11 +63,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -72,11 +82,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -86,11 +101,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -100,11 +120,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -114,11 +139,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -128,11 +158,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fNone: async () => fNone(), - fTest: fTest, - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -142,11 +177,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -156,11 +196,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -170,11 +215,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -184,11 +234,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -198,11 +253,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -212,11 +272,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -226,11 +291,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fNone: async () => fNone(), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fNone: fNone, + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -240,11 +310,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fSome: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -254,11 +329,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -268,11 +348,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + fSome: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -282,11 +367,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -296,11 +386,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -310,11 +405,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -324,11 +424,16 @@ public static Task MatchIfAsync(this Maybe @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -338,11 +443,16 @@ public static Task MatchIfAsync(this Maybe @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: fFalse, - fTrue: fTrue + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -352,11 +462,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fSome: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -366,11 +481,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -380,11 +500,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + fSome: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -394,11 +519,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + fSome: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -408,11 +538,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -422,11 +557,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + fSome: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -436,11 +576,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fNone: fNone, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + fSome: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// diff --git a/src/All/Extensions/Result/ResultExtensions.Audit.cs b/src/All/Extensions/Result/ResultExtensions.Audit.cs index d74fcd83..c933a954 100644 --- a/src/All/Extensions/Result/ResultExtensions.Audit.cs +++ b/src/All/Extensions/Result/ResultExtensions.Audit.cs @@ -75,8 +75,26 @@ public static Task> AuditAsync(this Result @this, Func AuditAsync(@this, null, fOk); /// - public static Task> AuditAsync(this Result @this, Func? fFail, Func? fOk) => - AuditAsync(@this.AsTask(), fFail: fFail, fOk: fOk); + public static async Task> AuditAsync(this Result @this, Func? fFail, Func? fOk) + { + try + { + if (@this is Result.FailureImpl y && fFail is not null) + { + await fFail(y.Value); + } + else if (@this is Ok x && fOk is not null) + { + await fOk(x.Value); + } + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return @this; + } /// public static Task> AuditAsync(this Task> @this, Action fFail) => @@ -87,8 +105,28 @@ public static Task> AuditAsync(this Task> @this, Action - public static Task> AuditAsync(this Task> @this, Action? fFail, Action? fOk) => - AuditAsync(@this, fFail: async x => fFail?.Invoke(x), fOk: async x => fOk?.Invoke(x)); + public static async Task> AuditAsync(this Task> @this, Action? fFail, Action? fOk) + { + var result = await @this; + + try + { + if (result is Result.FailureImpl y && fFail is not null) + { + fFail(y.Value); + } + else if (result is Ok x && fOk is not null) + { + fOk(x.Value); + } + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return result; + } /// public static Task> AuditAsync(this Task> @this, Func fFail) => @@ -123,12 +161,36 @@ public static async Task> AuditAsync(this Task> @this, Fu } /// - public static Task> AuditAsync(this Result @this, Func, Task> either) => - AuditAsync(@this.AsTask(), either: either); + public static async Task> AuditAsync(this Result @this, Func, Task> either) + { + try + { + await either(@this); + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return @this; + } /// - public static Task> AuditAsync(this Task> @this, Action> either) => - AuditAsync(@this, either: async x => either(x)); + public static async Task> AuditAsync(this Task> @this, Action> either) + { + var result = await @this; + + try + { + either(result); + } + catch (Exception ex) + { + F.LogException?.Invoke(ex); + } + + return result; + } /// public static async Task> AuditAsync(this Task> @this, Func, Task> either) diff --git a/src/All/Extensions/Result/ResultExtensions.BindIf.cs b/src/All/Extensions/Result/ResultExtensions.BindIf.cs index 60102a13..8a843889 100644 --- a/src/All/Extensions/Result/ResultExtensions.BindIf.cs +++ b/src/All/Extensions/Result/ResultExtensions.BindIf.cs @@ -22,13 +22,13 @@ public static Result BindIf(this Result @this, Func public static Task> BindIfAsync(this Result @this, Func fTest, Func>> f) => - BindIfAsync(@this.AsTask(), fTest, f); + IfAsync(@this, fTest, f, _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(BindIfAsync))); /// public static Task> BindIfAsync(this Task> @this, Func fTest, Func> f) => - BindIfAsync(@this, fTest, async x => f(x)); + IfAsync(@this, fTest, f, _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(BindIfAsync))); /// public static Task> BindIfAsync(this Task> @this, Func fTest, Func>> f) => - IfAsync(@this, fTest, f, async _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(BindIfAsync))); + IfAsync(@this, fTest, f, _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(BindIfAsync))); } diff --git a/src/All/Extensions/Result/ResultExtensions.GetSingle.cs b/src/All/Extensions/Result/ResultExtensions.GetSingle.cs index 9bb52e37..c08c1c35 100644 --- a/src/All/Extensions/Result/ResultExtensions.GetSingle.cs +++ b/src/All/Extensions/Result/ResultExtensions.GetSingle.cs @@ -35,16 +35,19 @@ public static Result GetSingle(this Result @this, R.Erro where T : IEnumerable => Bind(@this, x => x switch { - IEnumerable list when list.Count() == 1 => - R.Wrap(list.Single()), + IEnumerable y => y.ToArray() switch + { + { Length: 1 } z => + R.Wrap(z[0]), - IEnumerable list when !list.Any() => - onError?.Invoke(C.GetSingle.EmptyList) ?? R.Fail(C.GetSingle.EmptyList) - .Ctx(nameof(ResultExtensions), nameof(GetSingle)), + { Length: 0 } => + onError?.Invoke(C.GetSingle.EmptyList) ?? R.Fail(C.GetSingle.EmptyList) + .Ctx(nameof(ResultExtensions), nameof(GetSingle)), - IEnumerable => - onError?.Invoke(C.GetSingle.MultipleValues) ?? R.Fail(C.GetSingle.MultipleValues) - .Ctx(nameof(ResultExtensions), nameof(GetSingle)), + _ => + onError?.Invoke(C.GetSingle.MultipleValues) ?? R.Fail(C.GetSingle.MultipleValues) + .Ctx(nameof(ResultExtensions), nameof(GetSingle)) + }, IEnumerable => onError?.Invoke(C.GetSingle.IncorrectType, typeof(TSingle).Name, typeof(T).Name) ?? R.Fail(C.GetSingle.IncorrectType, typeof(TSingle).Name, typeof(T).Name) diff --git a/src/All/Extensions/Result/ResultExtensions.If.cs b/src/All/Extensions/Result/ResultExtensions.If.cs index c283d969..b99de9f4 100644 --- a/src/All/Extensions/Result/ResultExtensions.If.cs +++ b/src/All/Extensions/Result/ResultExtensions.If.cs @@ -30,27 +30,97 @@ public static Result If(this Result @this, Func } ); + /// + public static Task> IfAsync(this Result @this, Func fTest, Func>> fTrue, Func> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } + ); + + /// + public static Task> IfAsync(this Result @this, Func fTest, Func> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } + ); + /// public static Task> IfAsync(this Result @this, Func fTest, Func>> fTrue, Func>> fFalse) => - IfAsync(@this.AsTask(), fTest, fTrue, fFalse); + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } + ); /// public static Task> IfAsync(this Task> @this, Func fTest, Func> fTrue, Func> fFalse) => - IfAsync(@this, fTest, async x => fTrue(x), async x => fFalse(x)); + BindAsync(@this, + async x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } + ); /// - public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func>> fFalse) => + public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func> fFalse) => BindAsync(@this, - x => fTest(x) switch + async x => fTest(x) switch { false => fFalse(x), + true => + await fTrue(x) + } + ); + + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + true => fTrue(x) } ); + /// + public static Task> IfAsync(this Task> @this, Func fTest, Func>> fTrue, Func>> fFalse) => + BindAsync(@this, + async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } + ); + #region Special Case - Act if True /// @@ -66,15 +136,15 @@ public static Result If(this Result @this, Func fTest, Func public static Task> IfAsync(this Result @this, Func fTest, Func>> fThen) => - IfAsync(@this.AsTask(), fTest, fThen, async x => x); + IfAsync(@this, fTest, fThen, x => x); /// public static Task> IfAsync(this Task> @this, Func fTest, Func> fThen) => - IfAsync(@this, fTest, async x => fThen(x), async x => x); + IfAsync(@this, fTest, fThen, x => x); /// public static Task> IfAsync(this Task> @this, Func fTest, Func>> fThen) => - IfAsync(@this, fTest, fThen, async x => x); + IfAsync(@this, fTest, fThen, x => x); #endregion @@ -93,15 +163,15 @@ public static Result IfNot(this Result @this, Func fTest, Func /// public static Task> IfNotAsync(this Result @this, Func fTest, Func>> fThen) => - IfAsync(@this.AsTask(), fTest, async x => x, fThen); + IfAsync(@this, fTest, x => x, fThen); /// public static Task> IfNotAsync(this Task> @this, Func fTest, Func> fThen) => - IfAsync(@this, fTest, async x => x, async x => fThen(x)); + IfAsync(@this, fTest, x => x, fThen); /// public static Task> IfNotAsync(this Task> @this, Func fTest, Func>> fThen) => - IfAsync(@this, fTest, async x => x, fThen); + IfAsync(@this, fTest, x => x, fThen); #endregion } diff --git a/src/All/Extensions/Result/ResultExtensions.MapIf.cs b/src/All/Extensions/Result/ResultExtensions.MapIf.cs index 16dfd8b2..fb896bbd 100644 --- a/src/All/Extensions/Result/ResultExtensions.MapIf.cs +++ b/src/All/Extensions/Result/ResultExtensions.MapIf.cs @@ -22,13 +22,13 @@ public static Result MapIf(this Result @this, Func public static Task> MapIfAsync(this Result @this, Func fTest, Func> f) => - MapIfAsync(@this.AsTask(), fTest, f); + IfAsync(@this, fTest, async x => R.Wrap(await f(x)), _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(MapIfAsync))); /// public static Task> MapIfAsync(this Task> @this, Func fTest, Func f) => - MapIfAsync(@this, fTest, async x => f(x)); + IfAsync(@this, fTest, x => R.Wrap(f(x)), _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(MapIfAsync))); /// public static Task> MapIfAsync(this Task> @this, Func fTest, Func> f) => - IfAsync(@this, fTest, x => R.TryAsync(async () => await f(x)), async _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(MapIfAsync))); + IfAsync(@this, fTest, async x => R.Wrap(await f(x)), _ => R.Fail(C.TestFalseMessage).Ctx(nameof(ResultExtensions), nameof(MapIfAsync))); } diff --git a/src/All/Extensions/Result/ResultExtensions.MatchIf.cs b/src/All/Extensions/Result/ResultExtensions.MatchIf.cs index 436181c1..cfd39a93 100644 --- a/src/All/Extensions/Result/ResultExtensions.MatchIf.cs +++ b/src/All/Extensions/Result/ResultExtensions.MatchIf.cs @@ -44,11 +44,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -58,11 +63,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -72,11 +82,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -86,11 +101,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -100,11 +120,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -114,11 +139,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -128,11 +158,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), - fFail: async f => fFail(f), - fTest: fTest, - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -142,11 +177,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -156,11 +196,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -170,11 +215,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -184,11 +234,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -198,11 +253,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -212,11 +272,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -226,11 +291,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, - fFail: async f => fFail(f), - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + MatchAsync(@this, + fFail: fFail, + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -240,11 +310,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fOk: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -254,11 +329,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -268,11 +348,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + fOk: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -282,11 +367,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -296,11 +386,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -310,11 +405,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -324,11 +424,16 @@ public static Task MatchIfAsync(this Result @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -338,11 +443,16 @@ public static Task MatchIfAsync(this Result @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(Task.FromResult(@this), + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: fFalse, - fTrue: fTrue + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -352,11 +462,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fOk: x => fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -366,11 +481,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: async x => fTrue(x) + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -380,11 +500,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: async x => fFalse(x), - fTrue: fTrue + fOk: async x => fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -394,11 +519,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: async x => fTest(x), - fFalse: fFalse, - fTrue: fTrue + fOk: async x => fTest(x) switch + { + false => + await fFalse(x), + + true => + await fTrue(x) + } ); /// @@ -408,11 +538,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: async x => fTrue(x) + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + fTrue(x) + } ); /// @@ -422,11 +557,16 @@ public static Task MatchIfAsync(this Task> @this, Func> fFalse, Func fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: fFalse, - fTrue: async x => fTrue(x) + fOk: async x => await fTest(x) switch + { + false => + await fFalse(x), + + true => + fTrue(x) + } ); /// @@ -436,11 +576,16 @@ public static Task MatchIfAsync(this Task> @this, Func fFalse, Func> fTrue ) => - MatchIfAsync(@this, + MatchAsync(@this, fFail: fFail, - fTest: fTest, - fFalse: async x => fFalse(x), - fTrue: fTrue + fOk: async x => await fTest(x) switch + { + false => + fFalse(x), + + true => + await fTrue(x) + } ); /// diff --git a/src/Caching/WrapCache.cs b/src/Caching/WrapCache.cs index 79dea3e5..6148bc9e 100644 --- a/src/Caching/WrapCache.cs +++ b/src/Caching/WrapCache.cs @@ -108,8 +108,47 @@ public Maybe GetOrCreate(TKey key, Func valueFactory, Me GetOrCreate(key, () => M.Wrap(valueFactory()), opt); /// - public Maybe GetOrCreate(TKey key, Func> valueFactory) => - GetOrCreate(key, valueFactory, new()); + public Maybe GetOrCreate(TKey key, Func> valueFactory) + { + // Check whether or not the value already exists + var value = GetValue(key); + if (value.IsSome) + { + return value; + } + + // If there was an error, return None + if (LastFailure.IsSome) + { + return M.None; + } + + // Lock all threads + CacheLock.Wait(); + try + { + return valueFactory() + .IfNone( + () => LastFailure = R.Fail("Value factory returned null or None.") + .Ctx(nameof(WrapCache), nameof(GetOrCreate)) + ) + .Map( + x => Cache.GetOrCreate(key, e => { _ = e.SetValue(x!); return x; })! + ); + } + catch (Exception ex) + { + // Return none on failure + LastFailure = R.Fail(ex).Msg("Error creating cache value.") + .Ctx(nameof(WrapCache), nameof(GetOrCreate)); + return M.None; + } + finally + { + // Release other threads + _ = CacheLock.Release(); + } + } /// public Maybe GetOrCreate(TKey key, Func> valueFactory, MemoryCacheEntryOptions opt) => diff --git a/src/Common/Functions/F.Format.cs b/src/Common/Functions/F.Format.cs index a0302195..28c63eb0 100644 --- a/src/Common/Functions/F.Format.cs +++ b/src/Common/Functions/F.Format.cs @@ -30,58 +30,28 @@ public static string Format(string formatString, T source) => /// Formatted string. public static string Format(string formatString, T source, string? replaceIfNullOrEmpty) { - // Return if format string is null or empty - if (string.IsNullOrWhiteSpace(formatString)) + // Check arguments before proceeding + if (Check(formatString, source, replaceIfNullOrEmpty) is string earlyReturn) { - return replaceIfNullOrEmpty ?? string.Empty; - } - - // Return if source is null or an empty array - if (source is null) - { - return replaceIfNullOrEmpty ?? formatString; - } - else if (source is Array arr) - { - if (arr.Length == 0) - { - return replaceIfNullOrEmpty ?? formatString; - } - - // Attempt to use string.Format - try - { - return string.Format(DefaultCulture, formatString, [.. arr]); - } - catch (Exception) - { - // do nothing - } + return earlyReturn; } - // Thanks James Newton-King! + // Initialise variables before regex replace loop var regex = TemplateMatcherRegex(); - - var values = new List(); + var values = new List(regex.Count(formatString)); var replaceIndex = 0; // keeps track of replace loop so we can match named template values with an array source var numberedTemplates = true; var flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance; var rewrittenFormat = regex.Replace(formatString, (m) => { - var startGroup = m.Groups["start"]; - var templateGroup = m.Groups["template"]; - var formatGroup = m.Groups["format"]; - var endGroup = m.Groups["end"]; - // This is the value inside the braces, e.g. "0" in "{0}" or "A" in "{A}" - // Remove any @ symbols from the start - used by Serilog to denote an object format - // but breaks the following - var template = templateGroup.Value.TrimStart('@'); + // Remove any @ symbols from the start - used by Serilog to denote an object format but breaks the following + var template = m.Groups["template"].Value.TrimStart('@'); var templateIsNumber = int.TryParse(template, out var templateNumber); numberedTemplates = numberedTemplates && templateIsNumber; // Switch on the source type, using variety of methods to get this template's value - var value = source switch + values.Add(source switch { // Source array - get specific item in array for numbered template Array arr when numberedTemplates && templateNumber < arr.Length && arr.GetValue(templateNumber) is object val => @@ -102,21 +72,64 @@ public static string Format(string formatString, T source, string? replaceIfN // Nothing matches so put placeholder back _ => $"{{{template}}}" - }; - - values.Add(value); + }); // Recreate format using zero-based string - return new string('{', startGroup.Captures.Count) + return new string('{', m.Groups["start"].Captures.Count) + (values.Count - 1) - + formatGroup.Value - + new string('}', endGroup.Captures.Count); + + m.Groups["format"].Value + + new string('}', m.Groups["end"].Captures.Count); }); // Format string with ordered values return string.Format(DefaultCulture, rewrittenFormat, [.. values]); } + /// + /// Check inputs before attempting to apply complex string format. + /// + /// Source type. + /// String to format. + /// Source object to use for template values. + /// Returned if or are null / empty. + /// Early return value if checks fail, or string.Format succeeds (much faster!). + internal static string? Check(string formatString, T source, string? replaceIfNullOrEmpty) + { + // Return if format string is null or empty + if (string.IsNullOrWhiteSpace(formatString)) + { + return replaceIfNullOrEmpty ?? string.Empty; + } + + // Return if source is null or an empty array + if (source is null) + { + return replaceIfNullOrEmpty ?? formatString; + } + else if (source is Array arr) + { + if (arr.Length == 0) + { + return replaceIfNullOrEmpty ?? formatString; + } + + // Attempt to use string.Format + try + { + return string.Format(DefaultCulture, formatString, [.. arr]); + } + catch (Exception) + { + // Do nothing - null return will cause Format function to continue + } + } + + return null; + } + + /// + /// Thanks James Newton-King! + /// [GeneratedRegex("(?\\{)+(?