From 03efbf4cd84cae04dbcadb34359b2038a527a599 Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Wed, 1 May 2024 21:24:11 -0500 Subject: [PATCH] Added missing Task bindings. Fixed source overloads. Removed unnecessary Result qualifiers --- .../AsyncValidationCE.fs | 158 +++++++++++------- .../AsyncValidationCE.fs | 82 ++++++--- 2 files changed, 157 insertions(+), 83 deletions(-) diff --git a/src/FsToolkit.ErrorHandling/AsyncValidationCE.fs b/src/FsToolkit.ErrorHandling/AsyncValidationCE.fs index 0bfaf174..aa64e4f7 100644 --- a/src/FsToolkit.ErrorHandling/AsyncValidationCE.fs +++ b/src/FsToolkit.ErrorHandling/AsyncValidationCE.fs @@ -124,68 +124,100 @@ module AsyncValidationCE = ) : AsyncValidation<'left * 'right, 'error> = AsyncValidation.zip left right - /// - /// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type. - /// - /// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder - /// - /// - /// - member inline _.Source - (result: AsyncValidation<'ok, 'error>) - : AsyncValidation<'ok, 'error> = - result - let asyncValidation = AsyncValidationBuilder() -[] -module LowPriority = - - // Having members as extensions gives them lower priority in - // overload resolution and allows skipping more type annotations. - type AsyncValidationBuilder with - - /// - /// Method lets us transform data types into our internal representation. - /// - member inline _.Source(s: Async>) : AsyncValidation<'ok, 'error> = - AsyncResult.mapError List.singleton s - - /// - /// Method lets us transform data types into our internal representation. - /// - member inline _.Source(s: Result<'ok, 'error list>) : AsyncValidation<'ok, 'error> = - Async.retn s - -[] -module HighPriority = - - type AsyncValidationBuilder with - - /// - /// Method lets us transform data types into our internal representation. - /// - /// - member inline _.Source(a: Async<'ok>) : AsyncValidation<'ok, 'error> = - async { - let! result = a - return! AsyncValidation.ok result - } - - /// - /// Method lets us transform data types into our internal representation. - /// - member inline _.Source(s: Result<'ok, 'error>) : AsyncValidation<'ok, 'error> = - AsyncValidation.ofResult s - - /// - /// Method lets us transform data types into our internal representation. - /// - /// - member inline _.Source(choice: Choice<'ok, 'error>) : AsyncValidation<'ok, 'error> = - AsyncValidation.ofChoice choice - - /// - /// Needed to allow `for..in` and `for..do` functionality - /// - member inline _.Source(s: #seq<_>) : #seq<_> = s + [] + module LowPriority = + + type AsyncValidationBuilder with + + /// + /// Method lets us transform data types into our internal representation. + /// + /// + member inline _.Source(a: Async<'ok>) : AsyncValidation<'ok, 'error> = + async { + let! result = a + return! AsyncValidation.ok result + } + + /// + /// Method lets us transform data types into our internal representation. + /// + member inline _.Source(s: Result<'ok, 'error>) : AsyncValidation<'ok, 'error> = + AsyncValidation.ofResult s + + /// + /// Method lets us transform data types into our internal representation. + /// + /// + member inline _.Source(choice: Choice<'ok, 'error>) : AsyncValidation<'ok, 'error> = + AsyncValidation.ofChoice choice + + /// + /// Needed to allow `for..in` and `for..do` functionality + /// + member inline _.Source(s: #seq<_>) : #seq<_> = s + + [] + module MediumPriority = + + open System.Threading.Tasks + + type AsyncValidationBuilder with + + /// + /// Method lets us transform data types into our internal representation. + /// + member inline _.Source(s: Async>) : AsyncValidation<'ok, 'error> = + AsyncResult.mapError List.singleton s + +#if !FABLE_COMPILER + + /// + /// Method lets us transform data types into our internal representation. + /// + member inline _.Source(s: Task>) : AsyncValidation<'ok, 'error> = + Async.AwaitTask s + |> AsyncResult.mapError List.singleton + +#endif + + [] + module HighPriority = + + open System.Threading.Tasks + + // Having members as extensions gives them lower priority in + // overload resolution and allows skipping more type annotations. + type AsyncValidationBuilder with + + /// + /// Method lets us transform data types into our internal representation. + /// + member inline _.Source(s: Validation<'ok, 'error>) : AsyncValidation<'ok, 'error> = + Async.retn s + +#if !FABLE_COMPILER + + /// + /// Method lets us transform data types into our internal representation. + /// + member inline _.Source + (result: Task>) + : AsyncValidation<'ok, 'error> = + Async.AwaitTask result + +#endif + + /// + /// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type. + /// + /// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder + /// + /// + /// + member inline _.Source + (result: AsyncValidation<'ok, 'error>) + : AsyncValidation<'ok, 'error> = + result diff --git a/tests/FsToolkit.ErrorHandling.Tests/AsyncValidationCE.fs b/tests/FsToolkit.ErrorHandling.Tests/AsyncValidationCE.fs index 0400e76f..40594864 100644 --- a/tests/FsToolkit.ErrorHandling.Tests/AsyncValidationCE.fs +++ b/tests/FsToolkit.ErrorHandling.Tests/AsyncValidationCE.fs @@ -23,7 +23,7 @@ let ``AsyncValidationCE return Tests`` = <| async { let data = "Foo" let! actual = asyncValidation { return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } ] @@ -31,7 +31,7 @@ let ``AsyncValidationCE return! Tests`` = testList "AsyncValidationCE return! Tests" [ testCaseAsync "Return Ok result" <| async { - let data = Result.Ok "Foo" + let data = Ok "Foo" let! actual = asyncValidation { return! data } Expect.equal actual (data) "Should be ok" } @@ -48,7 +48,7 @@ let ``AsyncValidationCE return! Tests`` = let innerData = "Foo" let data = Choice1Of2 innerData let! actual = asyncValidation { return! data } - Expect.equal actual (Result.Ok innerData) "Should be ok" + Expect.equal actual (Ok innerData) "Should be ok" } testCaseAsync "Return Error Choice" <| async { @@ -63,7 +63,7 @@ let ``AsyncValidationCE return! Tests`` = let innerData = "Foo" let data = Validation.ok innerData let! actual = asyncValidation { return! data } - Expect.equal actual (Result.Ok innerData) "Should be ok" + Expect.equal actual (Ok innerData) "Should be ok" } testCaseAsync "Return Error Validation" <| async { @@ -92,7 +92,7 @@ let ``AsyncValidationCE bind Tests`` = } testCaseAsync "let! Ok result" <| async { - let data = Result.Ok "Foo" + let data = Ok "Foo" let! actual = asyncValidation { @@ -127,7 +127,7 @@ let ``AsyncValidationCE bind Tests`` = return f } - Expect.equal actual (Result.Ok innerData) "Should be ok" + Expect.equal actual (Ok innerData) "Should be ok" } testCaseAsync "let! Error Choice" <| async { @@ -153,17 +153,18 @@ let ``AsyncValidationCE bind Tests`` = return f } - Expect.equal actual (Result.Ok innerData) "Should be ok" + Expect.equal actual (Ok innerData) "Should be ok" } testCaseAsync "let! Error Validation" <| async { let innerData = "Foo" let error = Error innerData - let expected = Error [ [ innerData ] ] + let expected = Error [ innerData ] let! actual = asyncValidation { let! f = validation { return! error } + and! _ = validation { return! Ok innerData } return f } @@ -171,7 +172,7 @@ let ``AsyncValidationCE bind Tests`` = } testCaseAsync "do! Ok result" <| async { - let data = Result.Ok() + let data = Ok() let! actual = asyncValidation { do! data } Expect.equal actual (data) "Should be ok" } @@ -233,7 +234,7 @@ let ``AsyncValidationCE combine/zero/delay/run Tests`` = return result } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } ] @@ -256,7 +257,7 @@ let ``AsyncValidationCE try Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } testCaseAsync "Try Finally" <| async { @@ -274,7 +275,7 @@ let ``AsyncValidationCE try Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } ] @@ -295,7 +296,7 @@ let ``AsyncValidationCE using Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } testCaseAsync "use! normal wrapped disposable" <| async { @@ -305,12 +306,12 @@ let ``AsyncValidationCE using Tests`` = asyncValidation { use! d = makeDisposable () - |> Result.Ok + |> Ok return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } testCaseAsync "use null disposable" <| async { @@ -322,7 +323,7 @@ let ``AsyncValidationCE using Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } ] @@ -404,7 +405,7 @@ let ``AsyncValidationCE loop Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } testCaseAsync "for to" <| async { @@ -418,7 +419,7 @@ let ``AsyncValidationCE loop Tests`` = return data } - Expect.equal actual (Result.Ok data) "Should be ok" + Expect.equal actual (Ok data) "Should be ok" } ] @@ -475,13 +476,14 @@ let ``AsyncValidationCE applicative tests`` = Expect.equal actual (Ok 4) "Should be ok" } - testCaseAsync "Happy Path Result/Choice/Validation" + testCaseAsync "Happy Path Result/Choice/Task/Validation" <| async { let! actual = asyncValidation { let! a = Ok 3 and! b = Choice1Of2 2 and! c = AsyncValidation.ok 1 + return a + b - c } @@ -490,6 +492,12 @@ let ``AsyncValidationCE applicative tests`` = testCaseAsync "Happy Path Async Result/Async Result" <| async { + let expected = + Error [ + "Hello" + "World" + ] + let! actual = asyncValidation { let! _ = @@ -509,9 +517,43 @@ let ``AsyncValidationCE applicative tests`` = return () } - Expect.equal actual (Ok()) "Should be ok" + Expect.equal actual expected "Should be error" + } + +#if !FABLE_COMPILER + + testCaseAsync "Happy Path Task Result/Task Result" + <| async { + let expected = + Error [ + "Hello" + "World" + ] + + let! actual = + asyncValidation { + let! _ = + task { + do! Async.Sleep(1000) + printfn "Hello" + return Error "Hello" + } + + and! _ = + task { + do! Async.Sleep(1000) + printfn "World" + return Error "World" + } + + return () + } + + Expect.equal actual expected "Should be error" } +#endif + testCaseAsync "Fail Path Result" <| async { let expected =