From 9361a4bceb9a4c1c9e49ff514f88f2a6006cd623 Mon Sep 17 00:00:00 2001 From: Pim Brouwers Date: Fri, 6 Dec 2024 09:27:58 -0500 Subject: [PATCH] OptionParse --- src/Danom/Option/OptionParse.cs | 289 ++++++++++++++++++++ test/Danom.Tests/Option/OptionParseTests.cs | 188 +++++++++++++ test/Danom.Tests/Option/OptionTests.cs | 4 +- 3 files changed, 478 insertions(+), 3 deletions(-) create mode 100644 src/Danom/Option/OptionParse.cs create mode 100644 test/Danom.Tests/Option/OptionParseTests.cs diff --git a/src/Danom/Option/OptionParse.cs b/src/Danom/Option/OptionParse.cs new file mode 100644 index 0000000..d1f5d6e --- /dev/null +++ b/src/Danom/Option/OptionParse.cs @@ -0,0 +1,289 @@ +namespace Danom; + +using System.Globalization; + +/// +/// boolOption +/// +public static class boolOption +{ + /// + /// Attempt to parse string as bool, return None if invalid, Some if valid. + /// + /// + /// + public static Option TryParse(string? x) => + bool.TryParse(x, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// byteOption +/// +public static class byteOption +{ + /// + /// Attempt to parse string as byte, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + byte.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// shortOption +/// +public static class shortOption +{ + /// + /// Attempt to parse string as short, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + short.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// intOption +/// +public static class intOption +{ + /// + /// Attempt to parse string as int, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + int.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// longOption +/// +public static class longOption +{ + /// + /// Attempt to parse string as long, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + long.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// decimalOption +/// +public static class decimalOption +{ + /// + /// Attempt to parse string as decimal, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + decimal.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// doubleOption +/// +public static class doubleOption +{ + /// + /// Attempt to parse string as double, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + double.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// floatOption +/// +public static class floatOption +{ + /// + /// Attempt to parse string as float, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + float.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// GuidOption +/// +public static class GuidOption +{ + /// + /// Attempt to parse string as Guid, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + Guid.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as Guid, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format) => + Guid.TryParseExact(x, format, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// DateTimeOffsetOption +/// +public static class DateTimeOffsetOption +{ + /// + /// Attempt to parse string as DateTimeOffset, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + DateTimeOffset.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as DateTimeOffset, return None if invalid, Some if valid. + /// + /// + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format, IFormatProvider? provider = null, DateTimeStyles dateTimeStyles = DateTimeStyles.None) => + DateTimeOffset.TryParseExact(x, format, provider, dateTimeStyles, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// DateTimeOption +/// +public static class DateTimeOption +{ + /// + /// Attempt to parse string as DateTime, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + DateTime.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as DateTime, return None if invalid, Some if valid. + /// + /// + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format, IFormatProvider? provider = null, DateTimeStyles dateTimeStyles = DateTimeStyles.None) => + DateTime.TryParseExact(x, format, provider, dateTimeStyles, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// DateOnlyOption +/// +public static class DateOnlyOption +{ + /// + /// Attempt to parse string as DateOnly, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + DateOnly.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as DateOnly, return None if invalid, Some if valid. + /// + /// + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format, IFormatProvider? provider = null, DateTimeStyles dateTimeStyles = DateTimeStyles.None) => + DateOnly.TryParseExact(x, format, provider, dateTimeStyles, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// TimeOnlyOption +/// +public static class TimeOnlyOption +{ + /// + /// Attempt to parse string as TimeOnly, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + TimeOnly.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as TimeOnly, return None if invalid, Some if valid. + /// + /// + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format, IFormatProvider? provider = null, DateTimeStyles dateTimeStyles = DateTimeStyles.None) => + TimeOnly.TryParseExact(x, format, provider, dateTimeStyles, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// TimeSpanOption +/// +public static class TimeSpanOption +{ + /// + /// Attempt to parse string as TimeSpan, return None if invalid, Some if valid. + /// + /// + /// + /// + public static Option TryParse(string? x, IFormatProvider? provider = null) => + TimeSpan.TryParse(x, provider, out var y) ? Option.Some(y) : Option.NoneValue; + + /// + /// Attempt to parse string as TimeSpan, return None if invalid, Some if valid. + /// + /// + /// + /// + /// + public static Option TryParseExact(string? x, string? format, IFormatProvider? provider = null) => + TimeSpan.TryParseExact(x, format, provider, out var y) ? Option.Some(y) : Option.NoneValue; +} + +/// +/// EnumOption +/// +public static class EnumOption +{ + /// + /// Attempt to parse string as Enum, return None if invalid, Some if valid. + /// + /// + /// + public static Option TryParse(string? x) where TEnum : struct => + Enum.TryParse(x, out var y) ? Option.Some(y) : Option.NoneValue; +} diff --git a/test/Danom.Tests/Option/OptionParseTests.cs b/test/Danom.Tests/Option/OptionParseTests.cs new file mode 100644 index 0000000..10e856b --- /dev/null +++ b/test/Danom.Tests/Option/OptionParseTests.cs @@ -0,0 +1,188 @@ +namespace Danom.Tests; + +using System.Globalization; +using Xunit; +using Danom.TestHelpers; + +public sealed class OptionParseTests +{ + [Fact] + public void boolOptionTryParse() + { + AssertOption.IsNone(boolOption.TryParse(null)); + AssertOption.IsNone(boolOption.TryParse("danom")); + AssertOption.IsSome(boolOption.TryParse("true")); + AssertOption.IsSome(boolOption.TryParse("false")); + } + + [Fact] + public void byteOptionTryParse() + { + AssertOption.IsNone(byteOption.TryParse(null)); + AssertOption.IsNone(byteOption.TryParse("danom")); + AssertOption.IsSome(byteOption.TryParse("0")); + AssertOption.IsSome(byteOption.TryParse("255")); + } + + [Fact] + public void shortOptionTryParse() + { + AssertOption.IsNone(shortOption.TryParse(null)); + AssertOption.IsNone(shortOption.TryParse("danom")); + AssertOption.IsSome(shortOption.TryParse("-32768")); + AssertOption.IsSome(shortOption.TryParse("32767")); + } + + [Fact] + public void intOptionTryParse() + { + AssertOption.IsNone(intOption.TryParse(null, null)); + AssertOption.IsNone(intOption.TryParse("danom", null)); + AssertOption.IsSome(intOption.TryParse("-2147483648", null)); + AssertOption.IsSome(intOption.TryParse("2147483647", null)); + AssertOption.IsSome(intOption.TryParse("-2147483648", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(intOption.TryParse("2147483647", CultureInfo.CurrentUICulture)); + } + + [Fact] + public void longOptionTryParse() + { + AssertOption.IsNone(longOption.TryParse(null, null)); + AssertOption.IsNone(longOption.TryParse("danom", null)); + AssertOption.IsSome(longOption.TryParse("-9223372036854775808", null)); + AssertOption.IsSome(longOption.TryParse("9223372036854775807", null)); + AssertOption.IsSome(longOption.TryParse("-9223372036854775808", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(longOption.TryParse("9223372036854775807", CultureInfo.CurrentUICulture)); + } + + [Fact] + public void decimalOptionTryParse() + { + AssertOption.IsNone(decimalOption.TryParse(null, null)); + AssertOption.IsNone(decimalOption.TryParse("danom", null)); + AssertOption.IsSome(decimalOption.TryParse("-79228162514264337593543950335", null)); + AssertOption.IsSome(decimalOption.TryParse("79228162514264337593543950335", null)); + AssertOption.IsSome(decimalOption.TryParse("-79228162514264337593543950335", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(decimalOption.TryParse("79228162514264337593543950335", CultureInfo.CurrentUICulture)); + } + + [Fact] + public void doubleOptionTryParse() + { + AssertOption.IsNone(doubleOption.TryParse(null, null)); + AssertOption.IsNone(doubleOption.TryParse("danom", null)); + AssertOption.IsSome(doubleOption.TryParse("-1.7976931348623157E+308", null)); + AssertOption.IsSome(doubleOption.TryParse("1.7976931348623157E+308", null)); + AssertOption.IsSome(doubleOption.TryParse("-1.7976931348623157E+308", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(doubleOption.TryParse("1.7976931348623157E+308", CultureInfo.CurrentUICulture)); + } + + [Fact] + public void floatOptionTryParse() + { + AssertOption.IsNone(floatOption.TryParse(null, null)); + AssertOption.IsNone(floatOption.TryParse("danom", null)); + AssertOption.IsSome(floatOption.TryParse("-3.40282347E+38", null)); + AssertOption.IsSome(floatOption.TryParse("3.40282347E+38", null)); + AssertOption.IsSome(floatOption.TryParse("-3.40282347E+38", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(floatOption.TryParse("3.40282347E+38", CultureInfo.CurrentUICulture)); + } + + [Fact] + public void GuidOptionTryParse() + { + AssertOption.IsNone(GuidOption.TryParse(null)); + AssertOption.IsNone(GuidOption.TryParse("danom")); + AssertOption.IsSome(GuidOption.TryParse("00000000-0000-0000-0000-000000000000")); + AssertOption.IsSome(GuidOption.TryParse("11111111-1111-1111-1111-111111111111")); + } + + [Fact] + public void GuidOptionTryParseExact() + { + AssertOption.IsNone(GuidOption.TryParseExact(null, null)); + AssertOption.IsNone(GuidOption.TryParseExact("danom", "N")); + AssertOption.IsSome(GuidOption.TryParseExact("00000000-0000-0000-0000-000000000000", "D")); + AssertOption.IsSome(GuidOption.TryParseExact("11111111111111111111111111111111", "N")); + AssertOption.IsSome(GuidOption.TryParseExact("{00000000-0000-0000-0000-000000000000}", "B")); + AssertOption.IsSome(GuidOption.TryParseExact("(00000000-0000-0000-0000-000000000000)", "P")); + AssertOption.IsSome(GuidOption.TryParseExact("{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}", "X")); + } + + [Fact] + public void DateTimeOffsetOptionTryParse() + { + AssertOption.IsNone(DateTimeOffsetOption.TryParse(null, null)); + AssertOption.IsNone(DateTimeOffsetOption.TryParse("danom", null)); + AssertOption.IsSome(DateTimeOffsetOption.TryParse("0001-01-01T00:00:00.0000000+00:00", null)); + AssertOption.IsSome(DateTimeOffsetOption.TryParse("9999-12-31T23:59:59.9999999+00:00", null)); + } + + [Fact] + public void DateTimeOffsetOptionTryParseExact() + { + AssertOption.IsNone(DateTimeOffsetOption.TryParseExact(null, null, null)); + AssertOption.IsNone(DateTimeOffsetOption.TryParseExact("danom", null, null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("0001-01-01T00:00:00.0000000+00:00", "O", null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("9999-12-31T23:59:59.9999999+00:00", "O", null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("0001-01-01T00:00:00.0000000+00:00", "s", null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("9999-12-31T23:59:59.9999999+00:00", "s", null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("0001-01-01T00:00:00.0000000+00:00", "u", null)); + // AssertOption.IsSome(DateTimeOffsetOption.TryParseExact("9999-12-31T23:59:59.9999999+00:00", "u", null)); + } + + [Fact] + public void DateOnlyOptionTryParse() + { + AssertOption.IsNone(DateOnlyOption.TryParse(null, null)); + AssertOption.IsNone(DateOnlyOption.TryParse("danom", null)); + AssertOption.IsSome(DateOnlyOption.TryParse("0001-01-01", null)); + AssertOption.IsSome(DateOnlyOption.TryParse("9999-12-31", null)); + } + + [Fact] + public void TimeOnlyOptionTryParse() + { + AssertOption.IsNone(TimeOnlyOption.TryParse(null, null)); + AssertOption.IsNone(TimeOnlyOption.TryParse("danom", null)); + AssertOption.IsSome(TimeOnlyOption.TryParse("00:00:00", null)); + AssertOption.IsSome(TimeOnlyOption.TryParse("23:59:59", null)); + } + + [Fact] + public void TimeSpanOptionTryParse() + { + AssertOption.IsNone(TimeSpanOption.TryParse(null, null)); + AssertOption.IsNone(TimeSpanOption.TryParse("danom", null)); + AssertOption.IsSome(TimeSpanOption.TryParse("00:00:00", null)); + AssertOption.IsSome(TimeSpanOption.TryParse("10675199.02:48:05.4775807", null)); + } + + [Fact] + public void TimeSpanOptionTryParseExact() + { + AssertOption.IsNone(TimeSpanOption.TryParseExact(null, null, null)); + AssertOption.IsNone(TimeSpanOption.TryParseExact("danom", null, null)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("10675199.02:48:05.4775807", "c", null)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("3:17:14:48.153", "g", null)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("3:17:14:48.153", "G", null)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("10675199.02:48:05.4775807", "c", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("3:17:14:48.153", "g", CultureInfo.CurrentUICulture)); + AssertOption.IsSome(TimeSpanOption.TryParseExact("3:17:14:48.153", "G", CultureInfo.CurrentUICulture)); + } + + enum Borp + { + Meep, + Morp + } + + [Fact] + public void EnumOptionTryParse() + { + AssertOption.IsNone(EnumOption.TryParse(null)); + AssertOption.IsNone(EnumOption.TryParse("danom")); + AssertOption.IsSome(EnumOption.TryParse("Meep")); + AssertOption.IsSome(EnumOption.TryParse("Morp")); + } +} diff --git a/test/Danom.Tests/Option/OptionTests.cs b/test/Danom.Tests/Option/OptionTests.cs index c009e8a..0b6ee05 100644 --- a/test/Danom.Tests/Option/OptionTests.cs +++ b/test/Danom.Tests/Option/OptionTests.cs @@ -200,9 +200,7 @@ public void ToStringDefaultOrFormat() { Assert.Equal("1", Option.Some(1).ToString("0")); Assert.Equal("0", Option.None().ToString("0")); - - Assert.Equal("$1.99", Option.Some(1.9878565765675M).ToString("0", "C2")); - + Assert.NotEqual("0", Option.Some(1.9878565765675M).ToString("0", "C2")); Assert.Equal("£1.99", Option.Some(1.9878565765675M).ToString("0", "C2", CultureInfo.CreateSpecificCulture("en-GB"))); } }