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
26 changes: 26 additions & 0 deletions src/FSharp.Data.Csv.Core/CsvExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ open FSharp.Data.Runtime
[<Extension>]
type StringExtensions =

/// <summary>Converts the string to an integer. Fails if the value is not a valid integer.</summary>
/// <param name="x">The string to convert.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsInteger(x: String, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -21,6 +24,9 @@ type StringExtensions =
| Some i -> i
| _ -> failwithf "Not an int: %s" x

/// <summary>Converts the string to a 64-bit integer. Fails if the value is not a valid 64-bit integer.</summary>
/// <param name="x">The string to convert.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsInteger64(x: String, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -29,6 +35,9 @@ type StringExtensions =
| Some i -> i
| _ -> failwithf "Not an int64: %s" x

/// <summary>Converts the string to a decimal. Fails if the value is not a valid decimal.</summary>
/// <param name="x">The string to convert.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsDecimal(x: String, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -37,6 +46,10 @@ type StringExtensions =
| Some d -> d
| _ -> failwithf "Not a decimal: %s" x

/// <summary>Converts the string to a float. Fails if the value is not a valid float.</summary>
/// <param name="x">The string to convert.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
/// <param name="missingValues">Values to treat as missing (NaN). Defaults to standard missing value strings.</param>
[<Extension>]
static member AsFloat(x: String, [<Optional>] ?cultureInfo, [<Optional>] ?missingValues) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -46,12 +59,17 @@ type StringExtensions =
| Some f -> f
| _ -> failwithf "Not a float: %s" x

/// <summary>Converts the string to a boolean. Fails if the value is not a valid boolean.</summary>
/// <param name="x">The string to convert. Accepts "true", "false", "yes", "no", "1", "0" (case-insensitive).</param>
[<Extension>]
static member AsBoolean(x: String) =
match TextConversions.AsBoolean x with
| Some b -> b
| _ -> failwithf "Not a boolean: %s" x

/// <summary>Converts the string to a DateTime. Fails if the value is not a valid date/time string.</summary>
/// <param name="x">The string to convert. Accepts ISO 8601 format or MSFT JSON date format.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsDateTime(x: String, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -60,6 +78,9 @@ type StringExtensions =
| Some d -> d
| _ -> failwithf "Not a datetime: %s" x

/// <summary>Converts the string to a DateTimeOffset. Fails if the value is not a valid date/time with offset string.</summary>
/// <param name="x">The string to convert. Accepts ISO 8601 format with timezone offset or MSFT JSON date with offset.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsDateTimeOffset(x, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -68,6 +89,9 @@ type StringExtensions =
| Some d -> d
| _ -> failwithf "Not a datetime offset: %s" <| x

/// <summary>Converts the string to a TimeSpan. Fails if the value is not a valid time span string.</summary>
/// <param name="x">The string to convert.</param>
/// <param name="cultureInfo">Optional culture info for parsing. Defaults to InvariantCulture.</param>
[<Extension>]
static member AsTimeSpan(x: String, [<Optional>] ?cultureInfo) =
let cultureInfo = defaultArg cultureInfo CultureInfo.InvariantCulture
Expand All @@ -76,6 +100,8 @@ type StringExtensions =
| Some t -> t
| _ -> failwithf "Not a time span: %s" x

/// <summary>Converts the string to a Guid. Fails if the value is not a valid GUID string.</summary>
/// <param name="x">The string to convert.</param>
[<Extension>]
static member AsGuid(x: String) =
match x |> TextConversions.AsGuid with
Expand Down
10 changes: 10 additions & 0 deletions src/FSharp.Data.Html.Core/HtmlActivePatterns.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ namespace FSharp.Data
#if HIDE_REPRESENTATION
[<AutoOpen>]
#endif
/// Active patterns for decomposing HtmlNode and HtmlAttribute values
module HtmlActivePatterns =

/// <summary>
/// Active pattern that decomposes an <see cref="HtmlNode"/> into one of four cases:
/// HtmlElement (name, attributes, child elements), HtmlText (text content),
/// HtmlComment (comment content), or HtmlCData (CDATA content).
/// </summary>
let (|HtmlElement|HtmlText|HtmlComment|HtmlCData|) (node: HtmlNode) =
match node with
| HtmlNode.HtmlText content -> HtmlText(content)
| HtmlNode.HtmlComment content -> HtmlComment(content)
| HtmlNode.HtmlCData content -> HtmlCData(content)
| HtmlNode.HtmlElement(name, attributes, elements) -> HtmlElement(name, attributes, elements)

/// <summary>
/// Active pattern that decomposes an <see cref="HtmlAttribute"/> into its name and value.
/// </summary>
let (|HtmlAttribute|) (attribute: HtmlAttribute) =
match attribute with
| HtmlAttribute.HtmlAttribute(name, value) -> HtmlAttribute(name, value)
39 changes: 35 additions & 4 deletions src/FSharp.Data.Runtime.Utilities/TextConversions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,32 @@ type TextConversions private () =
static member AsString str =
if String.IsNullOrWhiteSpace str then None else Some str

