diff --git a/src/DotNext.Tests/OptionalTests.cs b/src/DotNext.Tests/OptionalTests.cs index 2877a30d9..ea656adba 100644 --- a/src/DotNext.Tests/OptionalTests.cs +++ b/src/DotNext.Tests/OptionalTests.cs @@ -322,10 +322,12 @@ public static void ConvertNoneToRefType() } [Fact] - public static void ConvertToOptional() + public static unsafe void ConvertToOptional() { Equal(Optional.None(), Optional.None().Convert(ToDouble)); + Equal(Optional.None(), Optional.None().Convert(&ToDouble)); Equal(42D, new Optional(42).Convert(ToDouble)); + Equal(42D, new Optional(42).Convert(&ToDouble)); static Optional ToDouble(int value) => double.CreateChecked(value); } diff --git a/src/DotNext.Tests/ResultTests.cs b/src/DotNext.Tests/ResultTests.cs index cb9e312b1..c18aa1a8d 100644 --- a/src/DotNext.Tests/ResultTests.cs +++ b/src/DotNext.Tests/ResultTests.cs @@ -185,6 +185,84 @@ public static unsafe void Conversion2() Equal(EnvironmentVariableTarget.Machine, result.Convert(&Convert.ToInt32).Error); } + [Fact] + public static unsafe void ConvertToResult() + { + // Standard conversion + Result validStringResult = "20"; + var convertedResult1 = validStringResult.Convert(ToInt); + True(convertedResult1.IsSuccessful); + Equal(20, convertedResult1); + + // Unsafe standard conversion + var convertedResult2 = validStringResult.Convert(&ToInt); + True(convertedResult2.IsSuccessful); + Equal(20, convertedResult2); + + // Failing conversion + Result invalidStringResult = "20F"; + var convertedResult3 = invalidStringResult.Convert(ToInt); + False(convertedResult3.IsSuccessful); + IsType(convertedResult3.Error); + + // Unsafe failing conversion + var convertedResult4 = invalidStringResult.Convert(&ToInt); + False(convertedResult4.IsSuccessful); + IsType(convertedResult4.Error); + + // Conversion of unsuccessful Result + Result exceptionResult = new(new ArgumentNullException()); + var convertedResult5 = exceptionResult.Convert(ToInt); + False(convertedResult5.IsSuccessful); + IsType(convertedResult5.Error); + + // Unsafe conversion of unsuccessful Result + var convertedResult6 = exceptionResult.Convert(&ToInt); + False(convertedResult6.IsSuccessful); + IsType(convertedResult6.Error); + + static Result ToInt(string value) => int.TryParse(value, out var result) ? result : throw new FormatException(); + } + + [Fact] + public unsafe static void ConvertToResultWithErrorCode() + { + // Standard conversion + Result validStringResult = "20"; + var convertedResult1 = validStringResult.Convert(ToInt); + True(convertedResult1.IsSuccessful); + Equal(20, convertedResult1); + + // Unsafe standard conversion + var convertedResult2 = validStringResult.Convert(&ToInt); + True(convertedResult2.IsSuccessful); + Equal(20, convertedResult2); + + // Failing conversion + Result invalidStringResult = "20F"; + var convertedResult3 = invalidStringResult.Convert(ToInt); + False(convertedResult3.IsSuccessful); + Equal(EnvironmentVariableTarget.Machine, convertedResult3.Error); + + // Unsafe failing conversion + var convertedResult4 = invalidStringResult.Convert(&ToInt); + False(convertedResult4.IsSuccessful); + Equal(EnvironmentVariableTarget.Machine, convertedResult4.Error); + + // Conversion of unsuccessful Result + Result errorCodeResult = new(EnvironmentVariableTarget.User); + var convertedResult5 = errorCodeResult.Convert(ToInt); + False(convertedResult5.IsSuccessful); + Equal(EnvironmentVariableTarget.User, convertedResult5.Error); + + // Unsafe conversion of unsuccessful Result + var convertedResult6 = errorCodeResult.Convert(&ToInt); + False(convertedResult6.IsSuccessful); + Equal(EnvironmentVariableTarget.User, convertedResult6.Error); + + static Result ToInt(string value) => int.TryParse(value, out var result) ? new(result) : new(EnvironmentVariableTarget.Machine); + } + [Fact] public static void HandleException() { diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index 8e61f3ba5..0f29cddfd 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -231,6 +231,30 @@ 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; + } + /// /// If the successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -241,6 +265,16 @@ 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 the successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -252,6 +286,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 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); + /// /// Attempts to extract value from the container if it is present. /// @@ -563,6 +608,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 the successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -573,6 +623,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 the successful result is present, apply the provided mapping function hiding any exception /// caused by the converter. @@ -584,6 +644,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