Skip to content

Commit 0756278

Browse files
committed
Add custom argument parsing for verbs
Introduces a `CustomArgumentParsingAttribute` to enable verbs without properties to ignore unknown arguments. This helps commands that manually parse arguments. Updates unit tests to reflect this new functionality.
1 parent 93563ad commit 0756278

5 files changed

Lines changed: 47 additions & 4 deletions

File tree

Neolution.DotNet.Console.UnitTests/DotNetConsoleEnvironmentTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ public async Task GivenEnvironmentNameEnvironmentVariable_WhenRunningConsoleApp_
3232
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", environmentName);
3333

3434
const string commandLineString = "default";
35-
var builder = DotNetConsole.CreateBuilderWithReference(Assembly.GetAssembly(typeof(DefaultCommand))!, commandLineString.Split(" "));
35+
var servicesAssembly = Assembly.GetAssembly(typeof(DefaultCommand))!;
36+
var verbTypes = new[] { typeof(DefaultOptions), typeof(EchoOptions) };
37+
var builder = DotNetConsole.CreateBuilderWithReference(servicesAssembly, verbTypes, commandLineString.Split(" "));
3638

3739
if (builder.Environment.IsDevelopment())
3840
{

Neolution.DotNet.Console.UnitTests/DotNetConsoleGrammarTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ public async Task GivenBuiltConsoleAppWithDefaultVerbWithoutProperties_WhenCalli
188188
/// <returns>A built console app ready to run.</returns>
189189
private static IDotNetConsole CreateConsoleAppWithLogger(string args, IUnitTestLogger tracker)
190190
{
191-
var builder = DotNetConsole.CreateBuilderWithReference(Assembly.GetAssembly(typeof(DefaultCommand))!, args.Split(" "));
191+
var servicesAssembly = Assembly.GetAssembly(typeof(DefaultCommand))!;
192+
var verbTypes = new[] { typeof(DefaultOptions), typeof(EchoOptions) };
193+
var builder = DotNetConsole.CreateBuilderWithReference(servicesAssembly, verbTypes, args.Split(" "));
192194

193195
builder.Services.Replace(new ServiceDescriptor(typeof(IUnitTestLogger), tracker));
194196

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
namespace Neolution.DotNet.Console.UnitTests.Fakes
22
{
33
using CommandLine;
4+
using Neolution.DotNet.Console.Attributes;
45

56
/// <summary>
6-
/// The options stub for the <see cref="DefaultCommandWithoutProperties"/> - simulates customer scenario where options have no properties
7+
/// The options stub for the <see cref="DefaultCommandWithoutProperties"/> - simulates scenario where options have none or are ignoring properties
78
/// </summary>
89
[Verb("default-no-props", isDefault: true)]
10+
[CustomArgumentParsing]
911
public class DefaultOptionsWithoutProperties
1012
{
11-
// No properties defined - customers parse manually in the command
13+
// No properties defined, options may be parsed manually in the command
1214
}
1315
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Neolution.DotNet.Console.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Indicates that this verb options class uses custom argument parsing.
7+
/// When applied, the CommandLineParser will not fail on arguments that are not mapped to Option or Value attributes,
8+
/// allowing your command implementation to manually parse command-line arguments.
9+
/// </summary>
10+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
11+
public sealed class CustomArgumentParsingAttribute : Attribute
12+
{
13+
}
14+
}

Neolution.DotNet.Console/Internal/CommandLineProcessor.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Reflection;
66
using CommandLine;
7+
using Neolution.DotNet.Console.Attributes;
78

89
/// <summary>
910
/// Handles command line argument parsing and validation.
@@ -27,6 +28,18 @@ public static ParserResult<object> ParseArguments(Assembly assembly, Type[]? ver
2728
CheckStrictVerbMatching(args, verbTypes);
2829
}
2930

31+
// Check if any verb type is marked for custom argument parsing
32+
// This supports customers who manually parse arguments in their command
33+
if (HasCustomArgumentParsing(verbTypes))
34+
{
35+
using var parser = new Parser(settings =>
36+
{
37+
settings.IgnoreUnknownArguments = true;
38+
settings.CaseSensitive = false;
39+
});
40+
return parser.ParseArguments(args, verbTypes);
41+
}
42+
3043
return Parser.Default.ParseArguments(args, verbTypes);
3144
}
3245

@@ -86,5 +99,15 @@ private static void CheckStrictVerbMatching(string[] args, Type[] availableVerbT
8699
throw new DotNetConsoleException($"Cannot create builder, because the specified verb '{firstVerb}' matches no command.");
87100
}
88101
}
102+
103+
/// <summary>
104+
/// Determines if any verb type has the CustomArgumentParsing attribute.
105+
/// </summary>
106+
/// <param name="verbTypes">The available verb types.</param>
107+
/// <returns>True if any verb type uses custom argument parsing, false otherwise.</returns>
108+
private static bool HasCustomArgumentParsing(Type[] verbTypes)
109+
{
110+
return verbTypes.Any(t => t.GetCustomAttribute<CustomArgumentParsingAttribute>() != null);
111+
}
89112
}
90113
}

0 commit comments

Comments
 (0)