Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CmdLineTest/CmdLineTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<ExcludeFromCodeCoverage>true</ExcludeFromCodeCoverage>
Expand Down
6 changes: 3 additions & 3 deletions CmdLineTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

IConsoleProcessorFactory factory = new ConsoleProcessorFactory();

object[] processors = new object[]
{
object[] processors =
[
new PluginProcessor(),
};
];

IConsoleProcessor consoleProcessor = factory.Create("MyProg", processors);

Expand Down
16 changes: 8 additions & 8 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="Exists('$(MSBuildThisFileDirectory)..\Directory.Build.props')"/>
<PropertyGroup>
<FrameWorks>net7.0;netstandard2.0</FrameWorks>
<FrameWorks>net9.0;netstandard2.0</FrameWorks>
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<NoWarn>1701;1702;8032;8981</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand All @@ -22,16 +22,16 @@
<PackageTags>GCode Sender</PackageTags>
<PackageProjectUrl>http://pluginmanager.website/</PackageProjectUrl>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<PackageReleaseNotes>Supports net7.0</PackageReleaseNotes>
<PackageReleaseNotes>Supports net9.0</PackageReleaseNotes>
<PackageTags>CommandLinePlus - Command line processor for c#</PackageTags>
</PropertyGroup>

<PropertyGroup>
<FrameWorkLatestVersion>net7.0</FrameWorkLatestVersion>
<FrameWorkLatestVersion>net9.0</FrameWorkLatestVersion>
</PropertyGroup>

<PropertyGroup>
<AssemblyVersion>2.3.0</AssemblyVersion>
<AssemblyVersion>2.4.0</AssemblyVersion>
<Version>$(AssemblyVersion).0</Version>
<FileVersion>$(Version)</FileVersion>
<ProductVersion>$(Version)</ProductVersion>
Expand All @@ -47,15 +47,15 @@
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Debug|net7.0'">
<DefineConstants>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</DefineConstants>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Debug|net9.0'">
<DefineConstants>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</DefineConstants>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Release|net7.0'">
<DefineConstants>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</DefineConstants>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)'=='Release|net9.0'">
<DefineConstants>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</DefineConstants>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
Expand Down
100 changes: 78 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CommandLinePlus
# CommandLinePlus
This project started because I needed a command line parser that could:

- Have a primary option
Expand All @@ -11,26 +11,28 @@ 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
myprog.exe Plugin Enable -p:myplugin
```

```
using CommandLinePlus;

[CmdLineDescription("Processes plugins for entire application")]
internal class PluginProcessor : BaseCommandLine, IDisposable
{
Expand Down Expand Up @@ -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
<Using Include="CommandLinePlus" />
```

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:<level>` (or `/v:<level>`) 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)
5 changes: 3 additions & 2 deletions src/CommandLinePlus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>Command line plus</Title>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageReleaseNotes>Supports netstandard2.0 and net7.0</PackageReleaseNotes>
<PackageReleaseNotes>Supports netstandard2.0 and net9.0</PackageReleaseNotes>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>latest-all</AnalysisLevel>
Expand All @@ -25,6 +25,7 @@
myprog.exe Option Sub -p1:test /p3:test</Description>
<PackageProjectUrl>https://github.com/k3ldar/CommandLinePlus</PackageProjectUrl>
<RepositoryUrl>https://github.com/k3ldar/CommandLinePlus</RepositoryUrl>
<Copyright>Copyright (c) 2022 - 2026 Simon Carter. All rights reserved.</Copyright>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
Expand Down Expand Up @@ -91,7 +92,7 @@ myprog.exe Option Sub -p1:test /p3:test</Description>
</ItemGroup>

<ItemGroup>
<None Include="readme.md" Pack="true" PackagePath=""/>
<None Include="readme.md" Pack="true" PackagePath="" />
</ItemGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)|$(CodeSignAssembly)'=='Release|true'">
Expand Down
2 changes: 1 addition & 1 deletion src/Internal/CommandLineArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public string[] AllArguments()
args.Add($"{item.Key}={item.Value}");
}

return args.ToArray();
return [.. args];
}

#endregion ICommandLine Methods
Expand Down
27 changes: 24 additions & 3 deletions src/Internal/ConsoleDisplay.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;

using static CommandLinePlus.Constants;

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<int>(Constants.CmdLineSettingVerbosity, 1);

#if NET9_0_OR_GREATER
if (!Enum.IsDefined(Verbosity))
#else
if (!Enum.IsDefined(typeof(VerbosityLevel), Verbosity))
#endif
Verbosity = VerbosityLevel.Normal;
}

Expand All @@ -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++;
}
Expand Down Expand Up @@ -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);

Expand Down
Loading
Loading