From 354c5faf443f29589d74545feaafa61975e55aaa Mon Sep 17 00:00:00 2001 From: k3ldar Date: Sat, 15 Aug 2026 15:02:36 +0200 Subject: [PATCH 1/2] Updated readme and convert from default net7 to net9 --- CmdLineTest/CmdLineTest.csproj | 2 +- CmdLineTest/Program.cs | 6 +- Directory.Build.props | 14 ++-- README.md | 100 +++++++++++++++++++------ src/CommandLinePlus.csproj | 2 +- src/Internal/CommandLineArguments.cs | 2 +- src/Internal/ConsoleDisplay.cs | 27 ++++++- src/Internal/ConsoleProcessorFacade.cs | 53 +++++++------ src/Readme.md | 97 ++++++++++++++++++++---- tests/CommandLinePlusTests.csproj | 2 +- tests/ConsoleProcessorFacadeTests.cs | 27 +++---- 11 files changed, 242 insertions(+), 90 deletions(-) diff --git a/CmdLineTest/CmdLineTest.csproj b/CmdLineTest/CmdLineTest.csproj index 32a521d..fb4208c 100644 --- a/CmdLineTest/CmdLineTest.csproj +++ b/CmdLineTest/CmdLineTest.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 enable disable true diff --git a/CmdLineTest/Program.cs b/CmdLineTest/Program.cs index 991b025..962813f 100644 --- a/CmdLineTest/Program.cs +++ b/CmdLineTest/Program.cs @@ -3,10 +3,10 @@ IConsoleProcessorFactory factory = new ConsoleProcessorFactory(); -object[] processors = new object[] -{ +object[] processors = +[ new PluginProcessor(), -}; +]; IConsoleProcessor consoleProcessor = factory.Create("MyProg", processors); diff --git a/Directory.Build.props b/Directory.Build.props index 892b4b2..cb29665 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ - net7.0;netstandard2.0 + net9.0;netstandard2.0 true 1701;1702;8032;8981 true @@ -22,12 +22,12 @@ GCode Sender http://pluginmanager.website/ GPL-3.0-or-later - Supports net7.0 + Supports net9.0 CommandLinePlus - Command line processor for c# - net7.0 + net9.0 @@ -47,15 +47,15 @@ True - - CODE_ANALYSIS;DEBUG;TRACE;NET_CORE;NET_6_0;NET_6_X;ISO_WEEK;ATTR_OS;NET_5_ABOVE;NET_6_ABOVE;NET_7_ABOVE;NET_7_0;NET_7_X;WIN_SYSTEM + + CODE_ANALYSIS;DEBUG;TRACE;NET_CORE;NET_6_0;NET_6_X;ISO_WEEK;ATTR_OS;NET_5_ABOVE;NET_6_ABOVE;NET_7_ABOVE;NET_8_ABOVE;NET_9_ABOVE;NET_9_0;NET_9_X;WIN_SYSTEM true embedded true - - CODE_ANALYSIS;NET_CORE;NET_6_0;NET_6_X;ISO_WEEK;ATTR_OS;NET_5_ABOVE;NET_6_ABOVE;NET_7_ABOVE;NET_7_0;NET_7_X;WIN_SYSTEM + + CODE_ANALYSIS;NET_CORE;NET_6_0;NET_6_X;ISO_WEEK;ATTR_OS;NET_5_ABOVE;NET_6_ABOVE;NET_7_ABOVE;NET_8_ABOVE;NET_9_ABOVE;NET_9_0;NET_9_X;WIN_SYSTEM true embedded true diff --git a/README.md b/README.md index dcfbd33..ead14f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CommandLinePlus +# CommandLinePlus This project started because I needed a command line parser that could: - Have a primary option @@ -11,19 +11,19 @@ myprog.exe Option SubOption -param:value #### Supports -- Net7.0 -- Netstandard2.0 +- .NET 9.0 +- .NET Standard 2.0 #### How it works -Create a class which descends from BaseCommandLine, specify the name (primary option) and create methods (each method is a sub option) +Create a class which descends from `BaseCommandLine`, specify the `Name` (primary option) and create methods (each method is a sub option). -Add CmdLineDescriptions and CmdLineAbbreviation to methods, properties and processor classes for self describing help information. +Add `CmdLineDescription` and `CmdLineAbbreviation` attributes to methods, parameters and processor classes for self-describing help information. -In the following example we can these command lines: +In the following example we can use these command lines: ``` -myprog.exe Plugin /p:myplugin +myprog.exe Plugin myprog.exe Plugin Add -p:myplugin myprog.exe Plugin Remove --p:myplugin myprog.exe Plugin Disable /p:myplugin @@ -31,6 +31,8 @@ myprog.exe Plugin Enable -p:myplugin ``` ``` + using CommandLinePlus; + [CmdLineDescription("Processes plugins for entire application")] internal class PluginProcessor : BaseCommandLine, IDisposable { @@ -106,25 +108,79 @@ myprog.exe Plugin Enable -p:myplugin throw new NotImplementedException(); } } +``` +#### Wiring it up + +Add a reference to the `CommandLinePlus` package, create your processors, then bootstrap them from the application entry point. The demo (`CmdLineTest`) uses C# top-level statements: + +```csharp +using CmdLineTest; + +IConsoleProcessorFactory factory = new ConsoleProcessorFactory(); + +object[] processors = new object[] +{ + new PluginProcessor(), +}; + +IConsoleProcessor consoleProcessor = factory.Create("MyProg", processors); + +switch (consoleProcessor.Run(out int resultCode)) +{ + case RunResult.CandidateFound: + Console.WriteLine("finished"); + break; + + case RunResult.DisplayHelp: + break; + + default: + throw new InvalidOperationException("Didn't work"); +} +return resultCode; ``` -#### Available Paramater seperators -The following characters can be used as param seperators -- = (equals) -- : (colon) - -#### Available Paramater Idetifiers -The following are used to identify parameter values - - (-) (single dash) doesn't need the brackets except in markdown :-\ - - -- (double dash) - - / (forward slash) -## Devops -[![CodeQL](https://github.com/k3ldar/CommandLinePlus/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/k3ldar/CommandLinePlus/actions/workflows/codeql-analysis.yml) [![SonarCloud](https://github.com/k3ldar/CommandLinePlus/actions/workflows/SonarCloud.yml/badge.svg)](https://github.com/k3ldar/CommandLinePlus/actions/workflows/SonarCloud.yml) [![.NET](https://github.com/k3ldar/CommandLinePlus/actions/workflows/dotnet.yml/badge.svg)](https://github.com/k3ldar/CommandLinePlus/actions/workflows/dotnet.yml) +The demo project makes the `CommandLinePlus` types available via a global using in its `.csproj`: + +```xml + +``` + +If you don't use that, add `using CommandLinePlus;` to your file instead. The `MyProg` argument passed to `Create` is the process name shown in help; replace it with your application's name. + +`Run` returns a `RunResult`: -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=vulnerabilities)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=bugs)](https://sonarcloud.io/summary/overalloverall?id=k3ldar_CommandLinePlus) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=code_smells)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=sqale_index)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) +| Value | Meaning | +| --- | --- | +| `None` | No operation performed (default value) | +| `DisplayHelp` | Built-in `-?` help was shown | +| `NotEnoughCandidates` | No processor matched the primary option | +| `TooManyCandidates` | More than one processor matched the primary option | +| `DefaultSubOptionUsed` | Sub-option did not match a method, `Execute` was called instead | +| `InvalidParameters` | A method matched but an argument could not be converted | +| `CandidateFound` | A sub-option method ran successfully | -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=reliability_rating)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=sqale_rating)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=security_rating)](https://sonarcloud.io/summary/overall?id=k3ldar_CommandLinePlus) +#### Built-in options -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=k3ldar_CommandLinePlus) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=k3ldar_CommandLinePlus&metric=coverage)](https://sonarcloud.io/summary/new_code?id=k3ldar_CommandLinePlus) +- `-?` (or `/?`) shows help for all processors, one processor's sub-options, or a sub-option's parameters. +- `-v:` (or `/v:`) sets the verbosity level: `0` Quiet, `1` Normal, `2` Diagnostic, `3` Full. The default is `1` (Normal). +#### Parameters + +Sub-option methods receive values from command-line parameters. Parameter names are matched case-insensitively, sub-option method names are matched case-insensitively, and the primary option name is case-sensitive by default. + +- `bool`, `int`, `enum` and `Guid` values are converted automatically. +- Parameters with a default value are optional. +- A method may return `void` or `int`; an `int` result becomes the process exit code. + +#### Available parameter separators +The following characters can be used as parameter separators +- `=` (equals) +- `:` (colon) + +#### Available parameter identifiers +The following are used to identify parameter values +- `-` (single dash) +- `--` (double dash) +- `/` (forward slash) \ No newline at end of file diff --git a/src/CommandLinePlus.csproj b/src/CommandLinePlus.csproj index 99036a1..cc8b74f 100644 --- a/src/CommandLinePlus.csproj +++ b/src/CommandLinePlus.csproj @@ -15,7 +15,7 @@ True Command line plus readme.md - Supports netstandard2.0 and net7.0 + Supports netstandard2.0 and net9.0 True True latest-all diff --git a/src/Internal/CommandLineArguments.cs b/src/Internal/CommandLineArguments.cs index 5184be6..78f4e98 100644 --- a/src/Internal/CommandLineArguments.cs +++ b/src/Internal/CommandLineArguments.cs @@ -113,7 +113,7 @@ public string[] AllArguments() args.Add($"{item.Key}={item.Value}"); } - return args.ToArray(); + return [.. args]; } #endregion ICommandLine Methods diff --git a/src/Internal/ConsoleDisplay.cs b/src/Internal/ConsoleDisplay.cs index 9b61c28..416dddc 100644 --- a/src/Internal/ConsoleDisplay.cs +++ b/src/Internal/ConsoleDisplay.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; +using System.Text; using static CommandLinePlus.Constants; @@ -8,14 +9,26 @@ namespace CommandLinePlus.Internal { internal sealed class ConsoleDisplay : IDisplay { +#if NET9_0_OR_GREATER + private static readonly CompositeFormat DisplayInnerExceptionFormat = CompositeFormat.Parse(DisplayInnerException); +#endif + public ConsoleDisplay(ICommandLineArguments arguments) { +#if NET9_0_OR_GREATER + ArgumentNullException.ThrowIfNull(arguments); +#else if (arguments == null) throw new ArgumentNullException(nameof(arguments)); +#endif Verbosity = (VerbosityLevel)arguments.Get(Constants.CmdLineSettingVerbosity, 1); +#if NET9_0_OR_GREATER + if (!Enum.IsDefined(Verbosity)) +#else if (!Enum.IsDefined(typeof(VerbosityLevel), Verbosity)) +#endif Verbosity = VerbosityLevel.Normal; } @@ -34,7 +47,11 @@ public void Write(Exception exception) if (exception.InnerException != null) { - InternalWriteLine(String.Format(CultureInfo.InvariantCulture, DisplayInnerException, exception.InnerException.GetType().Name)); + #if NET9_0_OR_GREATER + InternalWriteLine(String.Format(CultureInfo.InvariantCulture, DisplayInnerExceptionFormat, exception.InnerException.GetType().Name)); + #else + InternalWriteLine(String.Format(CultureInfo.InvariantCulture, DisplayInnerException, exception.InnerException.GetType().Name)); + #endif InternalWriteLine(exception.InnerException.Message); LineCount++; } @@ -90,8 +107,12 @@ public void WriteLine(VerbosityLevel verbosityLevel, string message, params obje if (String.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message)); - if (args == null) - throw new ArgumentNullException(nameof(args)); + #if NET9_0_OR_GREATER + ArgumentNullException.ThrowIfNull(args); + #else + if (args == null) + throw new ArgumentNullException(nameof(args)); + #endif string formattedMessage = String.Format(CultureInfo.InvariantCulture, message, args); diff --git a/src/Internal/ConsoleProcessorFacade.cs b/src/Internal/ConsoleProcessorFacade.cs index 9efe255..331178f 100644 --- a/src/Internal/ConsoleProcessorFacade.cs +++ b/src/Internal/ConsoleProcessorFacade.cs @@ -22,7 +22,11 @@ internal sealed class ConsoleProcessorFacade : IConsoleProcessor private const int TooManyCandidates = DefaultSubOptionUsed + 1; private const int NotEnoughCandidates = TooManyCandidates + 1; private const int CmdLineProcessorNotFound = NotEnoughCandidates + 1; - private static readonly List _ignoreMethods = new() { nameof(BaseCommandLine.DisplayHelp), nameof(BaseCommandLine.Execute) }; + private static readonly List _ignoreMethods = [nameof(BaseCommandLine.DisplayHelp), nameof(BaseCommandLine.Execute)]; +#if NET9_0_OR_GREATER + private static readonly CompositeFormat HelpOptionFormat = CompositeFormat.Parse(HelpOption); + private static readonly CompositeFormat VerbosityOptionFormat = CompositeFormat.Parse(VerbosityOption); +#endif private readonly string _processName; private readonly ICommandLineArguments _args; private readonly IDisplay _display; @@ -91,10 +95,9 @@ private RunResult FindAndExecuteCommandLineProcessor(List proce Type processorType = processor.GetType(); - List methods = processorType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + List methods = [.. processorType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) .Where(m => m.Name.Equals(_args.SubOption, StringComparison.OrdinalIgnoreCase)) - .OrderBy(m => m.GetParameters().Length) - .ToList(); + .OrderBy(m => m.GetParameters().Length)]; // find a matching method that has the right parameters for those passed, include default parameters if missing etc RunResult candidateResult = RunResult.None; @@ -111,7 +114,7 @@ private RunResult FindAndExecuteCommandLineProcessor(List proce { // no candidate methods found, use default method instead var defaultMethod = processorType.GetMethod(nameof(BaseCommandLine.Execute), BindingFlags.Instance | BindingFlags.Public); - resultCode = (int)defaultMethod.Invoke(processor, new object[] { _args.AllArguments() }); + resultCode = (int)defaultMethod.Invoke(processor, [_args.AllArguments()]); return RunResult.DefaultSubOptionUsed; } @@ -128,13 +131,13 @@ private RunResult FindAndExecuteCommandLineProcessor(List proce { MethodInfo method = methods[i]; bool isValidCandidate = true; - List parameters = new(); - List errorList = new(); + List parameters = []; + List errorList = []; // validate each parameter foreach (ParameterInfo param in method.GetParameters()) { - CmdLineAbbreviationAttribute abbreviated = param.GetCustomAttribute(typeof(CmdLineAbbreviationAttribute)) as CmdLineAbbreviationAttribute; + CmdLineAbbreviationAttribute abbreviated = param.GetCustomAttribute(); bool useAbbreviation = abbreviated != null && !_args.Contains(param.Name); string argName = useAbbreviation ? abbreviated.Abbreviation : param.Name; @@ -180,7 +183,7 @@ e is OverflowException || if (isValidCandidate) { - object resultCode = method.Invoke(instance, parameters.ToArray()) ?? SuccessResponseCode; + object resultCode = method.Invoke(instance, [.. parameters]) ?? SuccessResponseCode; return (RunResult.CandidateFound, (int)resultCode); } @@ -197,18 +200,28 @@ e is OverflowException || } } - throw new NotImplementedException(); + return (RunResult.DefaultSubOptionUsed, DefaultSubOptionUsed); } private int ShowHelpForAllCommandLineProcessors(List processors) { if (_options.ShowHelpMessage) +#if NET9_0_OR_GREATER + _display.WriteLine(Quiet, string.Format(CultureInfo.InvariantCulture, HelpOptionFormat, + SetMinimumLength(_options.ParameterPrefix + "-?", _options.InternalOptionsMinimumLength), _options.ParameterSuffix, DisplayHelp)); +#else _display.WriteLine(Quiet, string.Format(CultureInfo.InvariantCulture, HelpOption, SetMinimumLength(_options.ParameterPrefix + "-?", _options.InternalOptionsMinimumLength), _options.ParameterSuffix, DisplayHelp)); +#endif if (_options.ShowVerbosity) +#if NET9_0_OR_GREATER + _display.WriteLine(Quiet, string.Format(CultureInfo.InvariantCulture, VerbosityOptionFormat, + SetMinimumLength(_options.ParameterPrefix + "-v", _options.InternalOptionsMinimumLength), _options.ParameterSuffix, DisplayVerbosity)); +#else _display.WriteLine(Quiet, string.Format(CultureInfo.InvariantCulture, VerbosityOption, SetMinimumLength(_options.ParameterPrefix + "-v", _options.InternalOptionsMinimumLength), _options.ParameterSuffix, DisplayVerbosity)); +#endif if (_options.ShowHelpMessage || _options.ShowVerbosity) _display.WriteLine(Quiet, "\t"); @@ -263,17 +276,15 @@ private int DisplayAllSubOptionsForProcessor(BaseCommandLine processor) { _display.WriteLine(Quiet, processor.Name); Type processorType = processor.GetType(); - List namesProcessed = new(); + List namesProcessed = []; - List methods = processorType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding) - .Where(m => !m.IsSpecialName) - .ToList(); + List methods = [.. processorType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding).Where(m => !m.IsSpecialName)]; methods.RemoveAll(rm => _ignoreMethods.Contains(rm.Name)); foreach (MethodInfo methodInfo in methods) { - if (namesProcessed.Contains(methodInfo.Name) || methodInfo.GetCustomAttribute(typeof(CmdLineHiddenAttribute)) != null) + if (namesProcessed.Contains(methodInfo.Name) || methodInfo.GetCustomAttribute() != null) continue; _display.WriteLine(Quiet, $"{_options.SubOptionPrefix}{SetMinimumLength(methodInfo.Name, _options.SubOptionMinimumLength)}{_options.SubOptionSuffix}{GetMethodDescription(methodInfo)}"); @@ -308,11 +319,11 @@ private int DisplayAllParametersForSubOption(BaseCommandLine processor, MethodIn string description = String.Empty; string abbreviation = String.Empty; - CmdLineAbbreviationAttribute abbreviatedAttr = param.GetCustomAttribute(typeof(CmdLineAbbreviationAttribute)) as CmdLineAbbreviationAttribute; + CmdLineAbbreviationAttribute abbreviatedAttr = param.GetCustomAttribute(); if (abbreviatedAttr == null) { - CmdLineDescriptionAttribute descriptionAttr = param.GetCustomAttribute(typeof(CmdLineDescriptionAttribute)) as CmdLineDescriptionAttribute; + CmdLineDescriptionAttribute descriptionAttr = param.GetCustomAttribute(); if (descriptionAttr != null) description = descriptionAttr.Description; @@ -336,7 +347,7 @@ private int DisplayAllParametersForSubOption(BaseCommandLine processor, MethodIn private static string GetMethodDescription(MethodInfo methodInfo) { - CmdLineDescriptionAttribute descriptionAttr = methodInfo.GetCustomAttribute(typeof(CmdLineDescriptionAttribute)) as CmdLineDescriptionAttribute; + CmdLineDescriptionAttribute descriptionAttr = methodInfo.GetCustomAttribute(); if (descriptionAttr == null) return String.Empty; @@ -346,7 +357,7 @@ private static string GetMethodDescription(MethodInfo methodInfo) private static string GetProcessorDescription(BaseCommandLine processor) { - CmdLineDescriptionAttribute descriptionAttr = processor.GetType().GetCustomAttribute(typeof(CmdLineDescriptionAttribute)) as CmdLineDescriptionAttribute; + CmdLineDescriptionAttribute descriptionAttr = processor.GetType().GetCustomAttribute(); if (descriptionAttr == null) return String.Empty; @@ -356,7 +367,7 @@ private static string GetProcessorDescription(BaseCommandLine processor) private List ValidateCommandLineProcessors() { - List result = new(); + List result = []; _display.WriteLine(Full, FindingProcessors); foreach (object processor in _processors) @@ -381,7 +392,7 @@ private List ValidateCommandLineProcessors() } } - return result.OrderBy(p => p.SortOrder).ToList(); + return [.. result.OrderBy(p => p.SortOrder)]; } } } diff --git a/src/Readme.md b/src/Readme.md index 1508e22..ead14f3 100644 --- a/src/Readme.md +++ b/src/Readme.md @@ -11,19 +11,19 @@ myprog.exe Option SubOption -param:value #### Supports -- Net7.0 -- Netstandard2.0 +- .NET 9.0 +- .NET Standard 2.0 #### How it works -Create a class which descends from BaseCommandLine, specify the name (primary option) and create methods (each method is a sub option +Create a class which descends from `BaseCommandLine`, specify the `Name` (primary option) and create methods (each method is a sub option). -Add CmdLineDescriptions and CmdLineAbbreviation to methods, properties and processor classes for self describing help information. +Add `CmdLineDescription` and `CmdLineAbbreviation` attributes to methods, parameters and processor classes for self-describing help information. -In the following example we can these command lines: +In the following example we can use these command lines: ``` -myprog.exe Plugin /p:myplugin +myprog.exe Plugin myprog.exe Plugin Add -p:myplugin myprog.exe Plugin Remove --p:myplugin myprog.exe Plugin Disable /p:myplugin @@ -31,6 +31,8 @@ myprog.exe Plugin Enable -p:myplugin ``` ``` + using CommandLinePlus; + [CmdLineDescription("Processes plugins for entire application")] internal class PluginProcessor : BaseCommandLine, IDisposable { @@ -107,13 +109,78 @@ myprog.exe Plugin Enable -p:myplugin } } ``` -#### Available Paramater seperators -The following characters can be used as param seperators -- = (equals) -- : (colon) - -#### Available Paramater Idetifiers +#### Wiring it up + +Add a reference to the `CommandLinePlus` package, create your processors, then bootstrap them from the application entry point. The demo (`CmdLineTest`) uses C# top-level statements: + +```csharp +using CmdLineTest; + +IConsoleProcessorFactory factory = new ConsoleProcessorFactory(); + +object[] processors = new object[] +{ + new PluginProcessor(), +}; + +IConsoleProcessor consoleProcessor = factory.Create("MyProg", processors); + +switch (consoleProcessor.Run(out int resultCode)) +{ + case RunResult.CandidateFound: + Console.WriteLine("finished"); + break; + + case RunResult.DisplayHelp: + break; + + default: + throw new InvalidOperationException("Didn't work"); +} + +return resultCode; +``` + +The demo project makes the `CommandLinePlus` types available via a global using in its `.csproj`: + +```xml + +``` + +If you don't use that, add `using CommandLinePlus;` to your file instead. The `MyProg` argument passed to `Create` is the process name shown in help; replace it with your application's name. + +`Run` returns a `RunResult`: + +| Value | Meaning | +| --- | --- | +| `None` | No operation performed (default value) | +| `DisplayHelp` | Built-in `-?` help was shown | +| `NotEnoughCandidates` | No processor matched the primary option | +| `TooManyCandidates` | More than one processor matched the primary option | +| `DefaultSubOptionUsed` | Sub-option did not match a method, `Execute` was called instead | +| `InvalidParameters` | A method matched but an argument could not be converted | +| `CandidateFound` | A sub-option method ran successfully | + +#### Built-in options + +- `-?` (or `/?`) shows help for all processors, one processor's sub-options, or a sub-option's parameters. +- `-v:` (or `/v:`) sets the verbosity level: `0` Quiet, `1` Normal, `2` Diagnostic, `3` Full. The default is `1` (Normal). + +#### Parameters + +Sub-option methods receive values from command-line parameters. Parameter names are matched case-insensitively, sub-option method names are matched case-insensitively, and the primary option name is case-sensitive by default. + +- `bool`, `int`, `enum` and `Guid` values are converted automatically. +- Parameters with a default value are optional. +- A method may return `void` or `int`; an `int` result becomes the process exit code. + +#### Available parameter separators +The following characters can be used as parameter separators +- `=` (equals) +- `:` (colon) + +#### Available parameter identifiers The following are used to identify parameter values - - (-) (single dash) doesn't need the brackets except in markdown :-\ - - -- (double dash) - - / (forward slash) \ No newline at end of file +- `-` (single dash) +- `--` (double dash) +- `/` (forward slash) \ No newline at end of file diff --git a/tests/CommandLinePlusTests.csproj b/tests/CommandLinePlusTests.csproj index 312bce5..fa99e84 100644 --- a/tests/CommandLinePlusTests.csproj +++ b/tests/CommandLinePlusTests.csproj @@ -1,7 +1,7 @@  - net7.0 + net9.0 Library false false diff --git a/tests/ConsoleProcessorFacadeTests.cs b/tests/ConsoleProcessorFacadeTests.cs index 4e937fa..e8a185b 100644 --- a/tests/ConsoleProcessorFacadeTests.cs +++ b/tests/ConsoleProcessorFacadeTests.cs @@ -1,13 +1,10 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -using CommandLinePlus; +using CommandLinePlus; using CommandLinePlus.Internal; - using CommandLinePlusTests.Mocks; using CommandLinePlusTests.TestProcessors; - using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics.CodeAnalysis; namespace CommandLinePlusTests { @@ -75,11 +72,11 @@ public void Run_InvalidProcessor_DoesNotDescendFromBaseCommandLine_Throws_Invali { ICommandLineArguments args = new CommandLineArguments(new string[] { "--?" }); MockDisplay mockDisplay = new(); - ConsoleProcessorFacade sut = new("TestSuite", - new object[] - { - new InvalidProcessor() - }, + ConsoleProcessorFacade sut = new("TestSuite", + new object[] + { + new InvalidProcessor() + }, args, mockDisplay); sut.Run(out int _); @@ -164,9 +161,9 @@ public void Run_SubOptionFound_CallsRequiredMethod() public void Run_SubOptionFound_WithOptionalParams_CallsRequiredMethod() { TestProcessorWithMultipleSubOptionCandidates testProcessor = new(); - ICommandLineArguments args = new CommandLineArguments(new string[] - { - "Option", "Test", + ICommandLineArguments args = new CommandLineArguments(new string[] + { + "Option", "Test", "/a=\"a2f119a9-d030-4025-b122-a00a32288d94\"", "-b:hello", "--c=world" @@ -211,7 +208,7 @@ public void Run_SubOptionFound_FormatException_CallsRequiredMethod() RunResult result = sut.Run(out int _); Assert.AreEqual(RunResult.InvalidParameters, result); Assert.AreEqual(6, mockDisplay.Lines.Count); - Assert.AreEqual("Could not convert argument a (not a guid) to Guid - Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).", mockDisplay.Lines[5]); + Assert.AreEqual("Could not convert argument a (not a guid) to Guid - Unrecognized Guid format.", mockDisplay.Lines[5]); } [TestMethod] From 80dec36e67559e48720ec2fb3fd3e07f96e2f07e Mon Sep 17 00:00:00 2001 From: k3ldar Date: Sat, 15 Aug 2026 18:04:54 +0200 Subject: [PATCH 2/2] Update version for net9 --- Directory.Build.props | 2 +- src/CommandLinePlus.csproj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cb29665..e6e6534 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,7 +31,7 @@ - 2.3.0 + 2.4.0 $(AssemblyVersion).0 $(Version) $(Version) diff --git a/src/CommandLinePlus.csproj b/src/CommandLinePlus.csproj index cc8b74f..ef0d98a 100644 --- a/src/CommandLinePlus.csproj +++ b/src/CommandLinePlus.csproj @@ -25,6 +25,7 @@ myprog.exe Option Sub -p1:test /p3:test https://github.com/k3ldar/CommandLinePlus https://github.com/k3ldar/CommandLinePlus + Copyright (c) 2022 - 2026 Simon Carter. All rights reserved. @@ -91,7 +92,7 @@ myprog.exe Option Sub -p1:test /p3:test - +