From 12f3c8486eb64d58b364df0383d232c5b28e3315 Mon Sep 17 00:00:00 2001 From: Thomas Bruderer Date: Wed, 30 Aug 2023 11:11:33 +0200 Subject: [PATCH 1/3] Implement Generic ParseOrNone extensions. --- FrameworkFeatureConstants.props | 3 +++ .../ParseExtensionsTest.Version.cs | 8 ++++---- .../ParseExtensions.GenericNumber.cs | 20 +++++++++++++++++++ .../ParseExtensions.GenericParseable.cs | 12 +++++++++++ Funcky/PublicAPI.Unshipped.txt | 3 +++ 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs create mode 100644 Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs diff --git a/FrameworkFeatureConstants.props b/FrameworkFeatureConstants.props index e6a8d50e..cd17d346 100644 --- a/FrameworkFeatureConstants.props +++ b/FrameworkFeatureConstants.props @@ -12,6 +12,9 @@ $(DefineConstants);STACK_TRACE_HIDDEN_SUPPORTED;DATE_ONLY_SUPPORTED;TIME_ONLY_SUPPORTED;PRIORITY_QUEUE;TRY_GET_NON_ENUMERATED_COUNT;HTTP_HEADERS_NON_VALIDATED;ELEMENT_AT_INDEX + + $(DefineConstants);GENERIC_MATH;GENERIC_PARSEABLE + $(DefineConstants);RANDOM_SHUFFLE diff --git a/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Version.cs b/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Version.cs index 7f8fb5e5..67e38f40 100644 --- a/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Version.cs +++ b/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Version.cs @@ -15,7 +15,7 @@ public void ParseVersionIsTheSameAsTryParseForValidVersions(string input) FunctionalAssert.Some(expected!, input.ParseVersionOrNone()); } - #if PARSE_READ_ONLY_SPAN_SUPPORTED +#if PARSE_READ_ONLY_SPAN_SUPPORTED [Theory] [InlineData("1.0")] [InlineData("1.0.0")] @@ -26,7 +26,7 @@ public void ParseVersionIsTheSameAsTryParseForValidVersionsWithReadOnlySpan(stri Assert.True(Version.TryParse(inputSpan, out var expected)); FunctionalAssert.Some(expected!, inputSpan.ParseVersionOrNone()); } - #endif +#endif [Property] public Property ParseVersionIsTheSameAsTryParse(string input) @@ -38,7 +38,7 @@ public Property ParseVersionIsTheSameAsTryParse(string input) return result.ToProperty(); } - #if PARSE_READ_ONLY_SPAN_SUPPORTED +#if PARSE_READ_ONLY_SPAN_SUPPORTED [Property] public Property ParseVersionIsTheSameAsTryParseWithReadOnlySpan(string input) { @@ -49,5 +49,5 @@ public Property ParseVersionIsTheSameAsTryParseWithReadOnlySpan(string input) : parsed.Match(none: true, some: False); return result.ToProperty(); } - #endif +#endif } diff --git a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs new file mode 100644 index 00000000..56b8df9a --- /dev/null +++ b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs @@ -0,0 +1,20 @@ +#if GENERIC_MATH +using System.Globalization; +using System.Numerics; + +namespace Funcky.Extensions; +public static partial class ParseExtensions +{ + public static Option ParseGenericNumber(this string value, NumberStyles style, IFormatProvider? provider) + where TNumber : INumberBase + => TNumber.TryParse(value, style, provider, out var result) + ? result + : Option.None; + + public static Option ParseGenericNumber(this ReadOnlySpan value, NumberStyles style, IFormatProvider? provider) + where TNumber : INumberBase + => TNumber.TryParse(value, style, provider, out var result) + ? result + : Option.None; +} +#endif diff --git a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs new file mode 100644 index 00000000..fd703795 --- /dev/null +++ b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs @@ -0,0 +1,12 @@ +#if GENERIC_PARSEABLE +namespace Funcky.Extensions; + +public static partial class ParseExtensions +{ + public static Option ParseGenericParsable(this ReadOnlySpan value, IFormatProvider? provider) + where TParseable : ISpanParsable + => TParseable.TryParse(value, provider, out var result) + ? result + : Option.None; +} +#endif diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index b6f13844..80280bac 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -10,3 +10,6 @@ Funcky.Monads.Result.InspectError(System.Action Funcky.Monads.Result.OrElse(Funcky.Monads.Result fallback) -> Funcky.Monads.Result Funcky.Monads.Result.OrElse(System.Func>! fallback) -> Funcky.Monads.Result static Funcky.Extensions.EnumeratorExtensions.MoveNextOrNone(this System.Collections.Generic.IEnumerator! enumerator) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseGenericNumber(this string! value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseGenericNumber(this System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseGenericParsable(this System.ReadOnlySpan value, System.IFormatProvider? provider) -> Funcky.Monads.Option From c4d790f687551bfda36eb15615b06ba23ee39656 Mon Sep 17 00:00:00 2001 From: Thomas Bruderer Date: Mon, 4 Sep 2023 11:32:02 +0200 Subject: [PATCH 2/3] adressing review: * improve naming * implement ParseOrNone for IParseable --- .../ParseExtensions/ParseExtensions.GenericNumber.cs | 4 ++-- .../ParseExtensions/ParseExtensions.GenericParseable.cs | 8 +++++++- Funcky/PublicAPI.Unshipped.txt | 7 ++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs index 56b8df9a..8acc0e71 100644 --- a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs +++ b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericNumber.cs @@ -5,13 +5,13 @@ namespace Funcky.Extensions; public static partial class ParseExtensions { - public static Option ParseGenericNumber(this string value, NumberStyles style, IFormatProvider? provider) + public static Option ParseNumberOrNone(this string value, NumberStyles style, IFormatProvider? provider) where TNumber : INumberBase => TNumber.TryParse(value, style, provider, out var result) ? result : Option.None; - public static Option ParseGenericNumber(this ReadOnlySpan value, NumberStyles style, IFormatProvider? provider) + public static Option ParseNumberOrNone(this ReadOnlySpan value, NumberStyles style, IFormatProvider? provider) where TNumber : INumberBase => TNumber.TryParse(value, style, provider, out var result) ? result diff --git a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs index fd703795..d8cb3572 100644 --- a/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs +++ b/Funcky/Extensions/ParseExtensions/ParseExtensions.GenericParseable.cs @@ -3,10 +3,16 @@ namespace Funcky.Extensions; public static partial class ParseExtensions { - public static Option ParseGenericParsable(this ReadOnlySpan value, IFormatProvider? provider) + public static Option ParseOrNone(this ReadOnlySpan value, IFormatProvider? provider) where TParseable : ISpanParsable => TParseable.TryParse(value, provider, out var result) ? result : Option.None; + + public static Option ParseOrNone(this string? value, IFormatProvider? provider) + where TParseable : IParsable + => TParseable.TryParse(value, provider, out var result) + ? result + : Option.None; } #endif diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 80280bac..00f48e94 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -10,6 +10,7 @@ Funcky.Monads.Result.InspectError(System.Action Funcky.Monads.Result.OrElse(Funcky.Monads.Result fallback) -> Funcky.Monads.Result Funcky.Monads.Result.OrElse(System.Func>! fallback) -> Funcky.Monads.Result static Funcky.Extensions.EnumeratorExtensions.MoveNextOrNone(this System.Collections.Generic.IEnumerator! enumerator) -> Funcky.Monads.Option -static Funcky.Extensions.ParseExtensions.ParseGenericNumber(this string! value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option -static Funcky.Extensions.ParseExtensions.ParseGenericNumber(this System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option -static Funcky.Extensions.ParseExtensions.ParseGenericParsable(this System.ReadOnlySpan value, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this string! value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseOrNone(this string? value, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseOrNone(this System.ReadOnlySpan value, System.IFormatProvider? provider) -> Funcky.Monads.Option From 165175884e6cc216096adcef0c04a804811f2592 Mon Sep 17 00:00:00 2001 From: Thomas Bruderer Date: Mon, 4 Sep 2023 11:51:38 +0200 Subject: [PATCH 3/3] Add some minimal tests --- .../ParseExtensionsTest.Generic.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Generic.cs diff --git a/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Generic.cs b/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Generic.cs new file mode 100644 index 00000000..d6277843 --- /dev/null +++ b/Funcky.Test/Extensions/ParseExtensions/ParseExtensionsTest.Generic.cs @@ -0,0 +1,36 @@ +#if GENERIC_PARSEABLE +using System.Globalization; + +namespace Funcky.Test.Extensions.ParseExtensions; + +public sealed partial class ParseExtensionsTest +{ + [Theory] + [MemberData(nameof(ParseableDoubleNumbers))] + public void ParseGenericStringReturnsTheExpectedDouble(Option expected, string input) + { + Assert.Equal(expected, input.ParseNumberOrNone(NumberStyles.Number, null)); + Assert.Equal(expected, input.ParseOrNone(null)); + } + + [Theory] + [MemberData(nameof(ParseableDoubleNumbers))] + public void ParseGenericSpanReturnsTheExpectedDouble(Option expected, string input) + { + Assert.Equal(expected, input.AsSpan().ParseNumberOrNone(NumberStyles.Number, null)); + Assert.Equal(expected, input.AsSpan().ParseOrNone(null)); + } + + public static TheoryData, string> ParseableDoubleNumbers() + => new() + { + { Option.Some(1.0), "1.0" }, + { Option.Some(3.145), "3.145" }, + { Option.Some(0.5), ".5" }, + { Option.Some(1.0), "1.0" }, + { Option.Some(42.0), "42" }, + { Option.None, string.Empty }, + { Option.None, "no-number" }, + }; +} +#endif