Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add defaultError and zipError #130

Merged
merged 5 commits into from
May 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling.JobResult/JobResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ module JobResult =
let defaultValue ifError jobResult =
jobResult |> Job.map (Result.defaultValue ifError)

/// Extracts the contained value of an job-wrapped result if Error, otherwise
/// uses ifOk.
let defaultError ifOk jobResult =
jobResult |> Job.map (Result.defaultError ifOk)

/// Extracts the contained value of an job-wrapped result if Ok, otherwise
/// evaluates ifErrorThunk and uses the result.
let defaultWith ifErrorThunk jobResult =
Expand Down Expand Up @@ -143,6 +148,11 @@ module JobResult =
let zip j1 j2 =
Job.zip j1 j2
|> Job.map(fun (r1, r2) -> Result.zip r1 r2)

/// Takes two results and returns a tuple of the error pair
let zipError j1 j2 =
Job.zip j1 j2
|> Job.map(fun (r1, r2) -> Result.zipError r1 r2)

/// Catches exceptions and maps them to the Error case using the provided function.
let catch f x =
Expand Down
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling.TaskResult/TaskResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ module TaskResult =
let defaultValue ifError taskResult =
taskResult |> Task.map (Result.defaultValue ifError)

/// Extracts the contained value of an task-wrapped result if Error, otherwise
/// uses ifOk.
let defaultError ifOk taskResult =
taskResult |> Task.map (Result.defaultError ifOk)

/// Extracts the contained value of an task-wrapped result if Ok, otherwise
/// evaluates ifErrorThunk and uses the result.
let defaultWith ifErrorThunk taskResult =
Expand Down Expand Up @@ -138,6 +143,11 @@ module TaskResult =
Task.zip x1 x2
|> Task.map(fun (r1, r2) -> Result.zip r1 r2)

/// Takes two results and returns a tuple of the error pair
let zipError x1 x2 =
Task.zip x1 x2
|> Task.map(fun (r1, r2) -> Result.zipError r1 r2)

/// Catches exceptions and maps them to the Error case using the provided function.
let catch f x =
x
Expand Down
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling/AsyncResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ module AsyncResult =
let defaultValue ifError asyncResult =
asyncResult |> Async.map (Result.defaultValue ifError)

/// Extracts the contained value of an async-wrapped result if Error, otherwise
/// uses ifOk.
let defaultError ifOk asyncResult =
asyncResult |> Async.map (Result.defaultError ifOk)

/// Extracts the contained value of an async-wrapped result if Ok, otherwise
/// evaluates ifErrorThunk and uses the result.
let defaultWith ifErrorThunk asyncResult =
Expand Down Expand Up @@ -148,6 +153,11 @@ module AsyncResult =
Async.zip x1 x2
|> Async.map(fun (r1, r2) -> Result.zip r1 r2)

/// Takes two results and returns a tuple of the error pair
let zipError x1 x2 =
Async.zip x1 x2
|> Async.map(fun (r1, r2) -> Result.zipError r1 r2)

/// Catches exceptions and maps them to the Error case using the provided function.
let catch f x =
x
Expand Down
15 changes: 14 additions & 1 deletion src/FsToolkit.ErrorHandling/Result.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ module Result =
| Ok x -> x
| Error _ -> ifError

// Returns the contained value if Error, otherwise returns ifOk.
let defaultError ifOk result =
match result with
| Error error -> error
| Ok _ -> ifOk

/// Returns the contained value if Ok, otherwise evaluates ifErrorThunk and
/// returns the result.
let defaultWith ifErrorThunk result =
Expand Down Expand Up @@ -178,4 +184,11 @@ module Result =
match x1,x2 with
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
| Error e, _ -> Error e
| _, Error e -> Error e
| _, Error e -> Error e

/// Takes two results and returns a tuple of the error pair
let zipError x1 x2 =
match x1, x2 with
| Error x1res, Error x2res -> Error(x1res, x2res)
| Ok e, _ -> Ok e
| _, Ok e -> Ok e
54 changes: 54 additions & 0 deletions tests/FsToolkit.ErrorHandling.JobResult.Tests/JobResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ let defaultValueTests =
Expect.hasJobValue 43 v
]

[<Tests>]
let defaultErrorTests =
testList "JobResult.defaultError Tests" [
testCase "defaultError returns the error value" <| fun _ ->
let v = JobResult.defaultError 43 (toJob (Error 42))
Expect.hasJobValue 42 v

testCase "defaultError returns the given value for Ok" <| fun _ ->
let v = JobResult.defaultError 43 (toJob (Ok 42))
Expect.hasJobValue 43 v
]

[<Tests>]
let defaultWithTests =
testList "JobResult.defaultWith Tests" [
Expand Down Expand Up @@ -470,6 +482,48 @@ let catchTests =
testCase "catch returns unmapped error without exception" <| fun _ ->
Expect.hasJobErrorValue "unmapped" (JobResult.catch f (toJob (Error "unmapped")))
]


