Skip to content
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
8 changes: 5 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -18,7 +18,7 @@ jobs:

- name: Set VERSION variable from tag
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
Expand All @@ -37,7 +37,9 @@ jobs:
run: dotnet pack --configuration Release /p:NuGetVersion=${VERSION} /p:PackageVersion=${VERSION} --include-symbols --output .

- name: Push
run: dotnet nuget push FSharp.Data.Validation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
run: |
dotnet nuget push FSharp.Data.Validation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
dotnet nuget push FSharp.Data.Validation.Async.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${MTA_NUGET_KEY} --symbol-source https://symbols.nuget.org/download/symbols --symbol-api-key ${MTA_NUGET_KEY}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devinlyons Does it make sense to release this in lock-step with FSharp.Data.Validation? I opted to do it this way since it directly references FSharp.Data.Validation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That direct reference will be converted to a NuGet reference. However, the version range will be locked down to the exact version. I guess that's a long way of saying yes, let's release them in lock step.


env:
MTA_NUGET_KEY: ${{ secrets.MTA_NUGET_KEY }}
14 changes: 14 additions & 0 deletions FSharp.Data.Validation.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.Validation.Test
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.Validation.Giraffe", "src\FSharp.Data.Validation.Giraffe\FSharp.Data.Validation.Giraffe.fsproj", "{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Data.Validation.Async", "src\FSharp.Data.Validation.Async\FSharp.Data.Validation.Async.fsproj", "{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -83,6 +85,18 @@ Global
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x64.Build.0 = Release|Any CPU
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x86.ActiveCfg = Release|Any CPU
{99EA41F7-1E77-4DD2-9D2E-9F7BE20441FB}.Release|x86.Build.0 = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x64.ActiveCfg = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x64.Build.0 = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x86.ActiveCfg = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Debug|x86.Build.0 = Debug|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|Any CPU.Build.0 = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x64.ActiveCfg = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x64.Build.0 = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x86.ActiveCfg = Release|Any CPU
{CF59CFDC-CE24-4F02-A454-2FE0CC1A9FA9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [Validating Nested Types](#validating-nested-types)
- [Validating Collections](#validating-collections)
- [Serializing The Proof Type](#serializing-the-proof-type)
- [Validating Async Data](#validating-async-data)
- [Validation Operations](#validation-operations)
- [`refute*` Operations](#refute-operations)
- [`refute`](#refute)
Expand Down Expand Up @@ -1065,6 +1066,52 @@ Here is an example of what it might look like.
}
```

### Validating Async Data

What if we need to validate data that is retrieved asynchronously?
There are three functions available in the `FSharp.Data.Validation.Async` package that can help with this:

- `bindToAsync`
- `bindAsync`
- `bindFromAsync`

The `bindToAsync` function is used to bind a value to an asynchronous computation.
The value is passed to the computation and the result is returned.

The `bindAsync` function is used to bind an asynchronous computation to a value.
The computation is executed and the result is passed to the function.

The `bindFromAsync` function is used to bind an asynchronous computation to another asynchronous computation.
The first computation is executed and the result is passed to the second computation.

Let's say we have a function that retrieves a user's data from a database.
We want to validate the data before we use it.
We can use the `bindToAsync` function to bind the data to a validation computation.

```fsharp
module Example

open FSharp.Data.Validation
open FSharp.Data.Validation.Async

let getUserData (id:int): Async<UserData> =
// get user data from database

let validateUserData (data:UserData): Proof<UserDataFailure, UserData> =
validation {
withValue data
// validate data
qed
} |> fromVCtx

let getUserDataAndValidate (id:int): Async<Proof<UserDataFailure, UserData>> =
getUserData id |> bindToAsync validateUserData
```

The `getUserDataAndValidate` function retrieves the user data and validates it.
The `bindToAsync` function is used to bind the data to the validation computation.
The result is an asynchronous computation that returns the validated data.

## Validation Operations

### `refute*` Operations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="VCtx.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FSharp.Data.Validation\FSharp.Data.Validation.fsproj" />
</ItemGroup>

</Project>
71 changes: 71 additions & 0 deletions src/FSharp.Data.Validation.Async/VCtx.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace FSharp.Data.Validation

[<RequireQualifiedAccess>]
module VCtx =
/// <summary>
/// Binds a function that returns an asynchronous computation to a validation context.
/// </summary>
/// <remarks>
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into an
/// asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c> and a validation context <c>c</c>
/// of type <c>VCtx&lt;'F, 'A&gt;</c>. It returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.
///
/// The function handles the following cases:
/// <list type="bullet">
/// <item>
/// <description><c>ValidCtx a</c>: Applies the function <c>fn</c> to <c>a</c> and returns the result.</description>
/// </item>
/// <item>
/// <description><c>RefutedCtx (gfs, lfs)</c>: Returns the same <c>RefutedCtx</c> without applying the function.</description>
/// </item>
/// <item>
/// <description><c>DisputedCtx (gfs, lfs, a)</c>: Applies the function <c>fn</c> to <c>a</c> and merges the results accordingly.</description>
/// </item>
/// </list>
/// </remarks>
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</param>
/// <param name="c">A validation context of type <c>VCtx&lt;'F, 'A&gt;</c>.</param>
/// <returns>An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</returns>
let bindToAsync (fn:'A -> Async<VCtx<'F, 'B>>) (c: VCtx<'F, 'A>): Async<VCtx<'F, 'B>> =
async {
match c with
| ValidCtx a -> return! fn a
| RefutedCtx (gfs,lfs) -> return RefutedCtx (gfs,lfs)
| DisputedCtx (gfs,lfs,a) ->
let! b = fn a
match b with
| ValidCtx b -> return DisputedCtx (gfs,lfs,b)
| DisputedCtx (gfs',lfs',b) -> return DisputedCtx (gfs @ gfs', Utilities.mergeFailures lfs lfs', b)
| RefutedCtx (gfs',lfs') -> return RefutedCtx (gfs @ gfs', Utilities.mergeFailures lfs lfs')
}

/// <summary>
/// Binds a function that returns a validation context to an asynchronous computation.
/// </summary>
/// <remarks>
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into a validation context
/// of type <c>VCtx&lt;'F, 'B&gt;</c> and an asynchronous computation <c>c</c> of type <c>Async&lt;VCtx&lt;'F, 'A&gt;&gt;</c>.
/// It returns an asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.
/// </remarks>
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</param>
/// <param name="c">An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'A&gt;&gt;</c>.</param>
/// <returns>An asynchronous computation of type <c>Async&lt;VCtx&lt;'F, 'B&gt;&gt;</c>.</returns>
let bindAsync (fn:'A -> Async<VCtx<'F, 'B>>) (c: Async<VCtx<'F, 'A>>): Async<VCtx<'F, 'B>> =
async {
let! c' = c
return! bindToAsync fn c'
}

/// <summary>
/// Binds a function that returns a validation context to a validation context.
/// </summary>
/// <remarks>
/// This function takes a function <c>fn</c> that transforms a value of type <c>'A</c> into a validation context
/// of type <c>VCtx&lt;'F, 'B&gt;</c> and a validation context <c>c</c> of type <c>VCtx&lt;'F, 'A&gt;</c>.
/// It returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.
/// </remarks>
/// <param name="fn">A function that takes a value of type <c>'A</c> and returns a validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</param>
/// <param name="c">A validation context of type <c>VCtx&lt;'F, 'A&gt;</c>.</param>
/// <returns>A validation context of type <c>VCtx&lt;'F, 'B&gt;</c>.</returns>
let bindFromAsync (fn:'A -> VCtx<'F, 'B>) (c: Async<VCtx<'F, 'A>>): Async<VCtx<'F, 'B>> =
bindAsync (fn >> async.Return) c
2 changes: 1 addition & 1 deletion src/FSharp.Data.Validation/Utilities.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module internal FSharp.Data.Validation.Utilities
module FSharp.Data.Validation.Utilities

// Given a sequence of options, return list of Some
let catOptions l = Seq.choose id l
Expand Down
1 change: 0 additions & 1 deletion src/FSharp.Data.Validation/VCtx.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ open System.Linq.Expressions
open FSharpPlus.Data

type VCtx<'F, 'A> =
internal
| ValidCtx of 'A
| DisputedCtx of 'F list * FailureMap<'F> * 'A
| RefutedCtx of 'F list * FailureMap<'F>
Expand Down
Loading