Skip to content

Commit 4a07ea1

Browse files
authored
Merge pull request #46 from jpdillingham/develop
Fix implicit Type identification with async calling methods
2 parents b20bb54 + 45a30cf commit 4a07ea1

File tree

2 files changed

+85
-19
lines changed

2 files changed

+85
-19
lines changed

src/Utility.CommandLine.Arguments/Arguments.cs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ internal static void ExclusiveAdd(this Dictionary<string, object> dictionary, st
7777
}
7878
}
7979
}
80+
81+
/// <summary>
82+
/// Gets the DeclaringType of the first method on the stack whose name matches the specified <paramref name="caller"/>.
83+
/// </summary>
84+
/// <param name="caller">The name of the calling method for which the DeclaringType is to be fetched.</param>
85+
/// <returns>The DeclaringType of the first method on the stack whose name matches the specified <paramref name="caller"/>.</returns>
86+
internal static Type GetCallingType(string caller)
87+
{
88+
var callingMethod = new StackTrace().GetFrames()
89+
.Select(f => f.GetMethod())
90+
.Where(m => m.Name == caller)
91+
.FirstOrDefault();
92+
93+
if (callingMethod == default(MethodBase))
94+
{
95+
throw new InvalidOperationException($"Unable to determine the containing type of the calling method '{caller}'. Explicitly specify the originating Type.");
96+
}
97+
98+
return callingMethod.DeclaringType;
99+
}
80100
}
81101

