Skip to content

Commit

Permalink
Add TaskValidation module
Browse files Browse the repository at this point in the history
  • Loading branch information
ratsclub committed Nov 13, 2023
1 parent a0fa859 commit 2ec82b5
Show file tree
Hide file tree
Showing 7 changed files with 1,413 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
<Compile Include="TaskResultOption.fs" />
<Compile Include="TaskResultOptionCE.fs" />
<Compile Include="TaskResultOptionOp.fs" />
<Compile Include="TaskValidation.fs" />
<Compile Include="TaskValidationOp.fs" />
<Compile Include="TaskValidationCE.fs" />
<None Include="paket.references" />
<Watch Include="@(None)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FsToolkit.ErrorHandling\FsToolkit.ErrorHandling.fsproj" />
</ItemGroup>
<Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>
</Project>
193 changes: 193 additions & 0 deletions src/FsToolkit.ErrorHandling.TaskResult/TaskValidation.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
namespace FsToolkit.ErrorHandling

open System.Threading.Tasks

/// TaskValidation<'a, 'err> is defined as Async<Result<'a, 'err list>> meaning you can use many of the functions found in the Result and Async module.
type TaskValidation<'ok, 'error> = Task<Result<'ok, 'error list>>

[<RequireQualifiedAccess>]
module TaskValidation =
let inline ok (value: 'ok) : TaskValidation<'ok, 'error> =
Ok value
|> Task.FromResult

let inline error (error: 'error) : TaskValidation<'ok, 'error> =
Error [ error ]
|> Task.FromResult

let inline ofResult (result: Result<'ok, 'error>) : TaskValidation<'ok, 'error> =
Result.mapError List.singleton result
|> Task.FromResult


let inline ofChoice (choice: Choice<'ok, 'error>) : TaskValidation<'ok, 'error> =
match choice with
| Choice1Of2 x -> ok x
| Choice2Of2 e -> error e

let inline apply
(applier: TaskValidation<'okInput -> 'okOutput, 'error>)
(input: TaskValidation<'okInput, 'error>)
: TaskValidation<'okOutput, 'error> =
task {
let! applier = applier
let! input = input

return
match applier, input with
| Ok f, Ok x -> Ok(f x)
| Error errs, Ok _
| Ok _, Error errs -> Error errs
| Error errs1, Error errs2 ->
Error(
errs1
@ errs2
)
}

let inline retn (value: 'ok) : TaskValidation<'ok, 'error> = ok value

let inline returnError (error: 'error) : TaskValidation<'ok, 'error> =
Error [ error ]
|> Task.FromResult

let inline orElse
(ifError: TaskValidation<'ok, 'errorOutput>)
(result: TaskValidation<'ok, 'errorInput>)
: TaskValidation<'ok, 'errorOutput> =
task {
let! result = result

return!
result
|> Result.either ok (fun _ -> ifError)
}

let inline orElseWith
([<InlineIfLambda>] ifErrorFunc: 'errorInput list -> TaskValidation<'ok, 'errorOutput>)
(result: TaskValidation<'ok, 'errorInput>)
: TaskValidation<'ok, 'errorOutput> =
task {
let! result = result

return!
match result with
| Ok x -> ok x
| Error err -> ifErrorFunc err
}

let inline map
([<InlineIfLambda>] mapper: 'okInput -> 'okOutput)
(input: TaskValidation<'okInput, 'error>)
: TaskValidation<'okOutput, 'error> =
task {
let! input = input
return Result.map mapper input
}

let inline map2
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okOutput)
(input1: TaskValidation<'okInput1, 'error>)
(input2: TaskValidation<'okInput2, 'error>)
: TaskValidation<'okOutput, 'error> =
task {
let! input1 = input1
let! input2 = input2

return
match input1, input2 with
| Ok x, Ok y -> Ok(mapper x y)
| Ok _, Error errs -> Error errs
| Error errs, Ok _ -> Error errs
| Error errs1, Error errs2 ->
Error(
errs1
@ errs2
)
}

let inline map3
([<InlineIfLambda>] mapper: 'okInput1 -> 'okInput2 -> 'okInput3 -> 'okOutput)
(input1: TaskValidation<'okInput1, 'error>)
(input2: TaskValidation<'okInput2, 'error>)
(input3: TaskValidation<'okInput3, 'error>)
: TaskValidation<'okOutput, 'error> =
task {
let! input1 = input1
let! input2 = input2
let! input3 = input3

return
match input1, input2, input3 with
| Ok x, Ok y, Ok z -> Ok(mapper x y z)
| Error errs, Ok _, Ok _ -> Error errs
| Ok _, Error errs, Ok _ -> Error errs
| Ok _, Ok _, Error errs -> Error errs
| Error errs1, Error errs2, Ok _ ->
Error(
errs1
@ errs2
)
| Ok _, Error errs1, Error errs2 ->
Error(
errs1
@ errs2
)
| Error errs1, Ok _, Error errs2 ->
Error(
errs1
@ errs2
)
| Error errs1, Error errs2, Error errs3 ->
Error(
errs1
@ errs2
@ errs3
)
}

let inline mapError
([<InlineIfLambda>] errorMapper: 'errorInput -> 'errorOutput)
(input: TaskValidation<'ok, 'errorInput>)
: TaskValidation<'ok, 'errorOutput> =
task {
let! input = input
return Result.mapError (List.map errorMapper) input
}

let inline mapErrors
([<InlineIfLambda>] errorMapper: 'errorInput list -> 'errorOutput list)
(input: TaskValidation<'ok, 'errorInput>)
: TaskValidation<'ok, 'errorOutput> =
task {
let! input = input
return Result.mapError errorMapper input
}

let inline bind
([<InlineIfLambda>] binder: 'okInput -> TaskValidation<'okOutput, 'error>)
(input: TaskValidation<'okInput, 'error>)
: TaskValidation<'okOutput, 'error> =
task {
let! input = input

match input with
| Ok x -> return! binder x
| Error e -> return Error e
}

let inline zip
(left: TaskValidation<'left, 'error>)
(right: TaskValidation<'right, 'error>)
: TaskValidation<'left * 'right, 'error> =
task {
let! left = left
let! right = right

return
match left, right with
| Ok x1res, Ok x2res -> Ok(x1res, x2res)
| Error e, Ok _ -> Error e
| Ok _, Error e -> Error e
| Error e1, Error e2 -> Error(e1 @ e2)
}
Loading

0 comments on commit 2ec82b5

Please sign in to comment.