[<Tests>]
let zipTests =
testList "JobResult.zip tests" [
testCase "Ok, Ok" <| fun _ ->
let v = JobResult.zip (toJob (Ok 1)) (toJob (Ok 2))
Expect.hasJobValue (Ok(1, 2)) v

testCase "Ok, Error" <| fun _ ->
let v = JobResult.zip (toJob (Ok 1)) (toJob (Error "Bad"))
Expect.hasJobValue (Error("Bad")) v

testCase "Error, Ok" <| fun _ ->
let v = JobResult.zip (toJob (Error "Bad")) (toJob (Ok 1))
Expect.hasJobValue (Error("Bad")) v

testCase "Error, Error" <| fun _ ->
let v = JobResult.zip (toJob (Error "Bad1")) (toJob (Error "Bad2"))
Expect.hasJobValue (Error("Bad1")) v
]

[<Tests>]
let zipErrorTests =
testList "JobResult.zipError tests" [
testCase "Ok, Ok" <| fun _ ->
let v = JobResult.zipError (toJob (Ok 1)) (toJob (Ok 2))
Expect.hasJobValue (Ok(1)) v

testCase "Ok, Error" <| fun _ ->
let v = JobResult.zipError (toJob (Ok 1)) (toJob (Error "Bad"))
Expect.hasJobValue (Ok 1) v

testCase "Error, Ok" <| fun _ ->
let v = JobResult.zipError (toJob (Error "Bad")) (toJob (Ok 1))
Expect.hasJobValue (Ok 1) v

testCase "Error, Error" <| fun _ ->
let v = JobResult.zipError (toJob (Error "Bad1")) (toJob (Error "Bad2"))
Expect.hasJobValue (Error("Bad1", "Bad2")) v
]


type CreatePostResult =
| PostSuccess of NotifyNewPostRequest
Expand Down
52 changes: 52 additions & 0 deletions tests/FsToolkit.ErrorHandling.TaskResult.Tests/TaskResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ let defaultValueTests =
Expect.hasTaskValue 43 v
]

[<Tests>]
let defaultErrorTests =
testList "TaskResult.defaultError Tests" [
testCase "defaultError returns the error value" <| fun _ ->
let v = TaskResult.defaultError 43 (toTask (Error 42))
Expect.hasTaskValue 42 v

testCase "defaultError returns the given value for Ok" <| fun _ ->
let v = TaskResult.defaultError 43 (toTask (Ok 42))
Expect.hasTaskValue 43 v
]

[<Tests>]
let defaultWithTests =
testList "TaskResult.defaultWith Tests" [
Expand Down Expand Up @@ -471,6 +483,46 @@ let catchTests =
Expect.hasTaskErrorValue "unmapped" (TaskResult.catch f (toTask (Error "unmapped")))
]

[<Tests>]
let zipTests =
testList "TaskResult.zip tests" [
testCase "Ok, Ok" <| fun _ ->
let v = TaskResult.zip (toTask (Ok 1)) (toTask (Ok 2))
Expect.hasTaskValue (Ok(1, 2)) v

testCase "Ok, Error" <| fun _ ->
let v = TaskResult.zip (toTask (Ok 1)) (toTask (Error "Bad"))
Expect.hasTaskValue (Error("Bad")) v

testCase "Error, Ok" <| fun _ ->
let v = TaskResult.zip (toTask (Error "Bad")) (toTask (Ok 1))
Expect.hasTaskValue (Error("Bad")) v

testCase "Error, Error" <| fun _ ->
let v = TaskResult.zip (toTask (Error "Bad1")) (toTask (Error "Bad2"))
Expect.hasTaskValue (Error("Bad1")) v
]

[<Tests>]
let zipErrorTests =
testList "TaskResult.zipError tests" [
testCase "Ok, Ok" <| fun _ ->
let v = TaskResult.zipError (toTask (Ok 1)) (toTask (Ok 2))
Expect.hasTaskValue (Ok(1)) v

testCase "Ok, Error" <| fun _ ->
let v = TaskResult.zipError (toTask (Ok 1)) (toTask (Error "Bad"))
Expect.hasTaskValue (Ok 1) v

testCase "Error, Ok" <| fun _ ->
let v = TaskResult.zipError (toTask (Error "Bad")) (toTask (Ok 1))
Expect.hasTaskValue (Ok 1) v

testCase "Error, Error" <| fun _ ->
let v = TaskResult.zipError (toTask (Error "Bad1")) (toTask (Error "Bad2"))
Expect.hasTaskValue (Error("Bad1", "Bad2")) v
]

type CreatePostResult =
| PostSuccess of NotifyNewPostRequest
| NotAllowedToPost
Expand Down
58 changes: 58 additions & 0 deletions tests/FsToolkit.ErrorHandling.Tests/AsyncResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,18 @@ let defaultValueTests =
]


