Skip to content
Draft
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"

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
()