From ce9cfc4e55f33cf9066bcc3d42f955b1a5f59308 Mon Sep 17 00:00:00 2001 From: Salvage <29021710+Saalvage@users.noreply.github.com> Date: Sun, 19 Jun 2022 17:48:44 +0200 Subject: [PATCH] Fix #830 --- src/CommandLine/Core/ReflectionExtensions.cs | 9 ++- src/CommandLine/Text/HelpText.cs | 6 +- tests/CommandLine.Tests/Unit/Issue830Tests.cs | 65 +++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/CommandLine.Tests/Unit/Issue830Tests.cs diff --git a/src/CommandLine/Core/ReflectionExtensions.cs b/src/CommandLine/Core/ReflectionExtensions.cs index 622e1e6e..d4d073c5 100644 --- a/src/CommandLine/Core/ReflectionExtensions.cs +++ b/src/CommandLine/Core/ReflectionExtensions.cs @@ -15,7 +15,10 @@ static class ReflectionExtensions { public static IEnumerable GetSpecifications(this Type type, Func selector) { - return from pi in type.FlattenHierarchy().SelectMany(x => x.GetTypeInfo().GetProperties()) + return from pi in type.FlattenHierarchy() + .SelectMany(x => x.GetTypeInfo() + .GetProperties(BindingFlags.Instance | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public)) let attrs = pi.GetCustomAttributes(true) where attrs.OfType().Any() || @@ -38,7 +41,9 @@ public static Maybe GetVerbSpecification(this Type type) public static Maybe> GetUsageData(this Type type) { return - (from pi in type.FlattenHierarchy().SelectMany(x => x.GetTypeInfo().GetProperties()) + (from pi in type.FlattenHierarchy().SelectMany(x => x.GetTypeInfo().GetProperties( + BindingFlags.Instance | BindingFlags.Static | + BindingFlags.NonPublic | BindingFlags.Public)) let attrs = pi.GetCustomAttributes(typeof(UsageAttribute), true) where attrs.Any() select Tuple.Create(pi, (UsageAttribute)attrs.First())) diff --git a/src/CommandLine/Text/HelpText.cs b/src/CommandLine/Text/HelpText.cs index f5e9a7b9..13ad97c3 100644 --- a/src/CommandLine/Text/HelpText.cs +++ b/src/CommandLine/Text/HelpText.cs @@ -839,8 +839,10 @@ private static Maybe>> GetUsageFromTy var prop = tuple.Item1; var attr = tuple.Item2; - var examples = (IEnumerable)prop - .GetValue(null, BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty, null, null, null); + var examples = (IEnumerable) prop + .GetValue(null, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | + BindingFlags.GetProperty, null, null, null); return Tuple.Create(attr, examples); }); diff --git a/tests/CommandLine.Tests/Unit/Issue830Tests.cs b/tests/CommandLine.Tests/Unit/Issue830Tests.cs new file mode 100644 index 00000000..7e09333a --- /dev/null +++ b/tests/CommandLine.Tests/Unit/Issue830Tests.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.IO; +using CommandLine.Text; +using FluentAssertions; +using Xunit; + +// Issue #830 +// Allow private properties as options and usage +namespace CommandLine.Tests.Unit +{ + public class Issue830Tests + { + [Fact] + public void Parse_options_with_private_value_and_option() + { + var expectedOptions = new Options + { + Option = "a", + Value = "b" + }; + + var result = Parser.Default.ParseArguments( + new[] { "b", "--opt", "a" }); + + ((Parsed)result).Value.Should().BeEquivalentTo(expectedOptions); + } + + [Fact] + public void Print_private_usage() + { + var help = new StringWriter(); + var sut = new Parser(config => config.HelpWriter = help); + + sut.ParseArguments(new string[] { "--help" }); + var result = help.ToString(); + + var lines = result.ToLines().TrimStringArray(); + lines[3].Should().BeEquivalentTo("Do something very cool:"); + lines[4].Should().BeEquivalentTo("CommandLine --opt test1 test2"); + } + + private class Options + { + public string Option { get => PrivateOption; set => PrivateOption = value; } + + public string Value { get => PrivateValue; set => PrivateValue = value; } + + [Option("opt", Required = true)] + private string PrivateOption { get; set; } + + [Value(0, Required = true)] + private string PrivateValue { get; set; } + + [Usage] + private static IEnumerable PrivateUsage { get; } = new List + { + new Example("Do something very cool", new Options + { + PrivateOption = "test1", + PrivateValue = "test2" + }) + }; + } + } +}