let defaultErrorTests =
testList "AsyncResult.defaultError Tests" [
testCaseAsync "defaultError returns the error value" <|
(let v = AsyncResult.defaultError 43 (toAsync (Error 42))
Expect.hasAsyncValue 42 v)

testCaseAsync "defaultError returns the given value for Ok" <|
(let v = AsyncResult.defaultError 43 (toAsync (Ok 42))
Expect.hasAsyncValue 43 v)
]


let defaultWithTests =
testList "AsyncResult.defaultWith Tests" [
testCaseAsync "defaultWith returns the ok value" <|
Expand Down Expand Up @@ -535,6 +547,49 @@ let asyncResultOperatorTests =
|> Expect.hasAsyncOkValue (PostId newPostId)
}
]


let zipTests =
testList "AsyncResult.zip Tests" [
testCaseAsync "Ok, Ok" <| async {
let! v = AsyncResult.zip (toAsync (Ok 1)) (toAsync (Ok 2))
Expect.equal v (Ok(1, 2)) "Should be ok"
}
testCaseAsync "Ok, Error" <| async {
let! v = AsyncResult.zip (toAsync (Ok 1)) (toAsync (Error "Bad"))
Expect.equal v (Error("Bad")) "Should be Error"
}
testCaseAsync "Error, Ok" <| async {
let! v = AsyncResult.zip (toAsync (Error "Bad")) (toAsync (Ok 1))
Expect.equal v (Error("Bad")) "Should be Error"
}
testCaseAsync "Error, Error" <| async {
let! v = AsyncResult.zip (toAsync (Error "Bad1")) (toAsync (Error "Bad2"))
Expect.equal v (Error("Bad1")) "Should be Error"
}
]


let zipErrorTests =
testList "AsyncResult.zipError Tests" [
testCaseAsync "Ok, Ok" <| async {
let! v = AsyncResult.zipError (toAsync (Ok 1)) (toAsync (Ok 2))
Expect.equal v (Ok(1)) "Should be ok"
}
testCaseAsync "Ok, Error" <| async {
let! v = AsyncResult.zipError (toAsync (Ok 1)) (toAsync (Error "Bad"))
Expect.equal v (Ok 1) "Should be ok"
}
testCaseAsync "Error, Ok" <| async {
let! v = AsyncResult.zipError (toAsync (Error "Bad")) (toAsync (Ok 1))
Expect.equal v (Ok 1) "Should be ok"
}
testCaseAsync "Error, Error" <| async {
let! v = AsyncResult.zipError (toAsync (Error "Bad1")) (toAsync (Error "Bad2"))
Expect.equal v (Error("Bad1", "Bad2")) "Should be Error"
}
]


let allTests = testList "Async Result tests" [
mapTests
Expand All @@ -554,6 +609,7 @@ let allTests = testList "Async Result tests" [
setErrorTests
withErrorTests
defaultValueTests
defaultErrorTests
defaultWithTests
ignoreErrorTests
teeTests
Expand All @@ -563,4 +619,6 @@ let allTests = testList "Async Result tests" [
catchTests
asyncResultCETests
asyncResultOperatorTests
zipTests
zipErrorTests
]
30 changes: 30 additions & 0 deletions tests/FsToolkit.ErrorHandling.Tests/Result.fs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,18 @@ let defaultValueTests =
]


let defaultErrorTests =
testList "defaultError Tests" [
testCase "defaultError returns the error value" <| fun _ ->
let v = Result.defaultError 43 (Error 42)
Expect.equal v 42 ""

testCase "defaultError returns the given value for Ok" <| fun _ ->
let v = Result.defaultError 43 (Ok 42)
Expect.equal v 43 ""
]


let defaultWithTests =
testList "defaultWith Tests" [
testCase "defaultWith returns the ok value" <| fun _ ->
Expand Down Expand Up @@ -565,6 +577,22 @@ let zipTests =
Expect.equal actual (Error "Bad1") "Should be Error"
]

let zipErrorTests =
testList "zipError tests" [
testCase "Ok, Ok" <| fun () ->
let actual = Result.zipError (Ok 1) (Ok 2)
Expect.equal actual (Ok (1)) "Should be ok"
testCase "Ok, Error" <| fun () ->
let actual = Result.zipError (Ok 1) (Error "Bad")
Expect.equal actual (Ok 1) "Should be ok"
testCase "Error, Ok" <| fun () ->
let actual = Result.zipError (Error "Bad") (Ok 1)
Expect.equal actual (Ok 1) "Should be ok"
testCase "Error, Error" <| fun () ->
let actual = Result.zipError (Error "Bad1") (Error "Bad2")
Expect.equal actual (Error ("Bad1", "Bad2")) "Should be Error"
]

let allTests = testList "Result Tests" [
resultIsOk
resultIsError
Expand Down Expand Up @@ -592,6 +620,7 @@ let allTests = testList "Result Tests" [
setErrorTests
withErrorTests
defaultValueTests
defaultErrorTests
defaultWithTests
ignoreErrorTests
teeTests
Expand All @@ -601,4 +630,5 @@ let allTests = testList "Result Tests" [
sequenceAsyncTests
valueOrTests
zipTests
zipErrorTests
]