diff --git a/README.md b/README.md index e20d9b90..1f031da2 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,43 @@ You can utilize the parser library in several ways: 1. Create a class to define valid options, and to receive the parsed options. 2. Call ParseArguments with the args string array. +C# Quick Start: + +```csharp +using System; +using CommandLine; + +namespace QuickStart +{ + class Program + { + public class Options + { + [Option('v', "verbose", Required = false, HelpText = "Set output to verbose messages.")] + public bool Verbose { get; set; } + } + + static void Main(string[] args) + { + Parser.Default.ParseArguments(args) + .WithParsed(o => + { + if (o.Verbose) + { + Console.WriteLine($"Verbose output enabled. Current Arguments: -v {o.Verbose}"); + Console.WriteLine("Quick Start Example! App is in Verbose mode!"); + } + else + { + Console.WriteLine($"Current Arguments: -v {o.Verbose}"); + Console.WriteLine("Quick Start Example!"); + } + }); + } + } +} +``` + C# Examples: ```csharp @@ -55,10 +92,14 @@ class Options public IEnumerable InputFiles { get; set; } // Omitting long name, defaults to name of property, ie "--verbose" - [Option(Default = false, HelpText = "Prints all messages to standard output.")] + [Option( + Default = false, + HelpText = "Prints all messages to standard output.")] public bool Verbose { get; set; } - - [Option("stdin", Default = false, HelpText = "Read from stdin")] + + [Option("stdin", + Default = false + HelpText = "Read from stdin")] public bool stdin { get; set; } [Value(0, MetaName = "offset", HelpText = "File offset.")] @@ -79,7 +120,7 @@ F# Examples: type options = { [] files : seq; [] verbose : bool; - [] language : string; + [] language : string; [] offset : int64 option; } @@ -94,18 +135,22 @@ VB.Net: ```VB.NET Class Options - - Public Property InputFiles As IEnumerable(Of String) - - ' Omitting long name, defaults to name of property, ie "--verbose" - - Public Property Verbose As Boolean - - - Public Property Language As String - - - Public Property Offset As Long? + + Public Property InputFiles As IEnumerable(Of String) + + ' Omitting long name, defaults to name of property, ie "--verbose" + + Public Property Verbose As Boolean + + + Public Property Language As String + + + Public Property Offset As Long? End Class Sub Main(ByVal args As String()) diff --git a/src/CommandLine/Core/TypeConverter.cs b/src/CommandLine/Core/TypeConverter.cs index ad2b7d81..2feb54ca 100644 --- a/src/CommandLine/Core/TypeConverter.cs +++ b/src/CommandLine/Core/TypeConverter.cs @@ -49,6 +49,19 @@ private static Maybe ChangeTypeScalar(string value, Type conversionType, return result.ToMaybe(); } + private static object ConvertString(string value, Type type, CultureInfo conversionCulture) + { + try + { + return Convert.ChangeType(value, type, conversionCulture); + } + catch (InvalidCastException) + { + // Required for converting from string to TimeSpan because Convert.ChangeType can't + return System.ComponentModel.TypeDescriptor.GetConverter(type).ConvertFrom(null, conversionCulture, value); + } + } + private static Result ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase) { Func changeType = () => @@ -71,10 +84,9 @@ private static Result ChangeTypeScalarImpl(string value, Type () => #if !SKIP_FSHARP isFsOption - ? FSharpOptionHelper.Some(type, Convert.ChangeType(value, type, conversionCulture)) : + ? FSharpOptionHelper.Some(type, ConvertString(value, type, conversionCulture)) : #endif - Convert.ChangeType(value, type, conversionCulture); - + ConvertString(value, type, conversionCulture); #if !SKIP_FSHARP Func empty = () => isFsOption ? FSharpOptionHelper.None(type) : null; #else diff --git a/src/CommandLine/UnParserExtensions.cs b/src/CommandLine/UnParserExtensions.cs index 20122a7b..7db948e7 100644 --- a/src/CommandLine/UnParserExtensions.cs +++ b/src/CommandLine/UnParserExtensions.cs @@ -18,6 +18,7 @@ public class UnParserSettings private bool preferShortName; private bool groupSwitches; private bool useEqualToken; + private bool showHidden; /// /// Gets or sets a value indicating whether unparsing process shall prefer short or long names. @@ -46,6 +47,14 @@ public bool UseEqualToken set { PopsicleSetter.Set(Consumed, ref useEqualToken, value); } } + /// + /// Gets or sets a value indicating whether unparsing process shall expose hidden options. + /// + public bool ShowHidden + { + get { return showHidden; } + set { PopsicleSetter.Set(Consumed, ref showHidden, value); } + } /// /// Factory method that creates an instance of with GroupSwitches set to true. /// @@ -119,6 +128,7 @@ public static string FormatCommandLine(this Parser parser, T options, Action< var allOptSpecs = from info in specs.Where(i => i.Specification.Tag == SpecificationType.Option) let o = (OptionSpecification)info.Specification where o.TargetType != TargetType.Switch || (o.TargetType == TargetType.Switch && ((bool)info.Value)) + where !o.Hidden || settings.ShowHidden orderby o.UniqueName() select info; @@ -206,9 +216,10 @@ private static string FormatOption(OptionSpecification spec, object value, UnPar private static string FormatName(this OptionSpecification optionSpec, UnParserSettings settings) { - var longName = - optionSpec.LongName.Length > 0 - && !settings.PreferShortName; + // Have a long name and short name not preferred? Go with long! + // No short name? Has to be long! + var longName = (optionSpec.LongName.Length > 0 && !settings.PreferShortName) + || optionSpec.ShortName.Length == 0; return new StringBuilder(longName diff --git a/tests/CommandLine.Tests/CommandLine.Tests.csproj b/tests/CommandLine.Tests/CommandLine.Tests.csproj index 5914a3a9..9ecf4fcf 100644 --- a/tests/CommandLine.Tests/CommandLine.Tests.csproj +++ b/tests/CommandLine.Tests/CommandLine.Tests.csproj @@ -58,6 +58,7 @@ Properties\SharedAssemblyInfo.cs + @@ -95,6 +96,7 @@ + diff --git a/tests/CommandLine.Tests/Fakes/Hidden_Option.cs b/tests/CommandLine.Tests/Fakes/Hidden_Option.cs new file mode 100644 index 00000000..1f18f216 --- /dev/null +++ b/tests/CommandLine.Tests/Fakes/Hidden_Option.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CommandLine.Tests.Fakes +{ + public class Hidden_Option + { + [Option('h', "hiddenOption", Default="hidden", Hidden = true)] + public string HiddenOption { get; set; } + } +} diff --git a/tests/CommandLine.Tests/Fakes/Options_With_TimeSpan.cs b/tests/CommandLine.Tests/Fakes/Options_With_TimeSpan.cs new file mode 100644 index 00000000..0e88896d --- /dev/null +++ b/tests/CommandLine.Tests/Fakes/Options_With_TimeSpan.cs @@ -0,0 +1,12 @@ +// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information. + +using System; + +namespace CommandLine.Tests.Fakes +{ + public class Options_With_TimeSpan + { + [Option('d', "duration")] + public TimeSpan Duration { get; set; } + } +} diff --git a/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs b/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs index 7d7544e6..7a49b39b 100644 --- a/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs +++ b/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs @@ -1023,6 +1023,22 @@ public void Parse_Guid(string[] arguments, Options_With_Guid expected) // Teardown } + [Fact] + public void Parse_TimeSpan() + { + // Fixture setup + var expectedResult = new Options_With_TimeSpan { Duration = TimeSpan.FromMinutes(42) }; + + // Exercize system + var result = InvokeBuild( + new[] { "--duration=00:42:00" }); + + // Verify outcome + expectedResult.ShouldBeEquivalentTo(((Parsed)result).Value); + + // Teardown + } + public static IEnumerable RequiredValueStringData { get diff --git a/tests/CommandLine.Tests/Unit/UnParserExtensionsTests.cs b/tests/CommandLine.Tests/Unit/UnParserExtensionsTests.cs index 3d15e2d0..74851163 100644 --- a/tests/CommandLine.Tests/Unit/UnParserExtensionsTests.cs +++ b/tests/CommandLine.Tests/Unit/UnParserExtensionsTests.cs @@ -40,6 +40,15 @@ public static void UnParsing_immutable_instance_returns_command_line(Immutable_S .ShouldBeEquivalentTo(result); } + [Theory] + [MemberData("UnParseDataHidden")] + public static void Unparsing_hidden_option_returns_command_line(Hidden_Option options, bool showHidden, string result) + { + new Parser() + .FormatCommandLine(options, config => config.ShowHidden = showHidden) + .ShouldBeEquivalentTo(result); + } + #if !SKIP_FSHARP [Theory] [MemberData("UnParseDataFSharpOption")] @@ -141,6 +150,14 @@ public static IEnumerable UnParseDataImmutable } } + public static IEnumerable UnParseDataHidden + { + get + { + yield return new object[] { new Hidden_Option { HiddenOption = "hidden" }, true, "--hiddenOption hidden" }; + yield return new object[] { new Hidden_Option { HiddenOption = "hidden" }, false, ""}; + } + } #if !SKIP_FSHARP public static IEnumerable UnParseDataFSharpOption {