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

Adjust parse / tryParse #558

Merged
merged 5 commits into from
Oct 13, 2023
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
89 changes: 81 additions & 8 deletions src/FSharpPlus/Control/Converter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,47 @@ type TryParse =
static member TryParse (_: string , _: TryParse) = fun x -> Some x : option<string>
static member TryParse (_: StringBuilder , _: TryParse) = fun x -> Some (new StringBuilder (x: string)) : option<StringBuilder>
#if !FABLE_COMPILER
static member TryParse (_: DateTime , _: TryParse) = fun (x:string) -> DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) |> tupleToOption : option<DateTime>
static member TryParse (_: DateTimeOffset, _: TryParse) = fun (x:string) -> DateTimeOffset.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.RoundtripKind) |> tupleToOption : option<DateTimeOffset>

static member TryParse (_: DateTime , _: TryParse) = fun (x:string) ->
match DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) with
| true, x -> Some x
| _ ->
match DateTime.TryParse (x, CultureInfo.InvariantCulture, DateTimeStyles.None) with
| true, x -> Some x
| _ -> None

static member TryParse (_: DateTimeOffset, _: TryParse) = fun (x:string) ->
match DateTimeOffset.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.AssumeUniversal) with
| true, x -> Some x
| _ ->
match DateTimeOffset.TryParse (x, CultureInfo.InvariantCulture, DateTimeStyles.None) with
| true, x -> Some x
| _ -> None
#endif

