From 4dcee4d8a84a5aec7bbee0e9e112a2432e19a411 Mon Sep 17 00:00:00 2001 From: Rob Janssen Date: Wed, 11 May 2022 00:01:12 +0200 Subject: [PATCH] Add support for DateOnly and TimeOnly on .Net 6 (#1732). (#1734) --- .../Binding/TypeConversionTests.cs | 76 ++++++++++++++----- .../ArgumentConverter.StringConverters.cs | 36 ++++++++- 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs index afdbc3f415..62552003ad 100644 --- a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs +++ b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs @@ -1,14 +1,14 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; using System.Collections; using System.Collections.Generic; using System.CommandLine.Utility; using System.IO; -using FluentAssertions; using System.Linq; -using Xunit; using System.Net; +using Xunit; namespace System.CommandLine.Tests.Binding { @@ -230,7 +230,7 @@ public void Nullable_bool_parses_as_false_when_the_option_has_been_applied(strin public void Nullable_bool_parses_as_null_when_the_option_has_not_been_applied() { var option = new Option("-x"); - + option .Parse("") .GetValueForOption(option) @@ -252,7 +252,7 @@ public void Generic_option_bool_parses_when_passed_to_non_generic_GetValueForOpt parseResult.GetValueForOption((Option)option).Should().Be(true); } - + [Fact] public void When_exactly_one_argument_is_expected_and_none_are_provided_then_getting_value_throws() { @@ -274,7 +274,7 @@ public void When_exactly_one_argument_is_expected_and_none_are_provided_then_get .Should() .Be("Required argument missing for option: '-x'."); } - + [Theory] [InlineData("c -a o c c")] [InlineData("c c -a o c")] @@ -496,7 +496,7 @@ public void Values_can_be_correctly_converted_to_decimal_without_the_parser_spec value.Should().Be(123.456m); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_decimal_without_the_parser_specifying_a_custom_converter() { @@ -506,7 +506,7 @@ public void Values_can_be_correctly_converted_to_nullable_decimal_without_the_pa value.Should().Be(123.456m); } - + [Fact] public void Values_can_be_correctly_converted_to_double_without_the_parser_specifying_a_custom_converter() { @@ -516,7 +516,7 @@ public void Values_can_be_correctly_converted_to_double_without_the_parser_speci value.Should().Be(123.456d); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_double_without_the_parser_specifying_a_custom_converter() { @@ -536,7 +536,7 @@ public void Values_can_be_correctly_converted_to_float_without_the_parser_specif value.Should().Be(123.456f); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_float_without_the_parser_specifying_a_custom_converter() { @@ -557,7 +557,7 @@ public void Values_can_be_correctly_converted_to_Guid_without_the_parser_specify value.Should().Be(Guid.Parse(guidString)); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_Guid_without_the_parser_specifying_a_custom_converter() { @@ -689,7 +689,7 @@ public void Values_can_be_correctly_converted_to_nullable_ushort_without_the_par value.Should().Be(1234); } - + [Fact] public void Values_can_be_correctly_converted_to_sbyte_without_the_parser_specifying_a_custom_converter() { @@ -699,7 +699,7 @@ public void Values_can_be_correctly_converted_to_sbyte_without_the_parser_specif value.Should().Be(123); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_sbyte_without_the_parser_specifying_a_custom_converter() { @@ -732,6 +732,48 @@ public void Values_can_be_correctly_converted_to_ipendpoint_without_the_parser_s } #endif +#if NET6_0_OR_GREATER + [Fact] + public void Values_can_be_correctly_converted_to_dateonly_without_the_parser_specifying_a_custom_converter() + { + var option = new Option("-us"); + + var value = option.Parse("-us 2022-03-02").GetValueForOption(option); + + value.Should().Be(DateOnly.Parse("2022-03-02")); + } + + [Fact] + public void Values_can_be_correctly_converted_to_nullable_dateonly_without_the_parser_specifying_a_custom_converter() + { + var option = new Option("-x"); + + var value = option.Parse("-x 2022-03-02").GetValueForOption(option); + + value.Should().Be(DateOnly.Parse("2022-03-02")); + } + + [Fact] + public void Values_can_be_correctly_converted_to_timeonly_without_the_parser_specifying_a_custom_converter() + { + var option = new Option("-us"); + + var value = option.Parse("-us 12:34:56").GetValueForOption(option); + + value.Should().Be(TimeOnly.Parse("12:34:56")); + } + + [Fact] + public void Values_can_be_correctly_converted_to_nullable_timeonly_without_the_parser_specifying_a_custom_converter() + { + var option = new Option("-x"); + + var value = option.Parse("-x 12:34:56").GetValueForOption(option); + + value.Should().Be(TimeOnly.Parse("12:34:56")); + } +#endif + [Fact] public void Values_can_be_correctly_converted_to_byte_without_the_parser_specifying_a_custom_converter() { @@ -741,7 +783,7 @@ public void Values_can_be_correctly_converted_to_byte_without_the_parser_specify value.Should().Be(123); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_byte_without_the_parser_specifying_a_custom_converter() { @@ -751,7 +793,7 @@ public void Values_can_be_correctly_converted_to_nullable_byte_without_the_parse value.Should().Be(123); } - + [Fact] public void Values_can_be_correctly_converted_to_uint_without_the_parser_specifying_a_custom_converter() { @@ -761,7 +803,7 @@ public void Values_can_be_correctly_converted_to_uint_without_the_parser_specify value.Should().Be(1234); } - + [Fact] public void Values_can_be_correctly_converted_to_nullable_uint_without_the_parser_specifying_a_custom_converter() { @@ -781,7 +823,7 @@ public void Values_can_be_correctly_converted_to_array_of_int_without_the_parser value.Should().BeEquivalentTo(1, 2, 3); } - + [Theory] [InlineData(0, 100_000, typeof(string[]))] [InlineData(0, 3, typeof(string[]))] @@ -793,7 +835,7 @@ public void Values_can_be_correctly_converted_to_array_of_int_without_the_parser [InlineData(0, 3, typeof(IList))] [InlineData(0, 100_000, typeof(ICollection))] [InlineData(0, 3, typeof(ICollection))] - + [InlineData(1, 100_000, typeof(string[]))] [InlineData(1, 3, typeof(string[]))] [InlineData(1, 100_000, typeof(IEnumerable))] diff --git a/src/System.CommandLine/Binding/ArgumentConverter.StringConverters.cs b/src/System.CommandLine/Binding/ArgumentConverter.StringConverters.cs index 82cb8d90eb..0846fb7a3c 100644 --- a/src/System.CommandLine/Binding/ArgumentConverter.StringConverters.cs +++ b/src/System.CommandLine/Binding/ArgumentConverter.StringConverters.cs @@ -25,6 +25,20 @@ internal static partial class ArgumentConverter return false; }, +#if NET6_0_OR_GREATER + [typeof(DateOnly)] = (string input, out object? value) => + { + if (DateOnly.TryParse(input, out var parsed)) + { + value = parsed; + return true; + } + + value = default; + return false; + }, +#endif + [typeof(DateTime)] = (string input, out object? value) => { if (DateTime.TryParse(input, out var parsed)) @@ -63,7 +77,7 @@ internal static partial class ArgumentConverter [typeof(DirectoryInfo)] = (string path, out object? value) => { - if (String.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(path)) { value = default; return false; @@ -86,7 +100,7 @@ internal static partial class ArgumentConverter [typeof(FileInfo)] = (string path, out object? value) => { - if (String.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(path)) { value = default; return false; @@ -97,7 +111,7 @@ internal static partial class ArgumentConverter [typeof(FileSystemInfo)] = (string path, out object? value) => { - if (String.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(path)) { value = default; return false; @@ -130,7 +144,7 @@ internal static partial class ArgumentConverter value = default; return false; }, - + [typeof(Guid)] = (string input, out object? value) => { if (Guid.TryParse(input, out var parsed)) @@ -205,6 +219,20 @@ internal static partial class ArgumentConverter return false; }, +#if NET6_0_OR_GREATER + [typeof(TimeOnly)] = (string input, out object? value) => + { + if (TimeOnly.TryParse(input, out var parsed)) + { + value = parsed; + return true; + } + + value = default; + return false; + }, +#endif + [typeof(uint)] = (string token, out object? value) => { if (uint.TryParse(token, out var uintValue))