diff --git a/src/DotNext/Optional.cs b/src/DotNext/Optional.cs index c6dc79320..35d4b731b 100644 --- a/src/DotNext/Optional.cs +++ b/src/DotNext/Optional.cs @@ -48,6 +48,69 @@ public static class Optional public static async Task> Convert(this Task> task, Converter converter) => (await task.ConfigureAwait(false)).Convert(converter); + /// + /// If a value is present, apply the provided mapping function to it, and if the result is + /// non-null, return an Optional describing the result. Otherwise returns . + /// + /// The type of stored in the Optional container. + /// The type of the result of the mapping function. + /// The task containing Optional value. + /// A mapping function to be applied to the value, if present. + /// An Optional describing the result of applying a mapping function to the value of this Optional, if a value is present, otherwise . + public static async Task> Convert(this Task> task, Converter> converter) + => (await task.ConfigureAwait(false)).Convert(converter); + + /// + /// If a value is present, apply the provided mapping function to it, and if the result is + /// non-null, return an Optional describing the result. Otherwise returns . + /// + /// The type of stored in the Optional container. + /// The type of the result of the mapping function. + /// The task containing Optional value. + /// A mapping function to be applied to the value, if present. + /// An Optional describing the result of applying a mapping function to the value of this Optional, if a value is present, otherwise . + public static async Task> Convert(this Task> task, Converter>> converter) + => await (await task.ConfigureAwait(false)).Convert(converter).ConfigureAwait(false); + + /// + /// Creates from instance. + /// + /// The optional value. + /// The converted optional value. + public static Result ToResult(this in Optional optional) + => Result.FromOptional(optional); + + /// + /// Creates from instance. + /// + /// The task containing Optional value. + /// The converted optional value. + public static async Task> ToResult(this Task> task) + => Result.FromOptional(await task.ConfigureAwait(false)); + + /// + /// Creates from instance. + /// + /// The optional value. + /// The error code to apply if the value is not present. + /// The converted optional value. + public static Result ToResult(this in Optional optional, TError error) + where TError : struct, Enum + => optional.HasValue ? new(optional.Value) : new(error); + + /// + /// Creates from instance. + /// + /// The task containing Optional value. + /// The error code to apply if the value is not present. + /// The converted optional value. + public static async Task> ToResult(this Task> task, TError error) + where TError : struct, Enum + { + var optional = await task.ConfigureAwait(false); + return optional.HasValue ? new(optional.Value) : new(error); + } + /// /// If a value is present, returns the value, otherwise throw exception. /// @@ -553,6 +616,11 @@ private Optional ConvertOptional(TConverter conver where TConverter : struct, ISupplier> => HasValue ? converter.Invoke(value) : Optional.None; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Task> ConvertOptionalTask(TConverter converter) + where TConverter : struct, ISupplier>> + => HasValue ? converter.Invoke(value) : Task.FromResult(Optional.None); + /// /// If a value is present, apply the provided mapping function to it, and if the result is /// non-null, return an Optional describing the result. Otherwise, returns . @@ -570,10 +638,31 @@ public Optional Convert(Converter> mapper /// The type of the result of the mapping function. /// A mapping function to be applied to the value, if present. /// An Optional describing the result of applying a mapping function to the value of this Optional, if a value is present, otherwise . + public Task> Convert(Converter>> mapper) + => ConvertOptionalTask>>>(mapper); + + /// + /// If a value is present, apply the provided mapping function to it, and if the result is + /// non-null, return an Optional describing the result. Otherwise returns . + /// + /// The type of the result of the mapping function. + /// A mapping function to be applied to the value, if present. + /// An Optional describing the result of applying a mapping function to the value of this Optional, if a value is present, otherwise . [CLSCompliant(false)] public unsafe Optional Convert(delegate*> mapper) => ConvertOptional>>(mapper); + /// + /// If a value is present, apply the provided mapping function to it, and if the result is + /// non-null, return an Optional describing the result. Otherwise returns . + /// + /// The type of the result of the mapping function. + /// A mapping function to be applied to the value, if present. + /// An Optional describing the result of applying a mapping function to the value of this Optional, if a value is present, otherwise . + [CLSCompliant(false)] + public unsafe Task> Convert(delegate*>> mapper) + => ConvertOptionalTask>>>(mapper); + /// Func IFunctional>.ToDelegate() => Func.Constant(kind is NotEmptyValue ? value : null); diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index c9b2839eb..345d91284 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -7,6 +7,8 @@ namespace DotNext; +using System; +using DotNext.Threading.Tasks; using Runtime.CompilerServices; using Intrinsics = Runtime.Intrinsics; @@ -75,6 +77,87 @@ public static class Result /// The exception to be placed to the container. /// The exception encapsulated by . public static Result FromException(Exception e) => new(e); + + /// + /// Creates a new instance of from the specified exception. + /// + /// The type of the value. + /// The type of the error code. Default value must represent the successful result. + /// The error to be placed to the container. + /// The exception encapsulated by . + public static Result FromError(TError e) + where TError: struct, Enum + => new(e); + + /// + /// If successful result is present, apply the provided mapping function hiding any exception + /// caused by the converter. + /// + /// The task containing Result value. + /// A mapping function to be applied to the value, if present. + /// The type of the value. + /// The type of the result of the mapping function. + /// The conversion result. + public static AwaitableResult Convert(this AwaitableResult task, Converter converter) + { + async Task ConvertInternal() + { + var result = await task.ConfigureAwait(false); + var conversionResult = result.Convert(converter); + return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error; + } + + return ConvertInternal().SuspendException(); + } + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// The task containing Result value. + /// A mapping function to be applied to the value, if present. + /// The type of the value. + /// The type of the result of the mapping function. + /// The conversion result. + public static AwaitableResult Convert(this AwaitableResult task, Converter> converter) + { + async Task ConvertInternal() + { + var result = await task.ConfigureAwait(false); + var conversionResult = result.Convert(converter); + return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error; + } + + return ConvertInternal().SuspendException(); + } + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// The task containing Result value. + /// A mapping function to be applied to the value, if present. + /// The type of the value. + /// The type of the result of the mapping function. + /// The conversion result. + public static AwaitableResult Convert(this AwaitableResult task, Converter> converter) + { + async Task ConvertInternal() + { + var result = await task.ConfigureAwait(false); + var conversionResult = await result.Convert(converter); + return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error; + } + + return ConvertInternal().SuspendException(); + } + + /// + /// Converts the awaitable Result into a task holding . + /// + /// A task holding an Option monad representing value in this monad. + public static async Task> TryGet(this AwaitableResult awaitableResult) + => (await awaitableResult.ConfigureAwait(false)).TryGet(); } /// @@ -201,6 +284,78 @@ private Result Convert(TConverter converter) return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Result ConvertResult(TConverter converter) + where TConverter : struct, ISupplier> + { + Result result; + if (exception is null) + { + try + { + result = converter.Invoke(value); + } + catch (Exception e) + { + result = new(e); + } + } + else + { + result = new(exception); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private AwaitableResult ConvertTask(TConverter converter) + where TConverter : struct, ISupplier> + { + AwaitableResult result; + if (exception is null) + { + try + { + result = converter.Invoke(value).SuspendException(); + } + catch (Exception e) + { + result = new(Task.FromException(e)); + } + } + else + { + result = new(Task.FromException(exception.SourceException)); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private AwaitableResult ConvertAwaitableResult(TConverter converter) + where TConverter : struct, ISupplier> + { + AwaitableResult result; + if (exception is null) + { + var valueCopy = value; + async Task GetConversionResult() + { + var conversionResult = await converter.Invoke(valueCopy).ConfigureAwait(false); + return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error; + } + + result = new(GetConversionResult()); + } + else + { + result = new(Task.FromException(exception.SourceException)); + } + + return result; + } + /// /// If successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -211,6 +366,36 @@ private Result Convert(TConverter converter) public Result Convert(Converter converter) => Convert>(converter); + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + public Result Convert(Converter> converter) + => ConvertResult>>(converter); + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + public AwaitableResult Convert(Converter> converter) + => ConvertTask>>(converter); + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + public AwaitableResult Convert(Converter> converter) + => ConvertAwaitableResult>>(converter); + /// /// If successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -222,6 +407,39 @@ public Result Convert(Converter converter) public unsafe Result Convert(delegate* converter) => Convert>(converter); + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + [CLSCompliant(false)] + public unsafe Result Convert(delegate*> converter) + => ConvertResult>>(converter); + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + [CLSCompliant(false)] + public unsafe AwaitableResult Convert(delegate*> converter) + => ConvertTask>>(converter); + + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the exception. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + [CLSCompliant(false)] + public unsafe AwaitableResult Convert(delegate*> converter) + => ConvertAwaitableResult>>(converter); + /// /// Attempts to extract value from container if it is present. /// @@ -322,6 +540,13 @@ public ValueTask AsTask() { } error => ValueTask.FromException(error), }; + /// + /// Converts this result to . + /// + /// The awaitable Result representing the result. + public AwaitableResult ToAwaitable() + => IsSuccessful ? new(Task.FromResult(value)) : new(Task.FromException(Error)); + /// /// Converts the result to . /// @@ -537,6 +762,11 @@ private Result Convert(TConverter converte where TConverter : struct, ISupplier => IsSuccessful ? new(converter.Invoke(value)) : new(Error); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Result ConvertResult(TConverter converter) + where TConverter : struct, ISupplier> + => IsSuccessful ? converter.Invoke(value) : new(Error); + /// /// If successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -547,6 +777,16 @@ private Result Convert(TConverter converte public Result Convert(Converter converter) => Convert>(converter); + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the error. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + public Result Convert(Converter> converter) + => ConvertResult>>(converter); + /// /// If successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -558,6 +798,17 @@ public Result Convert(Converter converter) public unsafe Result Convert(delegate* converter) => Convert>(converter); + /// + /// If successful result is present, apply the provided mapping function. If not, + /// forward the error. + /// + /// A mapping function to be applied to the value, if present. + /// The type of the result of the mapping function. + /// The conversion result. + [CLSCompliant(false)] + public unsafe Result Convert(delegate*> converter) + => ConvertResult>>(converter); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private T OrInvoke(TSupplier defaultFunc) where TSupplier : struct, ISupplier diff --git a/src/DotNext/Threading/Tasks/Conversion.cs b/src/DotNext/Threading/Tasks/Conversion.cs index ac0535739..5d235be85 100644 --- a/src/DotNext/Threading/Tasks/Conversion.cs +++ b/src/DotNext/Threading/Tasks/Conversion.cs @@ -3,6 +3,7 @@ namespace DotNext.Threading.Tasks; +using System.Threading.Tasks; using Runtime.CompilerServices; /// @@ -20,6 +21,16 @@ public static class Conversion public static Task Convert(this Task task) where TInput : TOutput => task.Convert(Converter.Identity()); + /// + /// Converts one type of into another. + /// + /// The source Result type. + /// The target Result type. + /// The awaitable Result to convert. + /// The converted task. + public static AwaitableResult Convert(this AwaitableResult awaitableResult) + where TInput : TOutput => awaitableResult.Convert(Converter.Identity()); + /// /// Converts one type of task into another. /// @@ -31,6 +42,25 @@ public static Task Convert(this Task task) public static async Task Convert(this Task task, Converter converter) => converter(await task.ConfigureAwait(false)); + /// + /// Converts one type of into another. + /// + /// The source Result type. + /// The target Result type. + /// The awaitable Result to convert. + /// Non-blocking conversion function. + /// The converted task. + public static AwaitableResult Convert(this AwaitableResult awaitableResult, Converter converter) + { + async Task ConvertInternal() + { + var result = await awaitableResult.ConfigureAwait(false); + return result.IsSuccessful ? converter(result.Value) : throw result.Error; + } + + return ConvertInternal().SuspendException(); + } + /// /// Converts value type into nullable value type. /// @@ -53,6 +83,26 @@ public static async Task Convert(this Task tas public static async Task Convert(this Task task, Converter> converter) => await converter(await task.ConfigureAwait(false)).ConfigureAwait(false); + /// + /// Converts one type of into another. + /// + /// The source Result type. + /// The target Result type. + /// The awaitable Result to convert. + /// Asynchronous conversion function. + /// The converted task. + public static AwaitableResult Convert(this AwaitableResult awaitableResult, Converter> converter) + { + async Task ConvertInternal() + { + var result = await awaitableResult.ConfigureAwait(false); + var conversionResult = result.IsSuccessful ? await converter(result.Value).ConfigureAwait(false) : throw result.Error; + return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error; + } + + return ConvertInternal().SuspendException(); + } + /// /// Allows to convert of unknown result type into dynamically /// typed task which result can be obtained as