├── .github
├── dependabot.yml
└── workflows
│ └── dotnet.yml
├── .gitignore
├── README.md
├── docs
├── Arguments.md
├── ChangeLog.md
├── DefiningArguments.md
├── Migrating.md
├── Ookii.CommandLine.shfbproj
├── ParsingArguments.md
├── README.md
├── SourceGeneration.md
├── SourceGenerationDiagnostics.md
├── Subcommands.md
├── Tutorial.md
├── UsageHelp.md
├── Utilities.md
├── Validation.md
├── images
│ ├── color.png
│ ├── custom_usage.png
│ └── wpf.png
└── refs.json
├── license.md
└── src
├── .editorconfig
├── Create-Release.ps1
├── Directory.Build.props
├── Ookii.CommandLine.CodeFix
├── Ookii.CommandLine.CodeFix.csproj
├── ParserCodeFixProvider.cs
├── Properties
│ ├── Resources.Designer.cs
│ └── Resources.resx
└── ookii.snk
├── Ookii.CommandLine.Generator
├── AnalyzerReleases.Shipped.md
├── AnalyzerReleases.Unshipped.md
├── ArgumentAttributes.cs
├── ArgumentsClassAttributes.cs
├── CommandAttributeInfo.cs
├── CommandGenerator.cs
├── CommandLineArgumentAttributeInfo.cs
├── ConverterGenerator.cs
├── Diagnostics.cs
├── Extensions.cs
├── Ookii.CommandLine.Generator.csproj
├── ParserAnalyzer.cs
├── ParserGenerator.cs
├── ParserIncrementalGenerator.cs
├── Properties
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── SourceBuilder.cs
├── TypeHelper.cs
└── ookii.snk
├── Ookii.CommandLine.Tests.Commands
├── Commands.cs
├── Ookii.CommandLine.Tests.Commands.csproj
└── ookii.snk
├── Ookii.CommandLine.Tests
├── .editorconfig
├── ArgumentCategory.cs
├── ArgumentTypes.cs
├── ArgumentValidatorTest.cs
├── CommandLineParserNullableTest.cs
├── CommandLineParserTest.Usage.cs
├── CommandLineParserTest.cs
├── CommandOptionsTest.cs
├── CommandTypes.cs
├── CustomUsageWriter.cs
├── KeyValuePairConverterTest.cs
├── LineWrappingTextWriterTest.Constants.cs
├── LineWrappingTextWriterTest.cs
├── NameTransformTest.cs
├── NetStandardHelpers.cs
├── NullableArgumentTypes.cs
├── Ookii.CommandLine.Tests.csproj
├── ParseOptionsAttributeTest.cs
├── ParseOptionsTest.cs
├── StandardStreamTest.cs
├── SubCommandTest.Usage.cs
├── SubCommandTest.cs
├── TextFormatTest.cs
└── ookii.snk
├── Ookii.CommandLine.sln
├── Ookii.CommandLine
├── AliasAttribute.cs
├── AllowDuplicateDictionaryKeysAttribute.cs
├── AmbiguousPrefixAliasException.cs
├── ApplicationFriendlyNameAttribute.cs
├── ArgumentKind.cs
├── ArgumentParsedEventArgs.cs
├── BreakLineMode.cs
├── CancelMode.cs
├── CategoryInfo.cs
├── CommandLineArgument.cs
├── CommandLineArgumentAttribute.cs
├── CommandLineArgumentErrorCategory.cs
├── CommandLineArgumentException.cs
├── CommandLineParser.cs
├── CommandLineParserGeneric.cs
├── Commands
│ ├── AsyncCommandBase.cs
│ ├── AutomaticVersionCommand.cs
│ ├── AutomaticVersionCommandInfo.cs
│ ├── CommandAttribute.cs
│ ├── CommandInfo.cs
│ ├── CommandManager.cs
│ ├── CommandOptions.cs
│ ├── GeneratedCommandManagerAttribute.cs
│ ├── IAsyncCommand.cs
│ ├── ICommand.cs
│ ├── ICommandWithCustomParsing.cs
│ ├── ParentCommand.cs
│ └── ParentCommandAttribute.cs
├── Conversion
│ ├── ArgumentConverter.cs
│ ├── ArgumentConverterAttribute.cs
│ ├── BooleanConverter.cs
│ ├── ConstructorConverter.cs
│ ├── EnumConverter.cs
│ ├── GeneratedConverterNamespaceAttribute.cs
│ ├── KeyConverterAttribute.cs
│ ├── KeyValuePairConverter.cs
│ ├── KeyValueSeparatorAttribute.cs
│ ├── NullableConverter.cs
│ ├── ParsableConverter.cs
│ ├── ParseConverter.cs
│ ├── SpanParsableConverter.cs
│ ├── StringConverter.cs
│ ├── ValueConverterAttribute.cs
│ ├── WrappedDefaultTypeConverter.cs
│ ├── WrappedTypeConverter.cs
│ └── WrappedTypeConverterGeneric.cs
├── Convert-SyncMethod.ps1
├── DescriptionListFilterMode.cs
├── DescriptionListSortMode.cs
├── DictionaryArgumentInfo.cs
├── DisposableWrapper.cs
├── DuplicateArgumentEventArgs.cs
├── ErrorMode.cs
├── GeneratedParserAttribute.cs
├── IParser.cs
├── IParserProvider.cs
├── LineWrappingTextWriter.Async.cs
├── LineWrappingTextWriter.cs
├── LocalizedStringProvider.Error.cs
├── LocalizedStringProvider.Usage.cs
├── LocalizedStringProvider.Validators.cs
├── LocalizedStringProvider.cs
├── MultiValueArgumentInfo.cs
├── MultiValueSeparatorAttribute.cs
├── NameTransform.cs
├── NameTransformExtensions.cs
├── NativeMethods.txt
├── NonSwitchBoolean.cs
├── Ookii.CommandLine.csproj
├── PackageReadme.md
├── ParseOptions.cs
├── ParseOptionsAttribute.cs
├── ParseResult.cs
├── ParseStatus.cs
├── ParsingMode.cs
├── PrefixTerminationMode.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── RingBuffer.Async.cs
├── RingBuffer.cs
├── ShortAliasAttribute.cs
├── StringBuilderExtensions.cs
├── StringComparisonExtensions.cs
├── StringSegmentType.cs
├── StringSpan.Async.cs
├── StringSpanExtensions.cs
├── Support
│ ├── ArgumentCreationInfo.cs
│ ├── ArgumentProvider.cs
│ ├── CommandProvider.cs
│ ├── GeneratedArgument.cs
│ ├── GeneratedArgumentBase.cs
│ ├── GeneratedArgumentProvider.cs
│ ├── GeneratedCommandInfo.cs
│ ├── GeneratedCommandInfoWithCustomParsing.cs
│ ├── GeneratedDictionaryArgument.cs
│ ├── ProviderKind.cs
│ ├── ReflectionArgument.cs
│ ├── ReflectionArgumentProvider.cs
│ ├── ReflectionCommandInfo.cs
│ └── ReflectionCommandProvider.cs
├── Terminal
│ ├── StandardStream.cs
│ ├── StandardStreamExtensions.cs
│ ├── TextFormat.cs
│ ├── VirtualTerminal.cs
│ └── VirtualTerminalSupport.cs
├── TriState.cs
├── TypeHelper.cs
├── UnknownArgumentEventArgs.cs
├── UsageFooterAttribute.cs
├── UsageHelpRequest.cs
├── UsageWriter.cs
├── Validation
│ ├── ArgumentValidationAttribute.cs
│ ├── ArgumentValidationWithHelpAttribute.cs
│ ├── ClassValidationAttribute.cs
│ ├── DependencyValidationAttribute.cs
│ ├── ProhibitsAttribute.cs
│ ├── RequiresAnyAttribute.cs
│ ├── RequiresAttribute.cs
│ ├── ValidateCountAttribute.cs
│ ├── ValidateEnumValueAttribute.cs
│ ├── ValidateNotEmptyAttribute.cs
│ ├── ValidateNotNullAttribute.cs
│ ├── ValidateNotWhiteSpaceAttribute.cs
│ ├── ValidatePatternAttribute.cs
│ ├── ValidateRangeAttribute.cs
│ └── ValidateStringLengthAttribute.cs
├── ValueDescriptionAttribute.cs
├── WrappingMode.cs
├── icon.png
└── ookii.snk
├── Samples
├── ArgumentDependencies
│ ├── ArgumentDependencies.csproj
│ ├── Program.cs
│ ├── ProgramArguments.cs
│ └── README.md
├── Categories
│ ├── ArgumentCategory.cs
│ ├── Arguments.cs
│ ├── Categories.csproj
│ ├── DomainUser.cs
│ ├── InstallMethod.cs
│ ├── Program.cs
│ └── README.md
├── CustomUsage
│ ├── CustomStringProvider.cs
│ ├── CustomUsage.csproj
│ ├── CustomUsageWriter.cs
│ ├── Program.cs
│ ├── ProgramArguments.cs
│ └── README.md
├── LongShort
│ ├── LongShort.csproj
│ ├── Program.cs
│ ├── ProgramArguments.cs
│ └── README.md
├── NestedCommands
│ ├── BaseCommand.cs
│ ├── CourseCommands.cs
│ ├── ExitCode.cs
│ ├── GeneratedManager.cs
│ ├── ListCommand.cs
│ ├── Models.cs
│ ├── NestedCommands.csproj
│ ├── Program.cs
│ ├── README.md
│ └── StudentCommands.cs
├── Parser
│ ├── Parser.csproj
│ ├── Program.cs
│ ├── ProgramArguments.cs
│ └── README.md
├── README.md
├── Subcommand
│ ├── EncodingConverter.cs
│ ├── ExitCode.cs
│ ├── GeneratedManager.cs
│ ├── Program.cs
│ ├── README.md
│ ├── ReadCommand.cs
│ ├── Subcommand.csproj
│ └── WriteCommand.cs
├── TopLevelArguments
│ ├── CommandUsageWriter.cs
│ ├── EncodingConverter.cs
│ ├── ExitCode.cs
│ ├── GeneratedManager.cs
│ ├── Program.cs
│ ├── README.md
│ ├── ReadCommand.cs
│ ├── TopLevelArguments.cs
│ ├── TopLevelArguments.csproj
│ ├── TopLevelUsageWriter.cs
│ └── WriteCommand.cs
└── Wpf
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── Arguments.cs
│ ├── AssemblyInfo.cs
│ ├── HtmlUsageWriter.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── README.md
│ ├── UsageWindow.xaml
│ ├── UsageWindow.xaml.cs
│ ├── Wpf.csproj
│ └── app.manifest
├── history.txt
└── test.runsettings
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/src"
5 | schedule:
6 | interval: "daily"
7 | target-branch: "main"
8 | groups:
9 | # Group all dependencies in one PR.
10 | nuget-dependencies:
11 | patterns:
12 | - "*"
13 | ignore:
14 | # 4.11.x is the latest version that can work with the .Net 8.0 SDK for both of these.
15 | - dependency-name: "Microsoft.CodeAnalysis.CSharp"
16 | update-types: ["version-update:semver-major", "version-update:semver-minor"]
17 | - dependency-name: "Microsoft.CodeAnalysis.CSharp.Workspaces"
18 | update-types: ["version-update:semver-major", "version-update:semver-minor"]
19 | - package-ecosystem: "github-actions"
20 | directory: "/"
21 | schedule:
22 | interval: "daily"
23 | target-branch: "main"
24 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: .NET
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 | strategy:
15 | matrix:
16 | os: [ubuntu-latest, windows-latest]
17 |
18 | runs-on: ${{ matrix.os }}
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Setup .NET
22 | uses: actions/setup-dotnet@v4
23 | with:
24 | dotnet-version: 8.0.x
25 | - name: Restore dependencies
26 | run: dotnet restore src
27 | - name: Build
28 | run: dotnet build src --no-restore --configuration Release
29 | - name: Test
30 | run: dotnet test src --no-build --verbosity normal --configuration Release
31 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Ookii.CommandLine is a library for parsing the command line arguments for your applications. In this
4 | guide, we will introduce the basic functionality using a tutorial, describe in detail the command
5 | line parsing rules used and how to create various types of arguments, how to use and customize usage
6 | help, and explain other functionality such as subcommands.
7 |
8 | In addition to this documentation, several [samples](../src/Samples) are provided, all with
9 | explanations of what they do and examples of their output.
10 |
11 | ## Contents
12 |
13 | - [What's new in Ookii.CommandLine](ChangeLog.md)
14 | - [Migrating from Ookii.CommandLine 2.x / 3.x](Migrating.md)
15 | - [Tutorial: getting started with Ookii.CommandLine](Tutorial.md)
16 | - [Command line arguments in Ookii.CommandLine](Arguments.md)
17 | - [Defining command line arguments](DefiningArguments.md)
18 | - [Parsing command line arguments](ParsingArguments.md)
19 | - [Generating usage help](UsageHelp.md)
20 | - [Argument validation and dependencies](Validation.md)
21 | - [Subcommands](Subcommands.md)
22 | - [Source generation](SourceGeneration.md)
23 | - [Diagnostics](SourceGenerationDiagnostics.md)
24 | - [LineWrappingTextWriter and other utilities](Utilities.md)
25 | - [Class library documentation](https://www.ookii.org/Link/CommandLineDoc)
26 |
--------------------------------------------------------------------------------
/docs/images/color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/docs/images/color.png
--------------------------------------------------------------------------------
/docs/images/custom_usage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/docs/images/custom_usage.png
--------------------------------------------------------------------------------
/docs/images/wpf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/docs/images/wpf.png
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | # MIT license
2 |
3 | Copyright (c) Sven Groot (Ookii.org)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Create-Release.ps1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/src/Create-Release.ps1
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sven Groot
5 | Ookii.org
6 | Copyright (c) Sven Groot (Ookii.org)
7 | 5.0.0
8 |
9 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.CodeFix/Ookii.CommandLine.CodeFix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | false
6 | 12.0
7 | enable
8 | enable
9 | true
10 | true
11 | ookii.snk
12 | false
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 | True
29 | Resources.resx
30 |
31 |
32 |
33 |
34 |
35 | ResXFileCodeGenerator
36 | Resources.Designer.cs
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.CodeFix/ookii.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/src/Ookii.CommandLine.CodeFix/ookii.snk
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Generator/AnalyzerReleases.Unshipped.md:
--------------------------------------------------------------------------------
1 | ; Unshipped analyzer release
2 | ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
3 | ; This is only for rules used by the analyzer; rules for the source generator should not be listed
4 | ; here.
5 |
6 | ### New Rules
7 |
8 | Rule ID | Category | Severity | Notes
9 | --------|-------------------|----------|-----------------------
10 | OCL0043 | Ookii.CommandLine | Error | CategoryNotEnum
11 | OCL0044 | Ookii.CommandLine | Error | MismatchedCategoryType
12 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Generator/CommandAttributeInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Ookii.CommandLine.Generator;
4 |
5 | internal class CommandAttributeInfo
6 | {
7 | public CommandAttributeInfo(AttributeData data)
8 | {
9 | foreach (var named in data.NamedArguments)
10 | {
11 | switch (named.Key)
12 | {
13 | case nameof(IsHidden):
14 | IsHidden = (bool)named.Value.Value!;
15 | break;
16 | }
17 | }
18 | }
19 |
20 | public bool IsHidden { get; }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Generator/CommandLineArgumentAttributeInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace Ookii.CommandLine.Generator;
4 |
5 | internal class CommandLineArgumentAttributeInfo
6 | {
7 | private readonly bool _isShort;
8 | private readonly bool _isPositional;
9 |
10 | public CommandLineArgumentAttributeInfo(AttributeData data)
11 | {
12 | if (data.ConstructorArguments.Length > 0)
13 | {
14 | ArgumentName = data.ConstructorArguments[0].Value as string;
15 | }
16 |
17 | foreach (var named in data.NamedArguments)
18 | {
19 | switch (named.Key)
20 | {
21 | case nameof(IsRequired):
22 | IsRequired = (bool)named.Value.Value!;
23 | HasIsRequired = true;
24 | break;
25 |
26 | case nameof(DefaultValue):
27 | DefaultValue = named.Value.Value;
28 | break;
29 |
30 | case nameof(Position):
31 | var position = (int)named.Value.Value!;
32 | if (position >= 0)
33 | {
34 | Position = position;
35 | }
36 |
37 | break;
38 |
39 | case nameof(IsPositional):
40 | _isPositional = (bool)named.Value.Value!;
41 | break;
42 |
43 | case nameof(IsShort):
44 | _isShort = (bool)named.Value.Value!;
45 | ExplicitIsShort = _isShort;
46 | break;
47 |
48 | case nameof(ShortName):
49 | ShortName = (char)named.Value.Value!;
50 | break;
51 |
52 | case nameof(IsLong):
53 | IsLong = (bool)named.Value.Value!;
54 | break;
55 |
56 | case nameof(IsHidden):
57 | IsHidden = (bool)named.Value.Value!;
58 | break;
59 |
60 | case nameof(IncludeDefaultInUsageHelp):
61 | IncludeDefaultInUsageHelp = (bool)named.Value.Value!;
62 | break;
63 |
64 | case nameof(Category):
65 | Category = named.Value;
66 | break;
67 | }
68 | }
69 | }
70 |
71 | public string? ArgumentName { get; }
72 |
73 | public bool IsRequired { get; }
74 |
75 | public bool HasIsRequired { get; }
76 |
77 | public int? Position { get; }
78 |
79 | public bool IsPositional => _isPositional || Position != null;
80 |
81 | public object? DefaultValue { get; }
82 |
83 | public bool IsShort => _isShort || ShortName != '\0';
84 |
85 | public bool? ExplicitIsShort { get; }
86 |
87 | public char ShortName { get; }
88 |
89 | public bool IsLong { get; } = true;
90 |
91 | public bool IsHidden { get; }
92 |
93 | public bool IncludeDefaultInUsageHelp { get; set; } = true;
94 |
95 | public TypedConstant Category { get; set; }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Generator/Ookii.CommandLine.Generator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | false
6 | 12.0
7 | enable
8 | enable
9 | true
10 | true
11 | ookii.snk
12 | false
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | True
27 | True
28 | Resources.resx
29 |
30 |
31 |
32 |
33 |
34 | ResXFileCodeGenerator
35 | Resources.Designer.cs
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Generator/ookii.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/src/Ookii.CommandLine.Generator/ookii.snk
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests.Commands/Commands.cs:
--------------------------------------------------------------------------------
1 | // Commands to test loading commands from an external assembly.
2 | using Ookii.CommandLine.Commands;
3 |
4 | namespace Ookii.CommandLine.Tests.Commands;
5 |
6 | #pragma warning disable OCL0034 // Subcommands should have a description.
7 |
8 | [Command("external")]
9 | [GeneratedParser]
10 | public partial class ExternalCommand : ICommand
11 | {
12 | public int Run() => throw new NotImplementedException();
13 | }
14 |
15 | [Command]
16 | public class OtherExternalCommand : ICommand
17 | {
18 | public int Run() => throw new NotImplementedException();
19 | }
20 |
21 | [Command]
22 | internal class InternalCommand : ICommand
23 | {
24 | public int Run() => throw new NotImplementedException();
25 | }
26 |
27 | public class NotACommand : ICommand
28 | {
29 | public int Run() => throw new NotImplementedException();
30 | }
31 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests.Commands/Ookii.CommandLine.Tests.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net48
5 | enable
6 | enable
7 | 12.0
8 | true
9 | ookii.snk
10 | false
11 |
12 | 1.0.0
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests.Commands/ookii.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/src/Ookii.CommandLine.Tests.Commands/ookii.snk
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | # Unused parameters or privates are not a problem in the test project, and is often deliberate.
2 | [*.cs]
3 | dotnet_diagnostic.IDE0051.severity = silent
4 | dotnet_code_quality_unused_parameters = silent
5 | dotnet_diagnostic.IDE0060.severity = silent
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/ArgumentCategory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Ookii.CommandLine.Tests;
9 |
10 | class DerivedDescriptionAttribute : DescriptionAttribute
11 | {
12 | public override string Description => "The second category.";
13 | }
14 |
15 | enum ArgumentCategory
16 | {
17 | [Description("The first category.")]
18 | Category1,
19 | [DerivedDescription()]
20 | Category2,
21 | // No description
22 | Category3,
23 | // Unused
24 | [Description("The fourth category.")]
25 | Category4,
26 | }
27 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/CustomUsageWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine.Tests;
4 |
5 | internal class CustomUsageWriter : UsageWriter
6 | {
7 | public CustomUsageWriter() { }
8 |
9 | public CustomUsageWriter(LineWrappingTextWriter writer) : base(writer) { }
10 |
11 | protected override void WriteParserUsageFooter()
12 | {
13 | WriteLine("This is a custom footer.");
14 | }
15 |
16 | protected override void WriteCommandListUsageFooter()
17 | {
18 | base.WriteCommandListUsageFooter();
19 | WriteLine("This is the command list footer.");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/KeyValuePairConverterTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using Ookii.CommandLine.Conversion;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Globalization;
6 |
7 | namespace Ookii.CommandLine.Tests;
8 |
9 | [TestClass]
10 | public class KeyValuePairConverterTest
11 | {
12 | // Needed because SpanParsableConverter only exists on .Net 7.
13 | private class IntConverter : ArgumentConverter
14 | {
15 | public override object Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
16 | => int.Parse(value.ToString(), culture);
17 | }
18 |
19 | [TestMethod]
20 | public void TestConvertFrom()
21 | {
22 | var parser = new CommandLineParser();
23 | var converter = new KeyValuePairConverter();
24 | var converted = converter.Convert("foo=5".AsMemory(), CultureInfo.InvariantCulture, parser.GetArgument("Argument1")!);
25 | Assert.AreEqual(KeyValuePair.Create("foo", 5), converted);
26 | }
27 |
28 | [TestMethod]
29 | public void TestCustomSeparator()
30 | {
31 | var parser = new CommandLineParser();
32 | var converter = new KeyValuePairConverter(new StringConverter(), new IntConverter(), ":", false);
33 | var pair = converter.Convert("foo:5".AsMemory(), CultureInfo.InvariantCulture, parser.GetArgument("Argument1")!);
34 | Assert.AreEqual(KeyValuePair.Create("foo", 5), pair);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/NameTransformTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace Ookii.CommandLine.Tests;
4 |
5 | [TestClass]
6 | public class NameTransformTest
7 | {
8 | [TestMethod]
9 | public void TestNone()
10 | {
11 | var transform = NameTransform.None;
12 | Assert.AreEqual("TestName", transform.Apply("TestName"));
13 | Assert.AreEqual("testName", transform.Apply("testName"));
14 | Assert.AreEqual("__test__name__", transform.Apply("__test__name__"));
15 | Assert.AreEqual("TestName", transform.Apply("TestName"));
16 | }
17 |
18 | [TestMethod]
19 | public void TestPascalCase()
20 | {
21 | var transform = NameTransform.PascalCase;
22 | Assert.AreEqual("TestName", transform.Apply("TestName"));
23 | Assert.AreEqual("TestName", transform.Apply("testName"));
24 | Assert.AreEqual("TestName", transform.Apply("__test__name__"));
25 | Assert.AreEqual("TestName", transform.Apply("TestName"));
26 | }
27 |
28 | [TestMethod]
29 | public void TestCamelCase()
30 | {
31 | var transform = NameTransform.CamelCase;
32 | Assert.AreEqual("testName", transform.Apply("TestName"));
33 | Assert.AreEqual("testName", transform.Apply("testName"));
34 | Assert.AreEqual("testName", transform.Apply("__test__name__"));
35 | Assert.AreEqual("testName", transform.Apply("TestName"));
36 | }
37 |
38 | [TestMethod]
39 | public void TestSnakeCase()
40 | {
41 | var transform = NameTransform.SnakeCase;
42 | Assert.AreEqual("test_name", transform.Apply("TestName"));
43 | Assert.AreEqual("test_name", transform.Apply("testName"));
44 | Assert.AreEqual("test_name", transform.Apply("__test__name__"));
45 | Assert.AreEqual("test_name", transform.Apply("TestName"));
46 | }
47 |
48 | [TestMethod]
49 | public void TestDashCase()
50 | {
51 | var transform = NameTransform.DashCase;
52 | Assert.AreEqual("test-name", transform.Apply("TestName"));
53 | Assert.AreEqual("test-name", transform.Apply("testName"));
54 | Assert.AreEqual("test-name", transform.Apply("__test__name__"));
55 | Assert.AreEqual("test-name", transform.Apply("TestName"));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/NetStandardHelpers.cs:
--------------------------------------------------------------------------------
1 | #if !NET6_0_OR_GREATER
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Ookii.CommandLine.Tests;
8 |
9 | internal static class KeyValuePair
10 | {
11 | public static KeyValuePair Create(TKey key, TValue value)
12 | {
13 | return new KeyValuePair(key, value);
14 | }
15 | }
16 |
17 | internal static class StringExtensions
18 | {
19 | private static readonly char[] _newLineChars = { '\r', '\n' };
20 |
21 | public static string ReplaceLineEndings(this string value, string? ending = null)
22 | {
23 | ending ??= Environment.NewLine;
24 | var result = new StringBuilder();
25 | int pos = 0;
26 | while (pos < value.Length)
27 | {
28 | int index = value.IndexOfAny(_newLineChars, pos);
29 | if (index < 0)
30 | {
31 | result.Append(value.Substring(pos));
32 | break;
33 | }
34 |
35 | if (index > pos)
36 | {
37 | result.Append(value.Substring(pos, index - pos));
38 | }
39 |
40 | result.Append(ending);
41 | if (value[index] == '\r' && index + 1 < value.Length && value[index + 1] == '\n')
42 | {
43 | pos = index + 2;
44 | }
45 | else
46 | {
47 | pos = index + 1;
48 | }
49 | }
50 |
51 | return result.ToString();
52 | }
53 | }
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/Ookii.CommandLine.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net48
5 | net8.0
6 | enable
7 | Ookii.CommandLine Unit Tests
8 | Tests for Ookii.CommandLine.
9 | false
10 | 12.0
11 | true
12 | true
13 | ookii.snk
14 | false
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/ParseOptionsAttributeTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace Ookii.CommandLine.Tests;
4 |
5 | [TestClass]
6 | public class ParseOptionsAttributeTest
7 | {
8 | [TestMethod]
9 | public void TestConstructor()
10 | {
11 | var options = new ParseOptionsAttribute();
12 | Assert.IsTrue(options.AllowWhiteSpaceValueSeparator);
13 | Assert.IsNull(options.ArgumentNamePrefixes);
14 | Assert.AreEqual(NameTransform.None, options.ArgumentNameTransform);
15 | Assert.IsTrue(options.AutoHelpArgument);
16 | Assert.IsTrue(options.AutoPrefixAliases);
17 | Assert.IsTrue(options.AutoVersionArgument);
18 | Assert.IsFalse(options.CaseSensitive);
19 | Assert.AreEqual(ErrorMode.Error, options.DuplicateArguments);
20 | Assert.IsFalse(options.IsPosix);
21 | Assert.IsNull(options.LongArgumentNamePrefix);
22 | Assert.AreEqual(ParsingMode.Default, options.Mode);
23 | Assert.IsNull(options.NameValueSeparators);
24 | Assert.AreEqual(NameTransform.None, options.ValueDescriptionTransform);
25 | }
26 |
27 | [TestMethod]
28 | public void TestIsPosix()
29 | {
30 | var options = new ParseOptionsAttribute()
31 | {
32 | IsPosix = true
33 | };
34 |
35 | Assert.IsTrue(options.IsPosix);
36 | Assert.AreEqual(ParsingMode.LongShort, options.Mode);
37 | Assert.IsTrue(options.CaseSensitive);
38 | Assert.AreEqual(NameTransform.DashCase, options.ArgumentNameTransform);
39 | Assert.AreEqual(NameTransform.DashCase, options.ValueDescriptionTransform);
40 | options.CaseSensitive = false;
41 | Assert.IsFalse(options.IsPosix);
42 | options.CaseSensitive = true;
43 | Assert.IsTrue(options.IsPosix);
44 |
45 | options.IsPosix = false;
46 | Assert.AreEqual(ParsingMode.Default, options.Mode);
47 | Assert.IsFalse(options.CaseSensitive);
48 | Assert.AreEqual(NameTransform.None, options.ArgumentNameTransform);
49 | Assert.AreEqual(NameTransform.None, options.ValueDescriptionTransform);
50 |
51 | options = new ParseOptionsAttribute()
52 | {
53 | Mode = ParsingMode.LongShort,
54 | CaseSensitive = true,
55 | ArgumentNameTransform = NameTransform.DashCase,
56 | ValueDescriptionTransform = NameTransform.DashCase
57 | };
58 |
59 | Assert.IsTrue(options.IsPosix);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/StandardStreamTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using Ookii.CommandLine.Terminal;
3 | using System;
4 | using System.IO;
5 |
6 | namespace Ookii.CommandLine.Tests;
7 |
8 | [TestClass]
9 | public class StandardStreamTest
10 | {
11 | [TestMethod]
12 | public void TestGetWriter()
13 | {
14 | Assert.AreSame(Console.Out, StandardStream.Output.GetWriter());
15 | Assert.AreSame(Console.Error, StandardStream.Error.GetWriter());
16 | Assert.ThrowsException(() => StandardStream.Input.GetWriter());
17 | }
18 |
19 | [TestMethod]
20 | public void TestOpenStream()
21 | {
22 | using var output = StandardStream.Output.OpenStream();
23 | using var error = StandardStream.Error.OpenStream();
24 | using var input = StandardStream.Input.OpenStream();
25 | Assert.AreNotSame(output, input);
26 | Assert.AreNotSame(output, error);
27 | Assert.AreNotSame(error, input);
28 | }
29 |
30 | [TestMethod]
31 | public void TestGetStandardStream()
32 | {
33 | Assert.AreEqual(StandardStream.Output, Console.Out.GetStandardStream());
34 | Assert.AreEqual(StandardStream.Error, Console.Error.GetStandardStream());
35 | Assert.AreEqual(StandardStream.Input, Console.In.GetStandardStream());
36 | using (var writer = new StringWriter())
37 | {
38 | Assert.IsNull(writer.GetStandardStream());
39 | }
40 |
41 | using (var writer = LineWrappingTextWriter.ForConsoleOut())
42 | {
43 | Assert.AreEqual(StandardStream.Output, writer.GetStandardStream());
44 | }
45 |
46 | using (var writer = LineWrappingTextWriter.ForStringWriter())
47 | {
48 | Assert.IsNull(writer.GetStandardStream());
49 | }
50 |
51 | using (var reader = new StringReader("foo"))
52 | {
53 | Assert.IsNull(reader.GetStandardStream());
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/TextFormatTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using Ookii.CommandLine.Terminal;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Ookii.CommandLine.Tests;
10 |
11 | [TestClass]
12 | public class TextFormatTest
13 | {
14 | [TestMethod]
15 | public void TestDefault()
16 | {
17 | var value = new TextFormat();
18 | Assert.AreEqual("", value.Value);
19 |
20 | var value2 = default(TextFormat);
21 | Assert.AreEqual("", value2.Value);
22 | }
23 |
24 | [TestMethod]
25 | public void TestAddition()
26 | {
27 | var value = TextFormat.ForegroundRed + TextFormat.BackgroundGreen;
28 | Assert.AreEqual("\x1b[31m\x1b[42m", value.Value);
29 | }
30 |
31 | [TestMethod]
32 | public void TestEquality()
33 | {
34 | Assert.AreEqual(TextFormat.ForegroundRed, TextFormat.ForegroundRed);
35 | Assert.AreNotEqual(TextFormat.ForegroundGreen, TextFormat.ForegroundRed);
36 | var value1 = TextFormat.ForegroundRed;
37 | var value2 = TextFormat.ForegroundRed;
38 | Assert.IsTrue(value1 == value2);
39 | Assert.IsFalse(value1 != value2);
40 | value2 = TextFormat.ForegroundGreen;
41 | Assert.IsFalse(value1 == value2);
42 | Assert.IsTrue(value1 != value2);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine.Tests/ookii.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SvenGroot/Ookii.CommandLine/c40e39b380cf4d283f007643656203d9d953ee46/src/Ookii.CommandLine.Tests/ookii.snk
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/AllowDuplicateDictionaryKeysAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Ookii.CommandLine;
5 |
6 | ///
7 | /// Indicates that a dictionary argument accepts the same key more than once.
8 | ///
9 | ///
10 | ///
11 | /// If this attribute is applied to an argument whose type is
12 | /// or another type that implements the interface, a
13 | /// duplicate key will simply overwrite the previous value.
14 | ///
15 | ///
16 | /// If this attribute is not applied, a with a
17 | /// the property set to
18 | /// will
19 | /// be thrown when a duplicate key is specified.
20 | ///
21 | ///
22 | /// The is ignored if it is applied to any
23 | /// other type of argument.
24 | ///
25 | ///
26 | ///
27 | ///
28 | [AttributeUsage(AttributeTargets.Property)]
29 | public sealed class AllowDuplicateDictionaryKeysAttribute : Attribute
30 | {
31 | }
32 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ApplicationFriendlyNameAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace Ookii.CommandLine;
5 |
6 | ///
7 | /// Sets the friendly name of the application to be used in the output of the "-Version"
8 | /// argument or "version" subcommand.
9 | ///
10 | ///
11 | ///
12 | /// This attribute is used when a "-Version" argument is automatically added to the arguments
13 | /// of your application, and by the automatically added "version" subcommand. It can be applied to
14 | /// the type defining command line arguments, or to the assembly that contains it.
15 | ///
16 | ///
17 | /// If not present, the automatic "-Version" argument and "version" command will use the
18 | /// value of the attribute, or the assembly name of the
19 | /// assembly containing the arguments type.
20 | ///
21 | ///
22 | /// For the "version" subcommand, this attribute must be applied to the entry point assembly of
23 | /// your application.
24 | ///
25 | ///
26 | ///
27 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)]
28 | public class ApplicationFriendlyNameAttribute : Attribute
29 | {
30 | private readonly string _name;
31 |
32 | ///
33 | /// Initializes a new instance of the
34 | /// attribute.
35 | ///
36 | /// The friendly name of the application.
37 | ///
38 | /// is .
39 | ///
40 | public ApplicationFriendlyNameAttribute(string name)
41 | {
42 | _name = name ?? throw new ArgumentNullException(nameof(name));
43 | }
44 |
45 | ///
46 | /// Gets the friendly name of the application.
47 | ///
48 | ///
49 | /// The friendly name of the application.
50 | ///
51 | public string Name => _name;
52 | }
53 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ArgumentKind.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Specifies what kind of argument an instance of the class
5 | /// represents.
6 | ///
7 | public enum ArgumentKind
8 | {
9 | ///
10 | /// A regular argument that can have only a single value.
11 | ///
12 | SingleValue,
13 | ///
14 | /// A multi-value argument.
15 | ///
16 | MultiValue,
17 | ///
18 | /// A dictionary argument, which is a multi-value argument where the values are key/value
19 | /// pairs with unique keys.
20 | ///
21 | Dictionary,
22 | ///
23 | /// An argument that invokes a method when specified.
24 | ///
25 | Method
26 | }
27 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ArgumentParsedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine;
4 |
5 | ///
6 | /// Provides data for the event.
7 | ///
8 | ///
9 | public class ArgumentParsedEventArgs : EventArgs
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The argument that has been parsed.
15 | /// is .
16 | public ArgumentParsedEventArgs(CommandLineArgument argument)
17 | {
18 | Argument = argument ?? throw new ArgumentNullException(nameof(argument));
19 | }
20 |
21 | ///
22 | /// Gets the argument that was parsed.
23 | ///
24 | ///
25 | /// The instance for the argument.
26 | ///
27 | public CommandLineArgument Argument { get; }
28 |
29 | ///
30 | /// Gets a value that indicates whether parsing should be canceled when the event handler
31 | /// returns.
32 | ///
33 | ///
34 | /// One of the values of the enumeration. The default value is the
35 | /// value of the
36 | /// property, or the return value of a method argument.
37 | ///
38 | ///
39 | ///
40 | /// If the event handler sets this property to a value other than ,
41 | /// command line processing will stop immediately, returning either or
42 | /// an instance of the arguments class according to the value.
43 | ///
44 | ///
45 | /// If you want usage help to be displayed after canceling, set the value to
46 | /// .
47 | ///
48 | ///
49 | ///
50 | ///
51 | ///
52 | public CancelMode CancelParsing { get; set; }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/BreakLineMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | internal enum BreakLineMode
4 | {
5 | Backward,
6 | Forward,
7 | Force
8 | }
9 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/CancelMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates whether and how an argument should cancel parsing.
5 | ///
6 | ///
7 | ///
8 | public enum CancelMode
9 | {
10 | ///
11 | /// The argument does not cancel parsing.
12 | ///
13 | None,
14 | ///
15 | /// The argument cancels parsing, discarding the results so far. Parsing, using for example the
16 | /// method, will
17 | /// return a value. The
18 | /// property will be . The
19 | /// property will be
20 | /// .
21 | ///
22 | Abort,
23 | ///
24 | /// The same as , but the property will be .
26 | ///
27 | AbortWithHelp,
28 | ///
29 | /// The argument cancels parsing, returning success using the results so far. Remaining
30 | /// arguments are not parsed, and will be available in the
31 | /// property. The property will be .
32 | /// If not all required arguments have values at this point, an exception will be thrown.
33 | ///
34 | Success,
35 | }
36 |
37 | static class CancelModeExtensions
38 | {
39 | public static bool IsAborted(this CancelMode self) => self is CancelMode.Abort or CancelMode.AbortWithHelp;
40 |
41 | public static bool HelpRequested(this CancelMode self) => self == CancelMode.AbortWithHelp;
42 | }
43 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/CategoryInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 |
4 | namespace Ookii.CommandLine;
5 |
6 | ///
7 | /// Provides information about an argument category.
8 | ///
9 | ///
10 | ///
11 | ///
12 | ///
13 | public readonly struct CategoryInfo
14 | {
15 | private readonly CommandLineParser _parser;
16 |
17 | internal CategoryInfo(CommandLineParser parser, Enum category)
18 | {
19 | _parser = parser;
20 | Category = category;
21 | }
22 |
23 | ///
24 | /// Gets the category.
25 | ///
26 | ///
27 | /// The category's enumeration value. The enumeration type depends on the type used with the
28 | /// property.
29 | ///
30 | public Enum Category { get; }
31 |
32 | ///
33 | /// Gets the description of the category, which is displayed in the usage help.
34 | ///
35 | ///
36 | /// A string with the description of the category.
37 | ///
38 | ///
39 | ///
40 | /// The description for an argument category can be specified by applying the
41 | /// attribute to the enumeration value. If no description
42 | /// specified, the string representation of the enumeration value is used.
43 | ///
44 | ///
45 | public string Description => _parser.GetCategoryDescription(Category);
46 | }
47 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/CommandLineArgumentErrorCategory.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Ookii.CommandLine;
3 |
4 | ///
5 | /// Specifies the kind of error that occurred while parsing arguments.
6 | ///
7 | public enum CommandLineArgumentErrorCategory
8 | {
9 | ///
10 | /// The category was not specified.
11 | ///
12 | Unspecified,
13 | ///
14 | /// The argument value supplied could not be converted to the type of the argument.
15 | ///
16 | ArgumentValueConversion,
17 | ///
18 | /// The argument name supplied does not name a known argument.
19 | ///
20 | UnknownArgument,
21 | ///
22 | /// An argument name was supplied, but without an accompanying value.
23 | ///
24 | MissingNamedArgumentValue,
25 | ///
26 | /// An argument was supplied more than once.
27 | ///
28 | DuplicateArgument,
29 | ///
30 | /// Too many positional arguments were supplied.
31 | ///
32 | TooManyArguments,
33 | ///
34 | /// Not all required arguments were supplied.
35 | ///
36 | MissingRequiredArgument,
37 | ///
38 | /// Invalid value for a dictionary argument; typically the result of a duplicate key.
39 | ///
40 | InvalidDictionaryValue,
41 | ///
42 | /// An error occurred creating an instance of the arguments type (e.g. the constructor threw an exception).
43 | ///
44 | CreateArgumentsTypeError,
45 | ///
46 | /// An error occurred applying the value of the argument (e.g. the property set accessor threw an exception).
47 | ///
48 | ApplyValueError,
49 | ///
50 | /// An argument value was after conversion from a string, and the argument type is a value
51 | /// type or (in .Net 6.0 and later) a non-nullable reference type.
52 | ///
53 | NullArgumentValue,
54 | ///
55 | /// A combined short argument contains an argument that is not a switch.
56 | ///
57 | CombinedShortNameNonSwitch,
58 | ///
59 | /// An instance of a class derived from the
60 | /// class failed to validate the argument.
61 | ///
62 | ValidationFailed,
63 | ///
64 | /// An argument failed a dependency check performed by the
65 | /// or the class.
66 | ///
67 | DependencyFailed,
68 | ///
69 | /// The provided argument name was a prefix of more than one argument name or alias.
70 | ///
71 | AmbiguousPrefixAlias
72 | }
73 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/AsyncCommandBase.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace Ookii.CommandLine.Commands;
5 |
6 | ///
7 | /// Base class for asynchronous commands that want the method to
8 | /// invoke the method.
9 | ///
10 | ///
11 | ///
12 | /// This class is provided for convenience for creating asynchronous commands without having to
13 | /// implement the method.
14 | ///
15 | ///
16 | ///
17 | public abstract class AsyncCommandBase : IAsyncCommand
18 | {
19 | ///
20 | /// Calls the method and waits synchronously for it to complete.
21 | ///
22 | /// The exit code of the command.
23 | public virtual int Run() => Task.Run(() => RunAsync(CancellationToken.None)).ConfigureAwait(false).GetAwaiter().GetResult();
24 |
25 | ///
26 | public abstract Task RunAsync(CancellationToken cancellationToken = default);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/AutomaticVersionCommand.cs:
--------------------------------------------------------------------------------
1 | using Ookii.CommandLine.Support;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Reflection;
5 |
6 | namespace Ookii.CommandLine.Commands;
7 |
8 | [Command]
9 | internal class AutomaticVersionCommand : ICommand
10 | {
11 | private class ArgumentProvider : GeneratedArgumentProvider
12 | {
13 | private readonly LocalizedStringProvider _stringProvider;
14 |
15 | public ArgumentProvider(LocalizedStringProvider stringProvider)
16 | : base(typeof(AutomaticVersionCommand), null, null, null, null)
17 | {
18 | _stringProvider = stringProvider;
19 | }
20 |
21 | public override bool IsCommand => true;
22 |
23 | public override string Description => _stringProvider.AutomaticVersionCommandDescription();
24 |
25 | public override string UsageFooter => string.Empty;
26 |
27 | public override object CreateInstance(CommandLineParser parser, object?[]? requiredPropertyValues) => new AutomaticVersionCommand(parser);
28 |
29 | public override IEnumerable GetArguments(CommandLineParser parser)
30 | {
31 | yield break;
32 | }
33 | }
34 |
35 | private readonly CommandLineParser _parser;
36 |
37 | public AutomaticVersionCommand(CommandLineParser parser)
38 | {
39 | _parser = parser;
40 | }
41 |
42 | public int Run()
43 | {
44 | var assembly = Assembly.GetEntryAssembly();
45 | if (assembly == null)
46 | {
47 | Console.WriteLine(Properties.Resources.UnknownVersion);
48 | return 1;
49 | }
50 |
51 | // We can't use _parser.ApplicationFriendlyName because we're interested in the entry
52 | // assembly, not the one containing this command.
53 | var attribute = assembly.GetCustomAttribute();
54 | var friendlyName = attribute?.Name ?? assembly.GetName().Name ?? string.Empty;
55 | CommandLineArgument.ShowVersion(_parser.StringProvider, assembly, friendlyName);
56 | return 0;
57 | }
58 |
59 | public static CommandLineParser CreateParser(ParseOptions options)
60 | => new(new ArgumentProvider(options.StringProvider), options);
61 | }
62 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/AutomaticVersionCommandInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Ookii.CommandLine.Commands;
6 |
7 | internal class AutomaticVersionCommandInfo : CommandInfo
8 | {
9 | public AutomaticVersionCommandInfo(CommandManager manager)
10 | : base(typeof(AutomaticVersionCommand), manager.Options.AutoVersionCommandName(), manager)
11 | {
12 | }
13 |
14 | public override string? Description => Manager.Options.StringProvider.AutomaticVersionCommandDescription();
15 |
16 | public override bool UseCustomArgumentParsing => false;
17 |
18 | public override IEnumerable Aliases => [];
19 |
20 | public override ICommandWithCustomParsing CreateInstanceWithCustomParsing()
21 | => throw new InvalidOperationException(Properties.Resources.NoCustomParsing);
22 |
23 | public override CommandLineParser CreateParser()
24 | => AutomaticVersionCommand.CreateParser(Manager.Options);
25 | }
26 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/GeneratedCommandManagerAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Ookii.CommandLine.Commands;
5 |
6 | ///
7 | /// Indicates that the target class is a command manager created using source generation.
8 | ///
9 | ///
10 | ///
11 | /// When using this attribute, source generation is used to determine which classes are available
12 | /// at compile time, either in the assembly being compiled, or the assemblies specified using the
13 | /// property. The target class will be modified to inherit from the
14 | /// class, and should be used instead of the
15 | /// class to find, create, and run commands.
16 | ///
17 | ///
18 | /// Using a class with this attribute avoids the use of runtime reflection to determine which
19 | /// commands are available, improving performance and allowing your application to be trimmed.
20 | ///
21 | ///
22 | /// To use source generation for the command line arguments of individual commands, use the
23 | /// attribute on each command class.
24 | ///
25 | ///
26 | ///
27 | /// Source generation
28 | [AttributeUsage(AttributeTargets.Class, Inherited = false)]
29 | public sealed class GeneratedCommandManagerAttribute : Attribute
30 | {
31 | ///
32 | /// Gets or sets the names of the assemblies that contain the commands that the generated
33 | /// will use.
34 | ///
35 | ///
36 | /// An array with assembly names, or to use the commands from the
37 | /// assembly containing the generated manager.
38 | ///
39 | ///
40 | ///
41 | /// The assemblies used must be directly referenced by your project. Dynamically loading
42 | /// assemblies is not supported by this attribute; use the
43 | ///
44 | /// constructor instead for that purpose.
45 | ///
46 | ///
47 | /// The names in this array can be either just the assembly name, or the full assembly
48 | /// identity including version, culture, and public key token.
49 | ///
50 | ///
51 | public string[]? AssemblyNames { get; set; }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/IAsyncCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace Ookii.CommandLine.Commands;
5 |
6 | ///
7 | /// Represents a subcommand that executes asynchronously.
8 | ///
9 | ///
10 | ///
11 | /// This interface adds a method to the
12 | /// interface, that will be invoked by the
13 | ///
14 | /// method and its overloads. This allows you to write tasks that use asynchronous code.
15 | ///
16 | ///
17 | /// Use the class as a base class for your command to get a default
18 | /// implementation of the
19 | ///
20 | ///
21 | public interface IAsyncCommand : ICommand
22 | {
23 | ///
24 | /// Runs the command asynchronously.
25 | ///
26 | ///
27 | /// The token to monitor for cancellation requests. The default value is
28 | /// .
29 | ///
30 | ///
31 | /// A task that represents the asynchronous run operation. The result of the task is the
32 | /// exit code for the command.
33 | ///
34 | ///
35 | ///
36 | /// Typically, your application's Main() method should return the exit code of the
37 | /// command that was executed.
38 | ///
39 | ///
40 | /// This method will only be invoked if you run commands with the
41 | ///
42 | /// method or one of its overloads. Typically, it's recommended to implement the
43 | /// method to invoke this method and wait for
44 | /// it. Use the class for a default implementation that does
45 | /// this.
46 | ///
47 | ///
48 | /// If a was passed to the
49 | /// method,
50 | /// the parameter will be set to that token. Otherwise,
51 | /// the value will be .
52 | ///
53 | ///
54 | Task RunAsync(CancellationToken cancellationToken = default);
55 | }
56 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/ICommand.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine.Commands;
2 |
3 | ///
4 | /// Represents a subcommand of the application.
5 | ///
6 | ///
7 | ///
8 | /// To create a subcommand for your application, create a class that implements this interface,
9 | /// then apply the attribute to it.
10 | ///
11 | ///
12 | /// The class will be used as an arguments type with the .
13 | /// Alternatively, a command can implement its own argument parsing by implementing the
14 | /// interface.
15 | ///
16 | ///
17 | ///
18 | public interface ICommand
19 | {
20 | ///
21 | /// Runs the command.
22 | ///
23 | /// The exit code for the command.
24 | ///
25 | ///
26 | /// Typically, your application's Main() method should return the exit code of the
27 | /// command that was executed.
28 | ///
29 | ///
30 | int Run();
31 | }
32 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Commands/ICommandWithCustomParsing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine.Commands;
4 |
5 | ///
6 | /// Represents a subcommand that does its own argument parsing.
7 | ///
8 | ///
9 | ///
10 | /// Unlike commands that only implement the interface, commands that
11 | /// implement the interface are not created with the
12 | /// class. Instead, they must have a public constructor with no
13 | /// parameters, and must parse the arguments manually by implementing the
14 | /// method.
15 | ///
16 | ///
17 | ///
18 | public interface ICommandWithCustomParsing : ICommand
19 | {
20 | ///
21 | /// Parses the arguments for the command.
22 | ///
23 | /// The arguments for the command.
24 | /// The that was used to create this command.
25 | void Parse(ReadOnlyMemory args, CommandManager manager);
26 | }
27 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/ArgumentConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Ookii.CommandLine.Conversion;
5 |
6 | ///
7 | /// Base class for converters from a string to the type of an argument.
8 | ///
9 | ///
10 | ///
11 | /// To create a custom argument converter, you must implement the
12 | /// method.
13 | ///
14 | ///
15 | /// The source of the conversion is a that
16 | /// contains the argument text. This may be an entire argument, or a
17 | /// substring of an argument if the user used a non-whitespace argument name
18 | /// separator like -Name:Value.
19 | ///
20 | ///
21 | /// is used because
22 | /// does not
23 | /// allocate when the memory is an entire string. However, since it still
24 | /// allocates for substrings, it's still recommended to convert using the
25 | /// or directly
26 | /// if possible.
27 | ///
28 | ///
29 | ///
30 | public abstract class ArgumentConverter
31 | {
32 | ///
33 | /// Converts a string memory region to the type of the argument.
34 | ///
35 | ///
36 | /// The containing the string to convert.
37 | ///
38 | /// The culture to use for the conversion.
39 | ///
40 | /// The that will use the converted value.
41 | ///
42 | /// An object representing the converted value.
43 | ///
44 | /// or is .
45 | ///
46 | ///
47 | /// The value was not in a correct format for the target type.
48 | ///
49 | ///
50 | /// The value was out of range for the target type.
51 | ///
52 | ///
53 | /// The value was not in a correct format for the target type. Unlike other exceptions,
54 | /// which will be wrapped in a , a
55 | /// thrown by this method will be passed down to
56 | /// the user unmodified.
57 | ///
58 | public abstract object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument);
59 | }
60 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/BooleanConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Ookii.CommandLine.Conversion;
5 |
6 | ///
7 | /// Converter for arguments with values. These are typically switch arguments.
8 | ///
9 | ///
10 | ///
11 | /// For a switch argument, the converter is only used if the value was explicitly specified.
12 | ///
13 | ///
14 | ///
15 | public class BooleanConverter : ArgumentConverter
16 | {
17 | ///
18 | /// A default instance of the converter.
19 | ///
20 | public static readonly BooleanConverter Instance = new();
21 |
22 | ///
23 | /// Converts a string memory region to a .
24 | ///
25 | /// The containing the string to convert.
26 | /// The culture to use for the conversion.
27 | ///
28 | /// The that will use the converted value.
29 | ///
30 | /// An object representing the converted value.
31 | ///
32 | ///
33 | /// This method performs the conversion using the method.
34 | ///
35 | ///
36 | ///
37 | /// The value was not in a correct format for the target type.
38 | ///
39 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
40 | #if NET6_0_OR_GREATER
41 | => bool.Parse(value.Span);
42 | #else
43 | => bool.Parse(value.ToString());
44 | #endif
45 | }
46 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/ConstructorConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Globalization;
4 | using System.Reflection;
5 | using System.Runtime.ExceptionServices;
6 |
7 | namespace Ookii.CommandLine.Conversion;
8 |
9 | internal class ConstructorConverter : ArgumentConverter
10 | {
11 | #if NET6_0_OR_GREATER
12 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
13 | #endif
14 | private readonly Type _type;
15 |
16 | public ConstructorConverter(
17 | #if NET6_0_OR_GREATER
18 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
19 | #endif
20 | Type type)
21 | {
22 | _type = type;
23 | }
24 |
25 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
26 | {
27 | try
28 | {
29 | // Since we are passing BindingFlags.Public, the correct annotation is present.
30 | return Activator.CreateInstance(_type, value.ToString());
31 | }
32 | catch (TargetInvocationException ex)
33 | {
34 | if (ex.InnerException == null)
35 | {
36 | throw;
37 | }
38 |
39 | // Rethrow inner exception with original call stack.
40 | ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
41 |
42 | // Actually unreachable.
43 | throw;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/GeneratedConverterNamespaceAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine.Conversion;
4 |
5 | ///
6 | /// Sets the namespace to use for argument converters generated for arguments classes with the
7 | /// attribute.
8 | ///
9 | ///
10 | ///
11 | /// To convert argument types for which no built-in non-reflection argument converter exists,
12 | /// such as classes that have a constructor taking a parameter, or those
13 | /// that have a Parse method but don't implement , the source
14 | /// generator will create a new argument converter. The generated converter class will be
15 | /// internal to the assembly containing the generated parser, and will be placed in the namespace
16 | /// Ookii.CommandLine.Conversion.Generated by default.
17 | ///
18 | ///
19 | /// Use this attribute to modify the namespace used.
20 | ///
21 | ///
22 | ///
23 | [AttributeUsage(AttributeTargets.Assembly)]
24 | public sealed class GeneratedConverterNamespaceAttribute : Attribute
25 | {
26 | ///
27 | /// Initializes a new instance of the class
28 | /// with the specified namespace.
29 | ///
30 | /// The namespace to use.
31 | ///
32 | /// is .
33 | ///
34 | public GeneratedConverterNamespaceAttribute(string @namespace)
35 | {
36 | Namespace = @namespace ?? throw new ArgumentNullException(nameof(@namespace));
37 | }
38 |
39 | ///
40 | /// Gets the namespace to use for generated argument converters.
41 | ///
42 | ///
43 | /// The full name of the namespace.
44 | ///
45 | public string Namespace { get; }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/KeyValueSeparatorAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine.Conversion;
4 |
5 | ///
6 | /// Defines a custom key/value separator for dictionary arguments.
7 | ///
8 | ///
9 | ///
10 | /// By default, dictionary arguments use the equals sign ('=') as a separator. By using this
11 | /// attribute, you can choose a custom separator. This separator cannot appear in the key,
12 | /// but can appear in the value.
13 | ///
14 | ///
15 | /// This attribute is ignored if the dictionary argument uses the
16 | /// attribute, or if the argument is not a dictionary argument.
17 | ///
18 | ///
19 | ///
20 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
21 | public class KeyValueSeparatorAttribute : Attribute
22 | {
23 | private readonly string _separator;
24 |
25 | ///
26 | /// Initializes a new instance of the class.
27 | ///
28 | /// The separator to use.
29 | /// is .
30 | /// is an empty string.
31 | public KeyValueSeparatorAttribute(string separator)
32 | {
33 | if (separator == null)
34 | {
35 | throw new ArgumentNullException(nameof(separator));
36 | }
37 |
38 | if (separator.Length == 0)
39 | {
40 | throw new ArgumentException(Properties.Resources.EmptyKeyValueSeparator, nameof(separator));
41 | }
42 |
43 | _separator = separator;
44 | }
45 |
46 | ///
47 | /// Gets the separator.
48 | ///
49 | ///
50 | /// The separator.
51 | ///
52 | public string Separator => _separator;
53 | }
54 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/NullableConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Globalization;
4 |
5 | namespace Ookii.CommandLine.Conversion;
6 |
7 | ///
8 | /// Converts from a string to a .
9 | ///
10 | ///
11 | ///
12 | /// This converter uses the specified converter for the type T, except when the input is an
13 | /// empty string, in which case it return . This parallels the behavior
14 | /// of the .
15 | ///
16 | ///
17 | ///
18 | public class NullableConverter : ArgumentConverter
19 | {
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | /// The converter to use for the target type.
24 | ///
25 | /// is .
26 | ///
27 | public NullableConverter(ArgumentConverter baseConverter)
28 | {
29 | BaseConverter = baseConverter ?? throw new ArgumentNullException(nameof(baseConverter));
30 | }
31 |
32 | ///
33 | /// Gets the converter for the underlying type.
34 | ///
35 | ///
36 | /// The for the underlying type.
37 | ///
38 | public ArgumentConverter BaseConverter { get; }
39 |
40 | ///
41 | ///
42 | /// An object representing the converted value, or if the value was an
43 | /// empty string span.
44 | ///
45 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
46 | {
47 | if (value.Length == 0)
48 | {
49 | return null;
50 | }
51 |
52 | return BaseConverter.Convert(value, culture, argument);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/ParsableConverter.cs:
--------------------------------------------------------------------------------
1 | #if NET7_0_OR_GREATER
2 |
3 | using System;
4 | using System.Globalization;
5 |
6 | namespace Ookii.CommandLine.Conversion;
7 |
8 | ///
9 | /// An argument converter for types that implement the interface.
10 | ///
11 | /// The type to convert to.
12 | ///
13 | ///
14 | /// Conversion is performed using the method.
15 | ///
16 | ///
17 | /// Only use this converter for types that implement the interface,
18 | /// but not the interface. For types that implement the
19 | /// interface, use the
20 | /// class.
21 | ///
22 | ///
23 | ///
24 | public class ParsableConverter : ArgumentConverter
25 | where T : IParsable
26 | {
27 | ///
28 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
29 | => T.Parse(value.ToString(), culture);
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/ParseConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Reflection;
4 | using System.Runtime.ExceptionServices;
5 |
6 | namespace Ookii.CommandLine.Conversion;
7 |
8 | internal class ParseConverter : ArgumentConverter
9 | {
10 | private readonly MethodInfo _method;
11 | private readonly bool _hasCulture;
12 |
13 | public ParseConverter(MethodInfo method, bool hasCulture)
14 | {
15 | _method = method;
16 | _hasCulture = hasCulture;
17 | }
18 |
19 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
20 | {
21 | object[] parameters = _hasCulture
22 | ? [value.ToString(), culture]
23 | : [value.ToString()];
24 |
25 | try
26 | {
27 | return _method.Invoke(null, parameters);
28 | }
29 | catch (TargetInvocationException ex)
30 | {
31 | if (ex.InnerException == null)
32 | {
33 | throw;
34 | }
35 |
36 | // Rethrow inner exception with original call stack.
37 | ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
38 |
39 | // Actually unreachable.
40 | throw;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/SpanParsableConverter.cs:
--------------------------------------------------------------------------------
1 | #if NET7_0_OR_GREATER
2 |
3 | using System;
4 | using System.Globalization;
5 |
6 | namespace Ookii.CommandLine.Conversion;
7 |
8 | ///
9 | /// An argument converter for types that implement the interface.
10 | ///
11 | /// The type to convert to.
12 | ///
13 | ///
14 | /// Conversion is performed using the
15 | /// method.
16 | ///
17 | ///
18 | /// For types that implement the interface, but not the
19 | /// interface, use the class.
20 | ///
21 | ///
22 | ///
23 | public class SpanParsableConverter : ArgumentConverter
24 | where T : ISpanParsable
25 | {
26 | ///
27 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
28 | => T.Parse(value.Span, culture);
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/StringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Ookii.CommandLine.Conversion;
5 |
6 | ///
7 | /// A converter for arguments with string values.
8 | ///
9 | ///
10 | /// This converter does not perform any actual conversion, and returns the existing string as-is.
11 | /// If the input was a for , a new string is
12 | /// allocated for it.
13 | ///
14 | ///
15 | public class StringConverter : ArgumentConverter
16 | {
17 | ///
18 | /// A default instance of the converter.
19 | ///
20 | public static readonly StringConverter Instance = new();
21 |
22 | ///
23 | /// Converts a string memory region to a string.
24 | ///
25 | /// The string memory region to convert.
26 | /// The culture to use for the conversion.
27 | ///
28 | /// The that will use the converted value.
29 | ///
30 | /// The value of the parameter as a string.
31 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
32 | => value.ToString();
33 | }
34 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/WrappedDefaultTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace Ookii.CommandLine.Conversion;
5 |
6 | ///
7 | /// An that wraps the default for a
8 | /// type.
9 | ///
10 | /// The type to convert to.
11 | ///
12 | /// This class will convert argument values from a string using the default
13 | /// for the type . If you wish to use a specific custom ,
14 | /// use the class instead.
15 | ///
16 | ///
17 | #if NET6_0_OR_GREATER
18 | [RequiresUnreferencedCode("Determining the TypeConverter for a type may require the type to be annotated.")]
19 | #endif
20 | public class WrappedDefaultTypeConverter : WrappedTypeConverter
21 | {
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | public WrappedDefaultTypeConverter()
26 | : base(TypeDescriptor.GetConverter(typeof(T)))
27 | {
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/WrappedTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Globalization;
4 |
5 | namespace Ookii.CommandLine.Conversion;
6 |
7 | ///
8 | /// An that wraps an existing .
9 | ///
10 | ///
11 | ///
12 | /// For a convenient way to use to use any with the
13 | /// attribute, use the
14 | /// class. To use the default for a type, use the
15 | /// class.
16 | ///
17 | ///
18 | ///
19 | public class WrappedTypeConverter : ArgumentConverter
20 | {
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | /// The to use.
25 | ///
26 | /// is .
27 | ///
28 | ///
29 | /// The specified by cannot convert
30 | /// from a .
31 | ///
32 | public WrappedTypeConverter(TypeConverter converter)
33 | {
34 | Converter = converter ?? throw new ArgumentNullException(nameof(converter));
35 | if (!converter.CanConvertFrom(typeof(string)))
36 | {
37 | throw new ArgumentException(Properties.Resources.InvalidTypeConverter, nameof(converter));
38 | }
39 | }
40 |
41 | ///
42 | /// Gets the type converter used by this instance.
43 | ///
44 | ///
45 | /// An instance of the class.
46 | ///
47 | public TypeConverter Converter { get; }
48 |
49 | ///
50 | public override object? Convert(ReadOnlyMemory value, CultureInfo culture, CommandLineArgument argument)
51 | => Converter.ConvertFromString(null, culture, value.ToString());
52 | }
53 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Conversion/WrappedTypeConverterGeneric.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace Ookii.CommandLine.Conversion;
4 |
5 | ///
6 | /// An that wraps an existing .
7 | ///
8 | /// The type of the to wrap.
9 | ///
10 | ///
11 | /// This class will convert argument values from a string using the
12 | /// class . If you wish to use the default
13 | /// for a type, use the class instead.
14 | ///
15 | ///
16 | public class WrappedTypeConverter : WrappedTypeConverter
17 | where T : TypeConverter, new()
18 | {
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | public WrappedTypeConverter()
23 | : base(new T())
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Convert-SyncMethod.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter(Mandatory = $true)][string[]]$Path,
3 | [Parameter(Mandatory = $true)][string]$OutputDir
4 | )
5 |
6 | # This script takes async methods and makes the following replacements to generate a non-async
7 | # version of that method.
8 | $replacements = @(
9 | @("Async", ""), # Remove Async from identifiers
10 | @("async Task", "partial void"), # Function signature change
11 | @("await ", ""), # Remove await keyword
12 | @(" is (ReadOnlyMemory, ReadOnlyMemory) splits", ".TryGetValue(out var splits)"), # Replace pattern matching with NullableReadOnlySpan.TryGetValue
13 | @("ReadOnlyMemory", "ReadOnlySpan"), # Async stream functions uses Memory instead of span
14 | @(".Span", ""), # Needed to convert Memory usage to Span.
15 | @("async ", ""), # Remove keyword from async lambda
16 | @(", CancellationToken cancellationToken = default", ""), # Remove cancellation token parameter
17 | @(", CancellationToken cancellationToken", ""), # Remove cancellation token parameter
18 | @("(CancellationToken cancellationToken)", "()"), # Remove cancellation token parameter
19 | @(", cancellationToken", ""), # Remove cancellation token parameter value
20 | @("(cancellationToken)", "()") # Remove cancellation token parameter value
21 | )
22 |
23 | $files = Get-Item $Path
24 | foreach ($file in $files)
25 | {
26 | $outputPath = Join-Path $OutputDir ($file.Name.Replace("Async", "Sync"))
27 |
28 | &{
29 | "// "
30 | Get-Content $file | ForEach-Object {
31 | # Regex replace generic Task before the other replacements.
32 | $result = ($_ -creplace 'async Task<(.*?)>','partial $1')
33 | foreach ($item in $replacements) {
34 | $result = $result.Replace($item[0], $item[1])
35 | }
36 |
37 | $result
38 | }
39 | } | Set-Content $outputPath
40 | }
41 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/DescriptionListFilterMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates which arguments should be included in the description list when generating usage help.
5 | ///
6 | ///
7 | public enum DescriptionListFilterMode
8 | {
9 | ///
10 | /// Include arguments that have any information that is not included in the syntax,
11 | /// such as aliases, a default value, or a description.
12 | ///
13 | Information,
14 | ///
15 | /// Include only arguments that have a description.
16 | ///
17 | Description,
18 | ///
19 | /// Include all arguments.
20 | ///
21 | All,
22 | ///
23 | /// Omit the description list entirely.
24 | ///
25 | None
26 | }
27 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/DescriptionListSortMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates how the arguments in the description list should be sorted when generating usage help.
5 | ///
6 | ///
7 | public enum DescriptionListSortMode
8 | {
9 | ///
10 | /// The descriptions are listed in the same order as the usage syntax: first the positional
11 | /// arguments, then the non-positional required named arguments sorted by name, then the
12 | /// remaining arguments sorted by name.
13 | ///
14 | UsageOrder,
15 | ///
16 | /// The descriptions are listed in alphabetical order by argument name. If the parsing mode
17 | /// is , this uses the long name of the argument, unless
18 | /// the argument has no long name, in which case the short name is used.
19 | ///
20 | Alphabetical,
21 | ///
22 | /// The same as , but in reverse order.
23 | ///
24 | AlphabeticalDescending,
25 | ///
26 | /// The descriptions are listed in alphabetical order by the short argument name. If the
27 | /// argument has no short name, the long name is used. If the parsing mode is not
28 | /// , this has the same effect as .
29 | ///
30 | AlphabeticalShortName,
31 | ///
32 | /// The same as , but in reverse order.
33 | ///
34 | AlphabeticalShortNameDescending,
35 | }
36 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/DictionaryArgumentInfo.cs:
--------------------------------------------------------------------------------
1 | using Ookii.CommandLine.Conversion;
2 | using System;
3 |
4 | namespace Ookii.CommandLine;
5 |
6 | ///
7 | /// Provides information that only applies to dictionary arguments.
8 | ///
9 | ///
10 | public sealed class DictionaryArgumentInfo
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | ///
16 | /// if duplicate dictionary keys are allowed; otherwise,
17 | /// .
18 | ///
19 | /// The type of the dictionary's keys.
20 | /// The type of the dictionary's values.
21 | /// The separator between the keys and values.
22 | ///
23 | /// or or
24 | /// is .
25 | ///
26 | public DictionaryArgumentInfo(bool allowDuplicateKeys, Type keyType, Type valueType, string keyValueSeparator)
27 | {
28 | AllowDuplicateKeys = allowDuplicateKeys;
29 | KeyType = keyType ?? throw new ArgumentNullException(nameof(keyType));
30 | ValueType = valueType ?? throw new ArgumentNullException(nameof(valueType));
31 | KeyValueSeparator = keyValueSeparator ?? throw new ArgumentNullException(nameof(keyValueSeparator));
32 | }
33 |
34 | ///
35 | /// Gets a value indicating whether this argument allows duplicate keys.
36 | ///
37 | ///
38 | /// if this argument allows duplicate keys; otherwise, .
39 | ///
40 | ///
41 | public bool AllowDuplicateKeys { get; }
42 |
43 | ///
44 | /// Gets the type of the keys of a dictionary argument.
45 | ///
46 | ///
47 | /// The of the keys in the dictionary.
48 | ///
49 | public Type KeyType { get; }
50 |
51 | ///
52 | /// Gets the type of the values of a dictionary argument.
53 | ///
54 | ///
55 | /// The of the values in the dictionary.
56 | ///
57 | public Type ValueType { get; }
58 |
59 | ///
60 | /// Gets the separator for key/value pairs.
61 | ///
62 | ///
63 | /// The custom value specified using the attribute, or
64 | /// if no attribute was
65 | /// present.
66 | ///
67 | ///
68 | public string KeyValueSeparator { get; }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/DisposableWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine;
4 |
5 | internal static class DisposableWrapper
6 | {
7 | public static DisposableWrapper Create(T? obj, Func createIfNull)
8 | where T : IDisposable
9 | {
10 | return new DisposableWrapper(obj, createIfNull);
11 | }
12 | }
13 |
14 | ///
15 | /// Helper to either use an existing instance (and not dispose it), or create an instance
16 | /// and dispose it.
17 | ///
18 | ///
19 | internal class DisposableWrapper : IDisposable
20 | where T : IDisposable
21 | {
22 | private readonly T _inner;
23 | private bool _needDispose;
24 |
25 | public DisposableWrapper(T? inner, Func createIfNull)
26 | {
27 | if (inner == null)
28 | {
29 | _inner = createIfNull();
30 | _needDispose = true;
31 | }
32 | else
33 | {
34 | _inner = inner;
35 | }
36 | }
37 |
38 | public T Inner => _inner;
39 |
40 | protected virtual void Dispose(bool disposing)
41 | {
42 | if (_needDispose)
43 | {
44 | if (disposing)
45 | {
46 | _inner.Dispose();
47 | }
48 |
49 | _needDispose = false;
50 | }
51 | }
52 |
53 | public void Dispose()
54 | {
55 | Dispose(disposing: true);
56 | GC.SuppressFinalize(this);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/DuplicateArgumentEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine;
4 |
5 | ///
6 | /// Provides data for the event.
7 | ///
8 | ///
9 | public class DuplicateArgumentEventArgs : EventArgs
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The argument that was specified more than once.
15 | ///
16 | /// The raw new value of the argument, or if no value was given.
17 | ///
18 | ///
19 | /// is
20 | ///
21 | public DuplicateArgumentEventArgs(CommandLineArgument argument, ReadOnlyMemory? newValue)
22 | {
23 | Argument = argument ?? throw new ArgumentNullException(nameof(argument));
24 | NewValue = newValue;
25 | }
26 |
27 | ///
28 | /// Gets the argument that was specified more than once.
29 | ///
30 | ///
31 | /// The that was specified more than once.
32 | ///
33 | public CommandLineArgument Argument { get; }
34 |
35 | ///
36 | /// Gets the new value that will be assigned to the argument.
37 | ///
38 | ///
39 | /// The raw string value provided on the command line, before conversion, or
40 | /// if the argument is a switch argument that was provided without an explicit value.
41 | ///
42 | public ReadOnlyMemory? NewValue { get; }
43 |
44 | ///
45 | /// Gets or sets a value that indicates whether the value of the argument should stay
46 | /// unmodified.
47 | ///
48 | ///
49 | /// to preserve the current value of the argument, or
50 | /// to replace it with the value of the property. The default value
51 | /// is .
52 | ///
53 | public bool KeepOldValue { get; set; }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ErrorMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates whether something is an error, warning, or allowed.
5 | ///
6 | ///
7 | public enum ErrorMode
8 | {
9 | ///
10 | /// The operation should raise an error.
11 | ///
12 | Error,
13 | ///
14 | /// The operation should display a warning, but continue.
15 | ///
16 | Warning,
17 | ///
18 | /// The operation should continue silently.
19 | ///
20 | Allow,
21 | }
22 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/IParserProvider.cs:
--------------------------------------------------------------------------------
1 | #if NET7_0_OR_GREATER
2 |
3 | using System;
4 |
5 | namespace Ookii.CommandLine;
6 |
7 | ///
8 | /// Defines a mechanism to create a for a type.
9 | ///
10 | /// The type that implements this interface.
11 | ///
12 | ///
13 | /// This type is only available when using .Net 7 or later.
14 | ///
15 | ///
16 | /// This interface is automatically implemented on a class when the
17 | /// is used. Classes without that attribute must create
18 | /// the parser directly by using the
19 | /// constructor; these classes do not support this interface unless it is manually implemented.
20 | ///
21 | ///
22 | /// When using a version of .Net where static interface methods are not supported (versions prior
23 | /// to .Net 7.0), the will still generate the same method
24 | /// as defined by this interface, just without having it implement the interface.
25 | ///
26 | ///
27 | public interface IParserProvider
28 | where TSelf : class, IParserProvider
29 | {
30 | ///
31 | /// Creates a instance using the specified options.
32 | ///
33 | ///
34 | /// The options that control parsing behavior, or to use the
35 | /// default options.
36 | ///
37 | ///
38 | /// An instance of the class for the type
39 | /// .
40 | ///
41 | ///
42 | /// The cannot use type as the
43 | /// command line arguments type, because it violates one of the rules concerning argument
44 | /// names or positions. Even when the parser was generated using the
45 | /// class, not all those rules can be checked at compile time.
46 | ///
47 | ///
48 | ///
49 | /// This method is typically generated for a class that defines command line arguments by
50 | /// the attribute.
51 | ///
52 | ///
53 | ///
54 | public static abstract CommandLineParser CreateParser(ParseOptions? options = null);
55 | }
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/MultiValueArgumentInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Ookii.CommandLine;
8 |
9 | ///
10 | /// Provides information that only applies to multi-value and dictionary arguments.
11 | ///
12 | ///
13 | public sealed class MultiValueArgumentInfo
14 | {
15 | ///
16 | /// Creates a new instance of the class.
17 | ///
18 | ///
19 | /// The separator between multiple values in the same token, or if no
20 | /// separator is used.
21 | ///
22 | ///
23 | /// if the argument can consume multiple tokens; otherwise,
24 | /// .
25 | ///
26 | public MultiValueArgumentInfo(string? separator, bool allowWhiteSpaceSeparator)
27 | {
28 | Separator = separator;
29 | AllowWhiteSpaceSeparator = allowWhiteSpaceSeparator;
30 | }
31 |
32 |
33 | ///
34 | /// Gets the separator that can be used to supply multiple values in a single argument token.
35 | ///
36 | ///
37 | /// The separator, or if no separator is used.
38 | ///
39 | ///
40 | public string? Separator { get; }
41 |
42 | ///
43 | /// Gets a value that indicates whether or not the argument can consume multiple following
44 | /// argument tokens.
45 | ///
46 | ///
47 | /// if the argument consume multiple following tokens; otherwise,
48 | /// .
49 | ///
50 | ///
51 | ///
52 | /// A multi-value argument that allows white-space separators is able to consume multiple
53 | /// values from the command line that follow it. All values that follow the name, up until
54 | /// the next argument name, are considered values for this argument.
55 | ///
56 | ///
57 | ///
58 | public bool AllowWhiteSpaceSeparator { get; internal set; }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/NameTransform.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates how to transform the argument name, subcommand name, or value description if they are
5 | /// not explicitly specified but automatically derived from the member or type name.
6 | ///
7 | ///
8 | ///
9 | ///
10 | ///
11 | ///
12 | public enum NameTransform
13 | {
14 | ///
15 | /// The names are used without modification.
16 | ///
17 | None,
18 | ///
19 | /// The names are transformed to PascalCase. This removes all underscores, and the first
20 | /// character, and every character after an underscore, is changed to uppercase. The case of
21 | /// other characters is not changed.
22 | ///
23 | PascalCase,
24 | ///
25 | /// The names are transformed to camelCase. Similar to , but the
26 | /// first character will not be uppercase.
27 | ///
28 | CamelCase,
29 | ///
30 | /// The names are transformed to dash-case. This removes leading and trailing underscores,
31 | /// changes all characters to lower-case, replaces underscores with a dash, and reduces
32 | /// consecutive underscores to a single dash. A dash is inserted before previously
33 | /// capitalized letters.
34 | ///
35 | DashCase,
36 | ///
37 | /// The names are transformed to snake_case. Similar to , but uses an
38 | /// underscore instead of a dash.
39 | ///
40 | SnakeCase
41 | }
42 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/NativeMethods.txt:
--------------------------------------------------------------------------------
1 | CONSOLE_MODE
2 | STD_HANDLE
3 | GetConsoleMode
4 | GetStdHandle
5 | SetConsoleMode
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/PackageReadme.md:
--------------------------------------------------------------------------------
1 | # Ookii.CommandLine
2 |
3 | Ookii.CommandLine is a powerful, flexible and highly customizable command line argument parsing
4 | library for .Net applications.
5 |
6 | - Easily define arguments by creating a class with properties.
7 | - Create applications with multiple subcommands.
8 | - Generate fully customizable usage help.
9 | - Supports PowerShell-like and POSIX-like parsing rules.
10 | - Compatible with trimming and native AOT.
11 |
12 | Two styles of command line parsing rules are supported: the default mode uses rules similar to those
13 | used by PowerShell, and the alternative long/short mode uses a style influenced by POSIX
14 | conventions, where arguments have separate long and short names with different prefixes. Many
15 | aspects of the parsing rules are configurable.
16 |
17 | To determine which arguments are accepted, you create a class, with properties and methods that
18 | define the arguments. Attributes are used to specify names, create required or positional arguments,
19 | and to specify descriptions for use in the generated usage help.
20 |
21 | For example, the following class defines four arguments: a required positional argument, an optional
22 | positional argument, a named-only argument, and a switch argument (sometimes also called a flag):
23 |
24 | ```csharp
25 | [GeneratedParser]
26 | partial class MyArguments
27 | {
28 | [CommandLineArgument(IsPositional = true)]
29 | [Description("A required positional argument.")]
30 | public required string Required { get; set; }
31 |
32 | [CommandLineArgument(IsPositional = true)]
33 | [Description("An optional positional argument.")]
34 | public int Optional { get; set; } = 42;
35 |
36 | [CommandLineArgument]
37 | [Description("An argument that can only be supplied by name.")]
38 | public DateTime Named { get; set; }
39 |
40 | [CommandLineArgument]
41 | [Description("A switch argument, which doesn't require a value.")]
42 | public bool Switch { get; set; }
43 | }
44 | ```
45 |
46 | Each argument has a different type that determines the kinds of values it can accept.
47 |
48 | > If you are using an older version of .Net where the `required` keyword is not available, you can
49 | > use `[CommandLineArgument(IsRequired = true)]` to create a required argument instead.
50 |
51 | To parse these arguments, all you have to do is add the following line to your `Main` method:
52 |
53 | ```csharp
54 | var arguments = MyArguments.Parse();
55 | ```
56 |
57 | In addition, Ookii.CommandLine can be used to create applications that have multiple subcommands,
58 | each with their own arguments.
59 |
60 | For more information, including a tutorial and samples, see the [full documentation on GitHub](https://github.com/SvenGroot/Ookii.CommandLine).
61 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ParseStatus.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates the status of the last call to the
5 | /// method or one of its overloads.
6 | ///
7 | ///
8 | public enum ParseStatus
9 | {
10 | ///
11 | /// The method has not been called yet.
12 | ///
13 | None,
14 | ///
15 | /// The operation successfully parsed all arguments, or was canceled using
16 | /// . Check the
17 | /// property to differentiate between
18 | /// the two.
19 | ///
20 | Success,
21 | ///
22 | /// An error occurred while parsing the arguments.
23 | ///
24 | Error,
25 | ///
26 | /// Parsing was canceled by one of the arguments using
27 | /// or
28 | ///
29 | Canceled
30 | }
31 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ParsingMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates what argument parsing rules should be used to interpret the command line.
5 | ///
6 | ///
7 | ///
8 | /// To set the parsing mode for a , use the
9 | /// property or the property.
10 | ///
11 | ///
12 | ///
13 | public enum ParsingMode
14 | {
15 | ///
16 | /// Use the normal Ookii.CommandLine parsing rules.
17 | ///
18 | Default,
19 | ///
20 | /// Allow arguments to have both long and short names, using the
21 | /// to specify a long name, and the regular
22 | /// to specify a short name.
23 | ///
24 | LongShort
25 | }
26 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/PrefixTerminationMode.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | ///
4 | /// Indicates the effect of an argument that is just the long argument prefix ("--" by default)
5 | /// by itself, not followed by a name.
6 | ///
7 | ///
8 | ///
9 | public enum PrefixTerminationMode
10 | {
11 | ///
12 | /// There is no special behavior for the argument.
13 | ///
14 | None,
15 | ///
16 | /// The argument terminates the use of named arguments. Any following arguments are interpreted
17 | /// as values for positional arguments, even if they begin with a long or short argument name
18 | /// prefix.
19 | ///
20 | PositionalOnly,
21 | ///
22 | /// The argument cancels parsing, returning an instance of the arguments type and making the
23 | /// values after this argument available in the
24 | /// property. This is identical to how an argument with
25 | /// behaves.
26 | ///
27 | CancelWithSuccess
28 | }
29 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [assembly: CLSCompliant(true)]
4 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/RingBuffer.Async.cs:
--------------------------------------------------------------------------------
1 | // The async methods in this file are used to generate the normal, non-async versions using the
2 | // Convert-SyncMethod.ps1 script.
3 | using System;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace Ookii.CommandLine;
10 |
11 | internal partial class RingBuffer
12 | {
13 | public async Task WriteToAsync(TextWriter writer, int length, CancellationToken cancellationToken)
14 | {
15 | if (length > Size)
16 | {
17 | throw new ArgumentOutOfRangeException(nameof(length));
18 | }
19 |
20 | var remaining = _buffer.Length - _bufferStart;
21 | if (remaining < length)
22 | {
23 | await WriteAsyncHelper(writer, _buffer, _bufferStart, remaining, cancellationToken);
24 | remaining = length - remaining;
25 | await WriteAsyncHelper(writer, _buffer, 0, remaining, cancellationToken);
26 | _bufferStart = remaining;
27 | }
28 | else
29 | {
30 | await WriteAsyncHelper(writer, _buffer, _bufferStart, length, cancellationToken);
31 | _bufferStart += length;
32 | Debug.Assert(_bufferStart <= _buffer.Length);
33 | }
34 |
35 | if (_bufferEnd != null && _bufferStart == _bufferEnd.Value)
36 | {
37 | _bufferEnd = null;
38 | }
39 | }
40 |
41 | private static async Task WriteAsyncHelper(TextWriter writer, char[] buffer, int index, int length, CancellationToken cancellationToken)
42 | {
43 | #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
44 | await writer.WriteAsync(buffer.AsMemory(index, length), cancellationToken);
45 | #else
46 | await writer.WriteAsync(buffer, index, length);
47 | #endif
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/ShortAliasAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine;
4 |
5 | ///
6 | /// Defines an alternative short name for a command line argument.
7 | ///
8 | ///
9 | ///
10 | /// To specify multiple aliases, apply this attribute multiple times.
11 | ///
12 | ///
13 | /// This attribute specifies short name aliases used with .
14 | /// It is ignored if the property is not
15 | /// , or if the argument doesn't have a
16 | /// primary .
17 | ///
18 | ///
19 | /// The short aliases for a command line argument can be used instead of the regular short
20 | /// name to specify the parameter on the command line.
21 | ///
22 | ///
23 | /// All short argument names and short aliases defined by a single arguments type must be
24 | /// unique.
25 | ///
26 | ///
27 | /// By default, the command line usage help generated by
28 | /// includes the aliases. Set the
29 | /// property to to exclude them.
30 | ///
31 | ///
32 | ///
33 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
34 | public sealed class ShortAliasAttribute : Attribute
35 | {
36 | ///
37 | /// Initializes a new instance of the class.
38 | ///
39 | /// The alternative short name for the command line argument.
40 | public ShortAliasAttribute(char alias)
41 | {
42 | Alias = alias;
43 | }
44 |
45 | ///
46 | /// Gets the alternative short name for the command line argument.
47 | ///
48 | ///
49 | /// The alternative short name for the command line argument.
50 | ///
51 | public char Alias { get; }
52 |
53 |
54 | ///
55 | /// Gets or sets a value indicating whether the alias should be hidden from the usage help.
56 | ///
57 | ///
58 | /// if the alias should be hidden from the usage help; otherwise,
59 | /// . The default value is .
60 | ///
61 | public bool IsHidden { get; set; }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/StringBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | #if NETSTANDARD2_0
2 |
3 | using System.Xml.Linq;
4 | using System;
5 | using System.Text;
6 | using System.Collections.Generic;
7 |
8 | namespace Ookii.CommandLine;
9 |
10 | static class StringBuilderExtensions
11 | {
12 | // This already exists in all targets except .Net Standard 2.0
13 | public static void AppendJoin(this StringBuilder builder, string separator, IEnumerable values)
14 | {
15 | bool first = true;
16 | foreach (var value in values)
17 | {
18 | if (first)
19 | {
20 | first = false;
21 | }
22 | else
23 | {
24 | builder.Append(separator);
25 | }
26 |
27 | builder.Append(value);
28 | }
29 | }
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/StringComparisonExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Ookii.CommandLine;
4 |
5 | internal static class StringComparisonExtensions
6 | {
7 | public static StringComparer GetComparer(this StringComparison comparison)
8 | {
9 | #if NETSTANDARD2_0
10 | return comparison switch
11 | {
12 | StringComparison.CurrentCulture => StringComparer.CurrentCulture,
13 | StringComparison.CurrentCultureIgnoreCase => StringComparer.CurrentCultureIgnoreCase,
14 | StringComparison.InvariantCulture => StringComparer.InvariantCulture,
15 | StringComparison.InvariantCultureIgnoreCase => StringComparer.InvariantCultureIgnoreCase,
16 | StringComparison.Ordinal => StringComparer.Ordinal,
17 | StringComparison.OrdinalIgnoreCase => StringComparer.OrdinalIgnoreCase,
18 | _ => throw new ArgumentException(Properties.Resources.InvalidStringComparison, nameof(comparison))
19 | };
20 | #else
21 | return StringComparer.FromComparison(comparison);
22 | #endif
23 | }
24 |
25 | public static bool IsCaseSensitive(this StringComparison comparison)
26 | => comparison is StringComparison.Ordinal or StringComparison.InvariantCulture or StringComparison.CurrentCulture;
27 | }
28 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/StringSegmentType.cs:
--------------------------------------------------------------------------------
1 | namespace Ookii.CommandLine;
2 |
3 | enum StringSegmentType
4 | {
5 | Text,
6 | LineBreak,
7 | Formatting,
8 | PartialLineBreak,
9 | // Must be the last group of values in the enum
10 | PartialFormattingUnknown,
11 | PartialFormattingSimple,
12 | PartialFormattingCsi,
13 | PartialFormattingOsc,
14 | PartialFormattingOscWithEscape,
15 | }
16 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Support/CommandProvider.cs:
--------------------------------------------------------------------------------
1 | using Ookii.CommandLine.Commands;
2 | using System.Collections.Generic;
3 |
4 | namespace Ookii.CommandLine.Support;
5 |
6 | ///
7 | /// A source of commands for the .
8 | ///
9 | ///
10 | /// This class is used by the source generator when using the
11 | /// attribute. It should not normally be used by other code.
12 | ///
13 | ///
14 | public abstract class CommandProvider
15 | {
16 | ///
17 | /// Gets the kind of command provider.
18 | ///
19 | ///
20 | /// One of the values of the enumeration.
21 | ///
22 | public virtual ProviderKind Kind => ProviderKind.Unknown;
23 |
24 | ///
25 | /// Gets all the commands supported by this provider.
26 | ///
27 | /// The that the commands belong to.
28 | ///
29 | /// A list of instances for the commands, in arbitrary order.
30 | ///
31 | public abstract IEnumerable GetCommandsUnsorted(CommandManager manager);
32 |
33 | ///
34 | /// Gets the application description.
35 | ///
36 | /// The application description, or if there is none.
37 | public abstract string? GetApplicationDescription();
38 | }
39 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Support/GeneratedArgument.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace Ookii.CommandLine.Support;
5 |
6 | ///
7 | /// Represents information about an argument determined by the source generator. Used for all
8 | /// arguments except dictionary arguments.
9 | ///
10 | ///
11 | /// This class is used by the source generator when using the
12 | /// attribute. It should not normally be used by other code.
13 | ///
14 | ///
15 | /// The element type of the argument, including if it is one.
16 | public class GeneratedArgument : GeneratedArgumentBase
17 | {
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// The argument creation information.
22 | public GeneratedArgument(in ArgumentCreationInfo info) : base(info)
23 | {
24 | Debug.Assert(info.Kind != ArgumentKind.Dictionary);
25 | Debug.Assert(typeof(TElementWithNullable) == info.ElementTypeWithNullable);
26 | }
27 |
28 | private protected override IValueHelper CreateDictionaryValueHelper() => throw new NotImplementedException();
29 |
30 | private protected override IValueHelper CreateMultiValueHelper() => new MultiValueHelper();
31 | }
32 |
--------------------------------------------------------------------------------
/src/Ookii.CommandLine/Support/GeneratedArgumentBase.cs:
--------------------------------------------------------------------------------
1 | using Ookii.CommandLine.Conversion;
2 | using Ookii.CommandLine.Validation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.ComponentModel;
6 | using System.Diagnostics;
7 | using System.Diagnostics.CodeAnalysis;
8 | using System.Reflection;
9 |
10 | namespace Ookii.CommandLine.Support;
11 |
12 | ///
13 | /// Represents information about an argument determined by the source generator.
14 | ///
15 | ///
16 | /// This class is used by the source generator when using the
17 | /// attribute. It should not normally be used by other code.
18 | ///
19 | ///
20 | public abstract class GeneratedArgumentBase : CommandLineArgument
21 | {
22 | private readonly Action