static member inline Invoke (value: string) =
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member TryParse : _*_ -> _) b, a)
let inline call (a: 'a) = fun (x: 'x) -> call_2 (a, Unchecked.defaultof<'r>) x : 'r option
call Unchecked.defaultof<TryParse> value

type TryParse with
static member inline TryParse (_: 'R, _: Default2) = fun x ->
/// The F# signature
static member inline InvokeOnInstance (value: string) = (^R: (static member TryParse : string -> 'R option) value)

/// The .Net signature
static member inline InvokeOnConvention (value: string) =
let mutable r = Unchecked.defaultof< ^R>
if (^R: (static member TryParse : _ * _ -> _) (x, &r)) then Some r else None
if (^R: (static member TryParse : _ * _ -> _) (value, &r)) then Some r else None

#if NET7_0
/// IParsable<'T>
static member InvokeOnInterface<'T when 'T :> IParsable<'T>> (value: string) =
let mutable r = Unchecked.defaultof<'T>
if ('T.TryParse(value, CultureInfo.InvariantCulture, &r)) then Some r else None
#endif

static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
static member inline TryParse (_: 'R, _: Default1) = fun x -> (^R: (static member TryParse : string -> 'R option) x)

type Parse =
inherit Default1
static member inline Parse (_: ^R , _: Default1) = fun (x:string) -> (^R: (static member Parse : _ -> ^R) x)
static member inline Parse (_: ^R , _: Parse ) = fun (x:string) -> (^R: (static member Parse : _ * _ -> ^R) (x, CultureInfo.InvariantCulture))

static member inline Parse (_: 'T when 'T : enum<_>, _: Parse ) = fun (x:string) ->
Expand All @@ -139,6 +160,16 @@ type Parse =
| _ -> invalidArg "value" ("Requested value '" + x + "' was not found.")
) : 'enum

#if !FABLE_COMPILER
static member Parse (_: DateTime , _: Parse) = fun (x:string) ->
match DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) with
| true, x -> x
| _ -> DateTime.Parse (x, CultureInfo.InvariantCulture)

static member Parse (_: DateTimeOffset, _: Parse) = fun (x:string) ->
try DateTimeOffset.ParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.AssumeUniversal)
with _ -> DateTimeOffset.Parse (x, CultureInfo.InvariantCulture)
#endif

static member Parse (_: bool , _: Parse) = fun (x:string) -> Boolean.Parse (x)

Expand All @@ -151,4 +182,46 @@ type Parse =
let inline call (a: 'a) = fun (x: 'x) -> call_2 (a, Unchecked.defaultof<'r>) x : 'r
call Unchecked.defaultof<Parse> value

static member inline InvokeOnInstance (value: string) = (^R: (static member Parse : _ -> ^R) value)


type Parse with

static member inline Parse (_: ^R , _: Default4) = fun (value: string) ->
match TryParse.InvokeOnConvention value with
| Some x -> x : ^R
| None -> invalidArg "value" ("Error parsing value '" + value + "'.")

static member inline Parse (_: ^R , _: Default3) = fun (value: string) ->
match TryParse.InvokeOnInstance value with
| Some x -> x : ^R
| None -> invalidArg "value" ("Error parsing value '" + value + "'.")

static member inline Parse (_: ^R , _: Default2) : string -> ^R = Parse.InvokeOnInstance

#if NET7_0
static member Parse<'T when 'T :> IParsable<'T>> (_: 'T, _: Default1) = fun (x: string) -> 'T.Parse (x, CultureInfo.InvariantCulture)
static member inline Parse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
#else
static member inline Parse (_: ^t when ^t: null and ^t: struct, _: Default2) = id
#endif

type TryParse with

static member inline TryParse (_: 'R, _: Default4) : string -> 'R option = fun (value: string) ->
try Some (Parse.InvokeOnInstance value) with
| :? ArgumentNullException | :? FormatException -> None
| _ -> reraise ()

static member inline TryParse (_: 'R, _: Default3) : string -> 'R option = TryParse.InvokeOnConvention

static member inline TryParse (_: 'R, _: Default2) : string -> 'R option = TryParse.InvokeOnInstance

#if NET7_0
static member inline TryParse (_: 'R, _: Default1) : string -> 'R option = TryParse.InvokeOnInterface
static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
#else
static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default2) = id
#endif

#endif
35 changes: 25 additions & 10 deletions tests/FSharpPlus.Tests/Parsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,33 @@ module Parsing =

[<Test>]
let parseDateTime () =
#if MONO
let v1 : DateTime = parse "2011-03-04T15:42:19+03:00"
Assert.IsTrue((v1 = DateTime(2011,3,4,12,42,19)))
#else
Assert.Ignore ("Depends on how it's executed...")
#endif

let t = DateTime(2011,3,14,12,42,19)
let u = DateTimeOffset(2011,3,14,15,42,19, TimeSpan.FromHours 3.)
let u0 = DateTimeOffset(2011,3,14,15,42,19, TimeSpan.FromHours 0.)

let t1 = parse<DateTime> "2011-03-14T15:42:19+03:00" in Assert.AreEqual( t, t1, nameof t1)
let t2 = tryParse<DateTime> "2011-03-14T15:42:19+03:00" in Assert.AreEqual(Some t, t2, nameof t2)

let u1 = parse<DateTimeOffset> "2011-03-14T15:42:19+03:00" in Assert.AreEqual( u, u1, nameof u1)
let u2 = tryParse<DateTimeOffset> "2011-03-14T15:42:19+03:00" in Assert.AreEqual(Some u, u2, nameof u2)

let t3 = parse<DateTime> "Mon, 14 Mar 2011 12:42:19 GMT" in Assert.AreEqual( t, t3, nameof t3)
let t4 = tryParse<DateTime> "Mon, 14 Mar 2011 12:42:19 GMT" in Assert.AreEqual(Some t, t4, nameof t4)

let u3 = parse<DateTimeOffset> "Mon, 14 Mar 2011 15:42:19 GMT" in Assert.AreEqual( u0, u3, nameof u3)
let u4 = tryParse<DateTimeOffset> "Mon, 14 Mar 2011 15:42:19 GMT" in Assert.AreEqual(Some u0, u4, nameof u4)

let u5 = parse<DateTimeOffset> "2011-03-14T15:42:19" in Assert.AreEqual( u0, u5, nameof u5)
let u6 = tryParse<DateTimeOffset> "2011-03-14T15:42:19" in Assert.AreEqual(Some u0, u6, nameof u6)

let u7 = parse<DateTimeOffset> "2011-03-14T15:42:19Z" in Assert.AreEqual( u0, u7, nameof u7)
let u8 = tryParse<DateTimeOffset> "2011-03-14T15:42:19Z" in Assert.AreEqual(Some u0, u8, nameof u8)



[<Test>]
let parse () =
let v2 : DateTimeOffset = parse "2011-03-04T15:42:19+03:00"

Assert.IsTrue((v2 = DateTimeOffset(2011,3,4,15,42,19, TimeSpan.FromHours 3.)))

let _101 = tryParse "10.1.0.1" : Net.IPAddress option
let _102 = tryParse "102" : string option
Expand All @@ -50,7 +65,7 @@ module Parsing =
areStEqual r66 (Some 66.0)

let r123: WrappedListA<int> option = tryParse "[1;2;3]"
areStEqual r123 (Some (WrappedListA [1; 2; 3]))
areStEqual r123 (Some (WrappedListA [1; 2; 3]))

[<Test>]
let parseCustomType () =
Expand Down
Loading