82102
/// <summary>
@@ -198,11 +218,12 @@ public object this[string index]
198218
/// marked with the <see cref="ArgumentAttribute"/><see cref="Attribute"/> along with the short and long names and help text.
199219
/// </summary>
200220
/// <param name="type">The <see cref="Type"/> for which the matching properties are to be retrieived.</param>
221+
/// <param name="caller">Internal parameter used to identify the calling method.</param>
201222
/// <returns>The retrieved collection of <see cref="ArgumentHelp"/>.</returns>
202-
[Obsolete]
203-
public static IEnumerable<ArgumentHelp> GetArgumentHelp(Type type = null)
223+
[Obsolete("Use GetArgumentInfo()")]
224+
public static IEnumerable<ArgumentHelp> GetArgumentHelp(Type type = null, [CallerMemberName] string caller = default(string))
204225
{
205-
type = type ?? new StackFrame(1).GetMethod().DeclaringType;
226+
type = type ?? ArgumentsExtensions.GetCallingType(caller);
206227

207228
return GetArgumentInfo(type).Select(i => new ArgumentHelp()
208229
{
@@ -217,10 +238,11 @@ public static IEnumerable<ArgumentHelp> GetArgumentHelp(Type type = null)
217238
/// marked with the <see cref="ArgumentAttribute"/><see cref="Attribute"/> along with the short and long names and help text.
218239
/// </summary>
219240
/// <param name="type">The <see cref="Type"/> for which the matching properties are to be retrieived.</param>
241+
/// <param name="caller">Internal parameter used to identify the calling method.</param>
220242
/// <returns>The retrieved collection of <see cref="ArgumentInfo"/>.</returns>
221-
public static IEnumerable<ArgumentInfo> GetArgumentInfo(Type type = null)
243+
public static IEnumerable<ArgumentInfo> GetArgumentInfo(Type type = null, [CallerMemberName] string caller = default(string))
222244
{
223-
type = type ?? new StackFrame(1).GetMethod().DeclaringType;
245+
type = type ?? ArgumentsExtensions.GetCallingType(caller);
224246
var retVal = new List<ArgumentInfo>();
225247

226248
foreach (PropertyInfo property in GetArgumentProperties(type).Values.Distinct())
@@ -247,13 +269,14 @@ public static IEnumerable<ArgumentInfo> GetArgumentInfo(Type type = null)
247269
/// </summary>
248270
/// <param name="commandLineString">The command line arguments with which the application was started.</param>
249271
/// <param name="type">The <see cref="Type"/> for which the command line string is to be parsed.</param>
272+
/// <param name="caller">Internal parameter used to identify the calling method.</param>
250273
/// <returns>
251274
/// The dictionary containing the arguments and values specified in the command line arguments with which the
252275
/// application was started.
253276
/// </returns>
254-
public static Arguments Parse(string commandLineString = default(string), Type type = null)
277+
public static Arguments Parse(string commandLineString = default(string), Type type = null, [CallerMemberName] string caller = default(string))
255278
{
256-
type = type ?? new StackFrame(1).GetMethod().DeclaringType;
279+
type = type ?? ArgumentsExtensions.GetCallingType(caller);
257280

258281
commandLineString = commandLineString == default(string) || commandLineString == string.Empty ? Environment.CommandLine : commandLineString;
259282

@@ -300,16 +323,7 @@ public static IEnumerable<ArgumentInfo> GetArgumentInfo(Type type = null)
300323
/// <param name="caller">Internal parameter used to identify the calling method.</param>
301324
public static void Populate(string commandLineString = default(string), bool clearExistingValues = true, [CallerMemberName] string caller = default(string))
302325
{
303-
var callingMethod = new StackTrace().GetFrames()
304-
.Select(f => f.GetMethod())
305-
.Where(m => m.Name == caller).FirstOrDefault();
306-
307-
if (callingMethod == default(MethodBase))
308-
{
309-
throw new InvalidOperationException("Error populating arguments; Unable to determine the containing type of Main(). Use Populate(typeof(<class containing main>))");
310-
}
311-
312-
Populate(callingMethod.DeclaringType, Parse(commandLineString), clearExistingValues);
326+
Populate(ArgumentsExtensions.GetCallingType(caller), Parse(commandLineString), clearExistingValues);
313327
}
314328

315329
/// <summary>
@@ -488,7 +502,6 @@ private static object ChangeType(object value, string argument, Type toType)
488502
}
489503
catch (Exception ex)
490504
{
491-
// if the cast fails, throw an exception
492505
string message = $"Specified value '{value}' for argument '{argument}' (expected type: {toType}). ";
493506
message += "See inner exception for details.";
494507

tests/Utility.CommandLine.Arguments.Tests/ArgumentsTests.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,23 @@ public void GetArgumentInfo()
115115

116116
Assert.Equal(7, help.Count);
117117
Assert.Single(help.Where(h => h.ShortName == 'b'));
118+
Assert.Single(help.Where(h => h.LongName == "test-prop"));
119+
Assert.Equal("help", help.Where(h => h.ShortName == 'b').FirstOrDefault().HelpText);
120+
}
121+
122+
/// <summary>
123+
/// Tests <see cref="CommandLine.Arguments.GetArgumentHelp"/>.
124+
/// </summary>
125+
[Fact]
126+
public void GetArgumentHelp()
127+
{
128+
#pragma warning disable CS0618 // Type or member is obsolete
129+
var help = CommandLine.Arguments.GetArgumentHelp(typeof(Arguments)).ToList();
130+
#pragma warning restore CS0618 // Type or member is obsolete
131+
132+
Assert.Equal(7, help.Count);
133+
Assert.Single(help.Where(h => h.ShortName == 'b'));
134+
Assert.Single(help.Where(h => h.LongName == "test-prop"));
118135
Assert.Equal("help", help.Where(h => h.ShortName == 'b').FirstOrDefault().HelpText);
119136
}
120137

@@ -128,6 +145,18 @@ public void GetArgumentInfoNull()
128145
Assert.Equal("help", help.Where(h => h.ShortName == 'b').FirstOrDefault().HelpText);
129146
}
130147

148+
[Fact]
149+
public void GetArgumentHelpNull()
150+
{
151+
#pragma warning disable CS0618 // Type or member is obsolete
152+
var help = CommandLine.Arguments.GetArgumentHelp().ToList();
153+
#pragma warning restore CS0618 // Type or member is obsolete
154+
155+
Assert.Equal(7, help.Count);
156+
Assert.Single(help.Where(h => h.ShortName == 'b'));
157+
Assert.Equal("help", help.Where(h => h.ShortName == 'b').FirstOrDefault().HelpText);
158+
}
159+
131160
[Fact]
132161
public void Indexer()
133162
{
@@ -621,7 +650,7 @@ public class TestClassWithListProperty
621650
private static List<string> List { get; set; }
622651

623652
[Fact]
624-
public void Populate()
653+
public void PopulateShort()
625654
{
626655
Exception ex = Record.Exception(() => CommandLine.Arguments.Populate(GetType(), "-l one -l two -l three"));
627656

@@ -632,6 +661,30 @@ public void Populate()
632661
Assert.Equal("three", List[2]);
633662
}
634663

664+
[Fact]
665+
public void PopulateLong()
666+
{
667+
Exception ex = Record.Exception(() => CommandLine.Arguments.Populate(GetType(), "--list one --list two --list three"));
668+
669+
Assert.Null(ex);
670+
Assert.Equal(3, List.Count);
671+
Assert.Equal("one", List[0]);
672+
Assert.Equal("two", List[1]);
673+
Assert.Equal("three", List[2]);
674+
}
675+
676+
//[Fact]
677+
//public void PopulateLongAndShort()
678+
//{
679+
// Exception ex = Record.Exception(() => CommandLine.Arguments.Populate(GetType(), "-l one --list two -l three"));
680+
681+
// Assert.Null(ex);
682+
// Assert.Equal(3, List.Count);
683+
// Assert.Equal("one", List[0]);
684+
// Assert.Equal("two", List[1]);
685+
// Assert.Equal("three", List[2]);
686+
//}
687+
635688
[Fact]
636689
public void PopulateSingle()
637690
{

0 commit comments

Comments
 (0)