/// <summary>Attempts to parse the string as an integer using the given culture.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse. Currency and percentage adorners are removed before parsing.</param>
static member AsInteger cultureInfo text =
Int32.TryParse(TextConversions.RemoveAdorners text, NumberStyles.Integer, cultureInfo)
|> asOption

/// <summary>Attempts to parse the string as a 64-bit integer using the given culture.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse. Currency and percentage adorners are removed before parsing.</param>
static member AsInteger64 cultureInfo text =
Int64.TryParse(TextConversions.RemoveAdorners text, NumberStyles.Integer, cultureInfo)
|> asOption

/// <summary>Attempts to parse the string as a decimal using the given culture.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse. Currency and percentage adorners are removed before parsing.</param>
static member AsDecimal cultureInfo text =
Decimal.TryParse(TextConversions.RemoveAdorners text, NumberStyles.Currency, cultureInfo)
|> asOption

/// if useNoneForMissingValues is true, NAs are returned as None, otherwise Some Double.NaN is used
/// <summary>Attempts to parse the string as a float using the given culture.</summary>
/// <param name="missingValues">Values to treat as missing. If matched, returns None or Some NaN depending on <paramref name="useNoneForMissingValues"/>.</param>
/// <param name="useNoneForMissingValues">If true, missing values and NaN are returned as None; otherwise Some Double.NaN is used.</param>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse.</param>
static member AsFloat missingValues useNoneForMissingValues cultureInfo (text: string) =
match text.Trim() with
| OneOfIgnoreCase missingValues -> if useNoneForMissingValues then None else Some Double.NaN
Expand All @@ -138,6 +151,8 @@ type TextConversions private () =
else
Some f)

/// <summary>Attempts to parse the string as a boolean. Accepts "true", "false", "yes", "no", "1", "0" (case-insensitive).</summary>
/// <param name="text">The string to parse.</param>
static member AsBoolean(text: string) =
match text.Trim() with
| StringEqualsIgnoreCase "true"
Expand All @@ -148,9 +163,9 @@ type TextConversions private () =
| StringEqualsIgnoreCase "0" -> Some false
| _ -> None

/// Parse date time using either the JSON milliseconds format or using ISO 8601
/// that is, either `/Date(<msec-since-1/1/1970>)/` or something
/// along the lines of `2013-01-28T00:37Z`
/// <summary>Attempts to parse the string as a DateTime using ISO 8601 format or MSFT JSON date format.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse. Accepts e.g. <c>2013-01-28T00:37Z</c> or <c>/Date(1234567890)/</c>.</param>
static member AsDateTime cultureInfo (text: string) =
// Try parse "Date(<msec>)" style format
let matchesMS = msDateRegex.Value.Match(text.Trim())
Expand All @@ -167,6 +182,9 @@ type TextConversions private () =
| ValueSome x -> Some x
| ValueNone -> None

/// <summary>Attempts to parse the string as a DateTimeOffset using ISO 8601 format or MSFT JSON date with offset.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse. The timezone offset must be present.</param>
static member AsDateTimeOffset cultureInfo (text: string) =
// get TimeSpan presentation from 4-digit integers like 0000 or -0600
let getTimeSpanFromHourMin (hourMin: int) =
Expand Down Expand Up @@ -203,12 +221,19 @@ type TextConversions private () =
| false, _ -> None
| _ -> None

/// <summary>Attempts to parse the string as a TimeSpan using the given culture.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse.</param>
static member AsTimeSpan (cultureInfo: CultureInfo) (text: string) =
match TimeSpan.TryParse(text, cultureInfo) with
| true, t -> Some t
| _ -> None

#if NET6_0_OR_GREATER
/// <summary>Attempts to parse the string as a DateOnly using the given culture.
/// Strings that also parse as a DateTime with a non-zero time component are rejected.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse.</param>
static member AsDateOnly (cultureInfo: CultureInfo) (text: string) =
let mutable d = DateOnly.MinValue

Expand All @@ -221,6 +246,10 @@ type TextConversions private () =
else
None

/// <summary>Attempts to parse the string as a TimeOnly using the given culture.
/// Strings that also parse as a DateTime with a specific non-today date are rejected.</summary>
/// <param name="cultureInfo">The culture to use for parsing.</param>
/// <param name="text">The string to parse.</param>
static member AsTimeOnly (cultureInfo: CultureInfo) (text: string) =
let mutable t = TimeOnly.MinValue

Expand All @@ -234,6 +263,8 @@ type TextConversions private () =
None
#endif

/// <summary>Attempts to parse the string as a Guid.</summary>
/// <param name="text">The string to parse. Leading and trailing whitespace is trimmed before parsing.</param>
static member AsGuid(text: string) = Guid.TryParse(text.Trim()) |> asOption

module internal UnicodeHelper =
Expand Down
Loading