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 some extensions methods for validation #518

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
1 change: 0 additions & 1 deletion src/FSharpPlus/Data/Validation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ module Validation =
coll1.Close (), coll2.Close ()
#endif


type Validation<'err,'a> with

// as Applicative
Expand Down
75 changes: 75 additions & 0 deletions src/FSharpPlus/Extensions/Validators.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace FSharpPlus

[<RequireQualifiedAccess>]
module RequiredValidation =
open System
open FSharpPlus.Data

let inline private validate error f v =
if f v then
Success v
else
Failure [ error ]

let inline notNullOrWhiteSpace error value =
validate (error value) (String.IsNullOrWhiteSpace >> not) value

let inline greaterThan error min value =
validate (error value) (flip (>) min) value

let inline greaterOrEqualThan error min value =
validate (error value) (flip (>=) min) value

let inline email error value =
let check (v: string) =
try
let _ = Net.Mail.MailAddress(v)
true
with
| ex -> false

validate (error value) check value

let inline guidNotEmpty error value =
validate (error value) (fun v -> v <> Guid.Empty) value

let inline object error value =
let check value = box value <> null
validate (error value) check

let inline whenSome value checkWhenSome =
match value with
| Some v -> checkWhenSome v |> Validation.map Some
| _ -> Success None

let inline arrayValues values check =
let validated : Validation<_,_> [] =
values
|> Array.map check
validated
|> sequence
|> Validation.map Seq.toArray

let inline listValues values check =
let validated : List<Validation<_,_>> =
values
|> List.map check
validated
|> sequence
|> Validation.map Seq.toArray

let inline atLeastOne error =
let check values =
Seq.isEmpty values |> not

validate error check

let isSuccess =
function
| Success _ -> true
| Failure _ -> false

let isFailure =
function
| Success _ -> false
| Failure _ -> true
1 change: 1 addition & 0 deletions src/FSharpPlus/FSharpPlus.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<Compile Include="Data/Coproduct.fs" />
<Compile Include="Extensions/Observable.fs" />
<Compile Include="Extensions/AsyncEnumerable.fs" />
<Compile Include="Extensions\Validators.fs" />
<Compile Include="Memoization.fs" />
<Compile Include="Parsing.fs" />
</ItemGroup>
Expand Down
63 changes: 61 additions & 2 deletions tests/FSharpPlus.Tests/Validations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@ module Validation =
open FSharpPlus.Data
open Validation
open FSharpPlus.Tests.Helpers


let private getSuccess =
function
| Success s -> s
| Failure _ -> failwith "It's a failure"
rodriguestiago0 marked this conversation as resolved.
Show resolved Hide resolved

let private getFailure =
function
| Success _ -> failwith "It's a Success"
| Failure f -> f

let fsCheck s x = Check.One({Config.QuickThrowOnFailure with Name = s}, x)
module FunctorP =
[<Test>]
Expand Down Expand Up @@ -337,4 +347,53 @@ module Validation =
let v: Validation<string Async, int Async> = Success (async {return 42})
let r = Validation.bisequence v
let subject = Async.RunSynchronously r
areStEqual subject (Success 42)
areStEqual subject (Success 42)

[<Test>]
[<TestCase("", false)>]
[<TestCase(" ", false)>]
[<TestCase(null, false)>]
[<TestCase("NotEmpty", true)>]
let testValidateRequireString (str, success) =
let error = konst "Str"
let r = RequiredValidation.string error str
areStEqual (RequiredValidation.isSuccess r) success

if not success then
let failure = getFailure r
areStEqual failure.Length 1
areStEqual failure.[0] (error "")
else
()

[<Test>]
[<TestCase(1, 0, true)>]
[<TestCase(0, 0, false)>]
[<TestCase(-1, 0, false)>]
let testValidateRequireGreaterThan (value, limit, success) =
let error = konst "Int"
let r = RequiredValidation.greaterThan error limit value
areStEqual (RequiredValidation.isSuccess r) success

if not success then
let failure = getFailure r
areStEqual failure.Length 1
areStEqual failure.[0] (error 1)
else
()

[<Test>]
[<TestCase(1, 0, true)>]
[<TestCase(0, 0, true)>]
[<TestCase(-1, 0, false)>]
let testValidateRequireGreaterOrEqualThan (value, limit, success) =
let error = konst "Int"
let r = RequiredValidation.greaterOrEqualThan error limit value
areStEqual (RequiredValidation.isSuccess r) success

if not success then
let failure = getFailure r
areStEqual failure.Length 1
areStEqual failure.[0] (error 1)
else
()