├── CommandLineParser.DependencyInjection
├── Interfaces
│ ├── ICommandLineOptions.cs
│ ├── IValidateCommandLineOptions.cs
│ ├── IExecuteCommandLineOptions.cs
│ ├── IExecuteParsingFailure.cs
│ ├── IValidateCommandLineOptionsAsync.cs
│ ├── IExecuteCommandLineOptionsAsync.cs
│ ├── IExecuteParsingFailureAsync.cs
│ ├── ICommandLineOptionsValidator.cs
│ ├── ICommandLineParserSyncExecutionFactory.cs
│ ├── ICommandLineParserAsyncExecutionFactory.cs
│ └── ICommandLineParser.cs
├── Exceptions
│ ├── CommandLineOptionsValidationException.cs
│ └── NoExecuteCommandLineServiceFoundException.cs
├── Extensions
│ ├── TaskExtensions.cs
│ └── ServiceCollectionExtensions.cs
├── AsyncHelper.cs
├── CommandLineParser.DependencyInjection.csproj
├── Models
│ └── CommandLineParserDiOptions.cs
├── CommandLineParserSyncExecutionFactory.cs
├── CommandLineParserAsyncExecutionFactory.cs
├── CommandLineOptionsValidator.cs
└── CommandLineParser.cs
├── QuickStart
├── Properties
│ └── launchSettings.json
├── QuickStart.csproj
└── Program.cs
├── CommandLineParser.DependencyInjection.Tests
├── Services
│ └── DoYouLikeService.cs
├── ExecuteOptions
│ ├── ExecuteParsingFailure.cs
│ ├── ExecuteAskOptions.cs
│ ├── ExecuteParsingFailureAsync.cs
│ └── ExecuteAskOptionsAsync.cs
├── Options
│ ├── AskOptions.cs
│ └── AskOptionsAsync.cs
├── CommandLineParser.DependencyInjection.Tests.csproj
└── CommandLineParserDiTests.cs
├── license.txt
├── CommandLineParser.DependencyInjection.sln
├── .gitignore
└── readme.md
/CommandLineParser.DependencyInjection/Interfaces/ICommandLineOptions.cs:
--------------------------------------------------------------------------------
1 | namespace CommandLineParser.DependencyInjection.Interfaces;
2 |
3 | ///
4 | /// Command Line Interface.
5 | ///
6 | public interface ICommandLineOptions;
--------------------------------------------------------------------------------
/QuickStart/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "QuickStart": {
4 | "commandName": "Project"
5 | },
6 | "WSL": {
7 | "commandName": "WSL2",
8 | "distributionName": ""
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/Services/DoYouLikeService.cs:
--------------------------------------------------------------------------------
1 | namespace CommandLineParser.DependencyInjection.Tests.Services;
2 |
3 | class DoYouLikeService
4 | {
5 | public string DoILikeThis(string thing, bool like, bool async) =>
6 | async
7 | ? like
8 | ? $"Yes, I do like ASYNC {thing}! Thank you, Thank you Sam I Am!"
9 | : $"I do not like them, Sam I Am! I do not like ASYNC {thing}."
10 | : like
11 | ? $"Yes, I do like {thing}! Thank you, Thank you Sam I Am!"
12 | : $"I do not like them, Sam I Am! I do not like {thing}.";
13 | }
14 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IValidateCommandLineOptions.cs:
--------------------------------------------------------------------------------
1 | namespace CommandLineParser.DependencyInjection.Interfaces;
2 |
3 | ///
4 | /// Validate Command Line Options Synchronously.
5 | ///
6 | /// Command Line Options to Validate
7 | public interface IValidateCommandLineOptions
8 | where TOptions : class, ICommandLineOptions
9 | {
10 | ///
11 | /// Validate Options Synchronously.
12 | ///
13 | /// Options to validate.
14 | /// Validation Result
15 | bool Validate(TOptions options);
16 | }
--------------------------------------------------------------------------------
/QuickStart/QuickStart.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IExecuteCommandLineOptions.cs:
--------------------------------------------------------------------------------
1 | namespace CommandLineParser.DependencyInjection.Interfaces;
2 |
3 | ///
4 | /// Execute Command Line Synchronously.
5 | ///
6 | /// Command Line Options this executor handles.
7 | /// Results
8 | public interface IExecuteCommandLineOptions where TCommandLineOptions : ICommandLineOptions
9 | {
10 | ///
11 | /// Execute Command Synchronously.
12 | ///
13 | /// Command Line Options
14 | /// Result
15 | TResult Execute(TCommandLineOptions options);
16 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IExecuteParsingFailure.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CommandLine;
3 |
4 | namespace CommandLineParser.DependencyInjection.Interfaces;
5 |
6 | ///
7 | /// Synchronously Execute on Parsing Failure.
8 | ///
9 | /// Results
10 | public interface IExecuteParsingFailure
11 | {
12 | ///
13 | /// Execute Command Synchronously.
14 | ///
15 | /// Arguments that were passed into the parser.
16 | /// Errors as reported from the parser.
17 | /// Result
18 | TResult Execute(string[] args, IEnumerable errors);
19 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Exceptions/CommandLineOptionsValidationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandLineParser.DependencyInjection.Interfaces;
3 |
4 | namespace CommandLineParser.DependencyInjection.Exceptions;
5 |
6 | ///
7 | /// Command Line Options Validation Exception
8 | ///
9 | /// Options that are invalid.
10 | /// Exception that was thrown
11 | public class CommandLineOptionsValidationException(ICommandLineOptions options, Exception? innerException = null)
12 | : Exception($"Command Line Options '{options.GetType().Name}' are invalid.", innerException)
13 | {
14 | ///
15 | /// Invalid Options
16 | ///
17 | public ICommandLineOptions Options { get; } = options;
18 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IValidateCommandLineOptionsAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace CommandLineParser.DependencyInjection.Interfaces;
5 |
6 | ///
7 | /// Validate Command Line Options Asynchronously.
8 | ///
9 | /// Command Line Options to Validate
10 | public interface IValidateCommandLineOptionsAsync
11 | where TOptions : class, ICommandLineOptions
12 | {
13 | ///
14 | /// Validate Options Asynchronously.
15 | ///
16 | /// Options to validate.
17 | /// Cancellation Token
18 | /// Validation Result
19 | Task ValidateAsync(TOptions options, CancellationToken ctx);
20 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/ExecuteOptions/ExecuteParsingFailure.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CommandLine;
3 | using CommandLineParser.DependencyInjection.Interfaces;
4 |
5 | namespace CommandLineParser.DependencyInjection.Tests.ExecuteOptions;
6 |
7 | class ExecuteParsingFailure : IExecuteParsingFailure
8 | {
9 | #region Implementation of IExecuteParsingFailure
10 |
11 | ///
12 | /// Execute Command Synchronously.
13 | ///
14 | /// Arguments that were passed into the parser.
15 | /// Errors as reported from the parser.
16 | /// Result
17 | public string Execute(string[] args, IEnumerable errors) => $"Unable to parse \"{string.Join(' ', args)}\".";
18 |
19 | #endregion
20 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/ExecuteOptions/ExecuteAskOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandLineParser.DependencyInjection.Interfaces;
2 | using CommandLineParser.DependencyInjection.Tests.Options;
3 | using CommandLineParser.DependencyInjection.Tests.Services;
4 |
5 | namespace CommandLineParser.DependencyInjection.Tests.ExecuteOptions;
6 |
7 | class ExecuteAskOptions(DoYouLikeService doYouLikeService) : IExecuteCommandLineOptions
8 | {
9 | #region Implementation of IExecuteCommandLineOptions
10 |
11 | ///
12 | /// Execute Command Synchronously.
13 | ///
14 | /// Command Line Options
15 | /// Result
16 | public string Execute(AskOptions options) => doYouLikeService.DoILikeThis(options.DoYouLike, options.Like, false);
17 |
18 | #endregion
19 | }
20 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Extensions/TaskExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Threading.Tasks;
3 |
4 | namespace CommandLineParser.DependencyInjection.Extensions;
5 |
6 | public static class TaskExtensions
7 | {
8 | ///
9 | /// Invoke Async Method.
10 | ///
11 | /// Result Type
12 | /// Method Info
13 | /// Class method is being invoked on
14 | /// Method Parameters to use
15 | /// Result
16 | public static async Task InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
17 | {
18 | var awaitable = (Task)@this.Invoke(obj, parameters);
19 | await awaitable;
20 | return awaitable.GetAwaiter().GetResult();
21 | }
22 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IExecuteCommandLineOptionsAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace CommandLineParser.DependencyInjection.Interfaces;
5 |
6 | ///
7 | /// Execute Command Line Asynchronously.
8 | ///
9 | /// Command Line Options this executor handles.
10 | /// Results
11 | public interface IExecuteCommandLineOptionsAsync where TCommandLineOptions : ICommandLineOptions
12 | {
13 | ///
14 | /// Execute Command Asynchronously.
15 | ///
16 | /// Command Line Options
17 | /// Cancellation Token
18 | /// Result
19 | Task ExecuteAsync(TCommandLineOptions options, CancellationToken ctx = default);
20 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/IExecuteParsingFailureAsync.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace CommandLineParser.DependencyInjection.Interfaces;
7 |
8 | ///
9 | /// Asynchronously Execute on Parsing Failure.
10 | ///
11 | /// Results
12 | public interface IExecuteParsingFailureAsync
13 | {
14 | ///
15 | /// Execute Command Asynchronously.
16 | ///
17 | /// Arguments that were passed into the parser.
18 | /// Errors as reported from the parser.
19 | /// Cancellation Token
20 | /// Result
21 | Task ExecuteAsync(string[] args, IEnumerable errors, CancellationToken ctx = default);
22 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/Options/AskOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CommandLine;
3 | using CommandLine.Text;
4 | using CommandLineParser.DependencyInjection.Interfaces;
5 |
6 | namespace CommandLineParser.DependencyInjection.Tests.Options;
7 |
8 | [Verb("ask", HelpText = "Ask a question.")]
9 | class AskOptions : ICommandLineOptions
10 | {
11 | [Option("like", Required = false, Default = false, HelpText = "Should we like this?")]
12 | public bool Like { get; set; }
13 |
14 | [Value(0, Required = true, HelpText = "What do we like?")]
15 | public string DoYouLike { get; set; }
16 |
17 | [Usage(ApplicationAlias = "CommandLineParserDiTests")]
18 | public static IEnumerable Examples =>
19 | new List() {
20 | new Example("Do you like green eggs and ham?", new AskOptions { DoYouLike = "Green Eggs and Ham?", Like = true })
21 | };
22 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/Options/AskOptionsAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CommandLine;
3 | using CommandLine.Text;
4 | using CommandLineParser.DependencyInjection.Interfaces;
5 |
6 | namespace CommandLineParser.DependencyInjection.Tests.Options;
7 |
8 | [Verb("askAsync", HelpText = "Ask a question ASYNC.")]
9 | class AskOptionsAsync : ICommandLineOptions
10 | {
11 | [Option("like", Required = false, Default = false, HelpText = "Should we like this?")]
12 | public bool Like { get; set; }
13 |
14 | [Value(0, Required = true, HelpText = "What do we like?")]
15 | public string DoYouLike { get; set; }
16 |
17 | [Usage(ApplicationAlias = "CommandLineParserDiTests")]
18 | public static IEnumerable Examples =>
19 | new List() {
20 | new Example("Do you like green eggs and ham?", new AskOptions { DoYouLike = "Green Eggs and Ham?", Like = true })
21 | };
22 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/ExecuteOptions/ExecuteParsingFailureAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using CommandLine;
5 | using CommandLineParser.DependencyInjection.Interfaces;
6 |
7 | namespace CommandLineParser.DependencyInjection.Tests.ExecuteOptions;
8 |
9 | class ExecuteParsingFailureAsync : IExecuteParsingFailureAsync
10 | {
11 | #region Implementation of IExecuteParsingFailureAsync
12 |
13 | ///
14 | /// Execute Command Asynchronously.
15 | ///
16 | /// Arguments that were passed into the parser.
17 | /// Errors as reported from the parser.
18 | /// Cancellation Token
19 | /// Result
20 | public Task ExecuteAsync(string[] args, IEnumerable errors, CancellationToken ctx = default) =>
21 | Task.FromResult($"Unable to parse \"{string.Join(' ', args)}\" ASYNC.");
22 |
23 | #endregion
24 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/CommandLineParser.DependencyInjection.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 | false
7 |
8 | latest
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/ExecuteOptions/ExecuteAskOptionsAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using CommandLineParser.DependencyInjection.Interfaces;
4 | using CommandLineParser.DependencyInjection.Tests.Options;
5 | using CommandLineParser.DependencyInjection.Tests.Services;
6 |
7 | namespace CommandLineParser.DependencyInjection.Tests.ExecuteOptions;
8 |
9 | class ExecuteAskOptionsAsync(DoYouLikeService doYouLikeService)
10 | : IExecuteCommandLineOptionsAsync
11 | {
12 | #region Implementation of IExecuteCommandLineOptionsAsync
13 |
14 | ///
15 | /// Execute Command Asynchronously.
16 | ///
17 | /// Command Line Options
18 | /// Cancellation Token
19 | /// Result
20 | public Task ExecuteAsync(AskOptionsAsync options, CancellationToken ctx = default) =>
21 | Task.FromResult(doYouLikeService.DoILikeThis(options.DoYouLike, options.Like, true));
22 |
23 | #endregion
24 | }
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024, Jaron Horst
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.
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/AsyncHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace CommandLineParser.DependencyInjection;
6 |
7 | ///
8 | /// Async Helper to run Async Task's Synchronously.
9 | ///
10 | public static class AsyncHelper
11 | {
12 | private static readonly TaskFactory TaskFactory = new
13 | TaskFactory(CancellationToken.None,
14 | TaskCreationOptions.None,
15 | TaskContinuationOptions.None,
16 | TaskScheduler.Default);
17 |
18 | ///
19 | /// Run an Async task Synchronously and return the result.
20 | ///
21 | /// Result Type
22 | /// Async Task
23 | /// Async Task's Result
24 | public static TResult RunSync(Func> func)
25 | {
26 | return TaskFactory
27 | .StartNew(func)
28 | .Unwrap()
29 | .GetAwaiter()
30 | .GetResult();
31 | }
32 |
33 | ///
34 | /// Run an Async task Synchronously.
35 | ///
36 | /// Async Task
37 | public static void RunSync(Func func)
38 | {
39 | TaskFactory
40 | .StartNew(func)
41 | .Unwrap()
42 | .GetAwaiter()
43 | .GetResult();
44 | }
45 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/ICommandLineOptionsValidator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using CommandLineParser.DependencyInjection.Exceptions;
4 |
5 | namespace CommandLineParser.DependencyInjection.Interfaces;
6 |
7 | ///
8 | /// Command Line Options Validator that supports both Synchronous and Asynchronous validation.
9 | ///
10 | public interface ICommandLineOptionsValidator
11 | {
12 | ///
13 | /// Validate Options Synchronously.
14 | ///
15 | /// Prefers Synchronous validators but looks for Asynchronous validators as a fallback.
16 | /// Options to validate.
17 | /// Validation Result
18 | /// Thrown when the validator has an exception.
19 | bool Validate(ICommandLineOptions options);
20 |
21 | ///
22 | /// Validate Options Asynchronously.
23 | ///
24 | /// Prefers Asynchronous validators but looks for Synchronous validators as a fallback.
25 | /// Options to validate.
26 | /// Cancellation Token
27 | /// Validation Result
28 | /// Thrown when the validator has an exception.
29 | Task ValidateAsync(ICommandLineOptions options, CancellationToken ctx);
30 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/ICommandLineParserSyncExecutionFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using CommandLine;
4 | using CommandLineParser.DependencyInjection.Exceptions;
5 |
6 | namespace CommandLineParser.DependencyInjection.Interfaces;
7 |
8 | ///
9 | /// Factory used to execute command line parser results asynchronously.
10 | ///
11 | /// Result Type
12 | public interface ICommandLineParserSyncExecutionFactory
13 | {
14 | ///
15 | /// Priority. Higher number is higher priority.
16 | ///
17 | int Priority { get; }
18 |
19 | ///
20 | /// Execute requested command asynchronously.
21 | ///
22 | /// Original Arguments
23 | /// Options Type
24 | /// Options
25 | /// Result
26 | /// Exception thrown when the Command Line Parser was able to get the Options but there was no service found to handle it.
27 | (TResult? Result, bool Handled) ExecuteCommand(string[] args, Type optionsType, object options);
28 |
29 | ///
30 | /// Execute parsing failure asynchronously.
31 | ///
32 | /// Original Arguments
33 | /// Command Line Parsing Errors
34 | /// Result, if handled
35 | (TResult? Result, bool Handled) ExecuteParsingFailure(string[] args, IEnumerable errors);
36 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Exceptions/NoExecuteCommandLineServiceFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandLineParser.DependencyInjection.Interfaces;
3 |
4 | namespace CommandLineParser.DependencyInjection.Exceptions;
5 |
6 | ///
7 | /// Exception thrown when the Command Line Parser was able to get the Options but there was no service found to handle it.
8 | ///
9 | public class NoExecuteCommandLineServiceFoundException : Exception
10 | {
11 | ///
12 | /// Create new Exception
13 | ///
14 | /// Options Type
15 | /// Result Type
16 | /// Was run synchronously?
17 | /// Was the sync/async allowed to fallback to async/sync?
18 | public NoExecuteCommandLineServiceFoundException(Type optionsType, Type resultType, bool isSynchronous, bool allowFallback)
19 | {
20 | OptionsType = optionsType;
21 | ResultType = resultType;
22 | IsSynchronous = isSynchronous;
23 | AllowFallback = allowFallback;
24 | }
25 |
26 | ///
27 | /// Options Type
28 | ///
29 | public Type OptionsType { get; }
30 |
31 | ///
32 | /// Result Type
33 | ///
34 | public Type ResultType { get; }
35 |
36 | ///
37 | /// Was run synchronously?
38 | ///
39 | public bool IsSynchronous { get; }
40 |
41 | ///
42 | /// Was the sync/async allowed to fallback to async/sync?
43 | ///
44 | public bool AllowFallback { get; }
45 | }
46 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/CommandLineParser.DependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | Microsoft Dependency Injection extensions for CommandLineParser (https://github.com/commandlineparser/commandline)
6 | Added support for Async parsing/execution.
7 | CommandLineParser.DependencyInjection
8 | CommandLineParser.DependencyInjection
9 | True
10 | 1.5
11 | False
12 | CommandLineParser.DependencyInjection
13 | CommandLineParser.DependencyInjection
14 | readme.md
15 | license.txt
16 | Jaron Horst
17 | https://github.com/JaronrH/CommandLineParser.DependencyInjection
18 | https://github.com/JaronrH/CommandLineParser.DependencyInjection
19 | latest
20 | enable
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Interfaces/ICommandLineParserAsyncExecutionFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using CommandLine;
6 | using CommandLineParser.DependencyInjection.Exceptions;
7 |
8 | namespace CommandLineParser.DependencyInjection.Interfaces;
9 |
10 | ///
11 | /// Factory used to execute command line parser results asynchronously.
12 | ///
13 | /// Result Type
14 | public interface ICommandLineParserAsyncExecutionFactory
15 | {
16 | ///
17 | /// Priority. Higher number is higher priority.
18 | ///
19 | int Priority { get; }
20 |
21 | ///
22 | /// Execute requested command asynchronously.
23 | ///
24 | /// Original Arguments
25 | /// Options Type
26 | /// Options
27 | /// Cancellation Token
28 | /// Result
29 | /// Exception thrown when the Command Line Parser was able to get the Options but there was no service found to handle it.
30 | Task<(TResult? Result, bool Handled)> ExecuteCommandAsync(string[] args, Type optionsType, object options, CancellationToken ctx);
31 |
32 | ///
33 | /// Execute parsing failure asynchronously.
34 | ///
35 | /// Original Arguments
36 | /// Command Line Parsing Errors
37 | /// Cancellation Token
38 | /// Result, if handled
39 | Task<(TResult? Result, bool Handled)> ExecuteParsingFailureAsync(string[] args, IEnumerable errors, CancellationToken ctx);
40 | }
--------------------------------------------------------------------------------
/QuickStart/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using CommandLineParser.DependencyInjection.Interfaces;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.Logging;
5 |
6 | new ServiceCollection() // Create Service Collection
7 | .AddCommandLineParser(typeof(Options).Assembly) // Add CommandLineParser registrations to DI
8 | .AddLogging(c => c.AddConsole().SetMinimumLevel(LogLevel.Debug)) // Add Console Logging
9 | .BuildServiceProvider() // Build Service Provider
10 | .GetRequiredService>() // Get Parser Service
11 | .ParseArguments(args, -1) // Call Parser with Arguments (Options and ExecuteOptions will be loaded from DI as needed)
12 | ;
13 |
14 | public class Options: ICommandLineOptions
15 | {
16 | [Option('v', "verbose", Required = false, HelpText = "Set output to verbose messages.")]
17 | public bool Verbose { get; set; }
18 |
19 | [Option('i', "treatAsInvalid", Required = false, HelpText = "Set to true to trigger validation error.")]
20 | public bool TreatAsInvalid { get; set; }
21 | }
22 |
23 | public class OptionsValidator : IValidateCommandLineOptions
24 | {
25 | public bool Validate(Options options) => !options.TreatAsInvalid;
26 | }
27 |
28 | public class ExecuteOptions(ILogger log) : IExecuteCommandLineOptions
29 | {
30 | #region Implementation of IExecuteCommandLineOptions
31 |
32 | ///
33 | /// Execute Command Synchronously.
34 | ///
35 | /// Command Line Options
36 | /// Result
37 | public int Execute(Options options)
38 | {
39 | if (options.Verbose)
40 | {
41 | log.LogInformation($"Verbose output enabled. Current Arguments: -v {options.Verbose}");
42 | log.LogWarning("Quick Start Example! App is in Verbose mode!");
43 | }
44 | else
45 | {
46 | log.LogInformation($"Current Arguments: -v {options.Verbose}");
47 | log.LogInformation("Quick Start Example!");
48 | }
49 |
50 | return 0;
51 | }
52 |
53 | #endregion
54 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/Models/CommandLineParserDiOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandLineParser.DependencyInjection.Interfaces;
2 |
3 | namespace CommandLineParser.DependencyInjection.Models;
4 |
5 | ///
6 | /// Command Line Parser DI Options
7 | ///
8 | public class CommandLineParserDiOptions
9 | {
10 | ///
11 | /// When true, only public types will be registered (default).
12 | ///
13 | public bool PublicOnly { get; set; } = true;
14 |
15 | ///
16 | /// Always include the calling assembly when scanning for services even if assemblies are provided. True by default.
17 | ///
18 | public bool AlwaysIncludeCallingAssembly { get; set; } = true;
19 |
20 | ///
21 | /// Scan and import services for , & and factories for . True by default.
22 | ///
23 | /// Services will be transient while factories will be singleton.
24 | public bool FindAsyncServices { get; set; } = true;
25 |
26 | ///
27 | /// Include default factory for async execution. True by default.
28 | ///
29 | public bool IncludeDefaultAsyncFactory { get; set; } = true;
30 |
31 | ///
32 | /// Scan and import services for , & and factories for . True by default.
33 | ///
34 | /// Services will be transient while factories will be singleton.
35 | public bool FindSyncServices { get; set; } = true;
36 |
37 | ///
38 | /// Include default factory for async execution. True by default.
39 | ///
40 | public bool IncludeDefaultSyncFactory { get; set; } = true;
41 |
42 | ///
43 | /// Include default validator for options validation. True by default.
44 | ///
45 | /// Turn off if not using validators!
46 | public bool IncludeDefaultOptionsValidator { get; set; } = true;
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.10.35027.167
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineParser.DependencyInjection", "CommandLineParser.DependencyInjection\CommandLineParser.DependencyInjection.csproj", "{7B3F7B26-B85B-4D35-BD5E-A2FAE8375008}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineParser.DependencyInjection.Tests", "CommandLineParser.DependencyInjection.Tests\CommandLineParser.DependencyInjection.Tests.csproj", "{72B442E6-654D-4CC9-9995-0F10B86B686A}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A7353A99-C73A-4750-B658-4823E3FF3A63}"
11 | ProjectSection(SolutionItems) = preProject
12 | license.txt = license.txt
13 | readme.md = readme.md
14 | EndProjectSection
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickStart", "QuickStart\QuickStart.csproj", "{18B8EC8C-D181-4057-9160-C0A8E6B8E3EE}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {7B3F7B26-B85B-4D35-BD5E-A2FAE8375008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {7B3F7B26-B85B-4D35-BD5E-A2FAE8375008}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {7B3F7B26-B85B-4D35-BD5E-A2FAE8375008}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {7B3F7B26-B85B-4D35-BD5E-A2FAE8375008}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {72B442E6-654D-4CC9-9995-0F10B86B686A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {72B442E6-654D-4CC9-9995-0F10B86B686A}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {72B442E6-654D-4CC9-9995-0F10B86B686A}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {72B442E6-654D-4CC9-9995-0F10B86B686A}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {18B8EC8C-D181-4057-9160-C0A8E6B8E3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {18B8EC8C-D181-4057-9160-C0A8E6B8E3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {18B8EC8C-D181-4057-9160-C0A8E6B8E3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {18B8EC8C-D181-4057-9160-C0A8E6B8E3EE}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {55911917-AEF0-4E83-BCA4-6C31A8A4DF72}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/CommandLineParserSyncExecutionFactory.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using CommandLineParser.DependencyInjection.Exceptions;
3 | using CommandLineParser.DependencyInjection.Interfaces;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using System;
6 | using System.Collections.Concurrent;
7 | using System.Collections.Generic;
8 | using System.Reflection;
9 |
10 |
11 | namespace CommandLineParser.DependencyInjection;
12 |
13 | ///
14 | /// Default Factory that uses to get the appropriate async service to execute command line parser results synchronously.
15 | ///
16 | public class CommandLineParserSyncExecutionFactory(IServiceProvider serviceProvider) : ICommandLineParserSyncExecutionFactory
17 | {
18 | // ReSharper disable once StaticMemberInGenericType
19 | private static readonly Type ExecuteCommandLineOptionsInterfaceType = typeof(IExecuteCommandLineOptions<,>);
20 |
21 | ///
22 | /// Options Type to Service Type Map
23 | ///
24 | // ReSharper disable once StaticMemberInGenericType
25 | private static readonly ConcurrentDictionary OptionTypeToServiceType = new();
26 |
27 | ///
28 | /// Priority. Higher number is higher priority.
29 | ///
30 | public int Priority => 0;
31 |
32 | ///
33 | /// Execute requested command asynchronously.
34 | ///
35 | /// Original Arguments
36 | /// Options Type
37 | /// Options
38 | /// Result
39 | /// Exception thrown when the Command Line Parser was able to get the Options but there was no service found to handle it.
40 | public virtual (TResult? Result, bool Handled) ExecuteCommand(string[] args, Type optionsType, object options)
41 | {
42 | var serviceInfo = OptionTypeToServiceType.GetOrAdd(optionsType, t =>
43 | {
44 | var type = ExecuteCommandLineOptionsInterfaceType.MakeGenericType(optionsType, typeof(TResult));
45 | var methodType = type.GetMethod("Execute");
46 | return (type, methodType);
47 | });
48 | var executingService = serviceProvider.GetService(serviceInfo.ServiceType);
49 | return executingService != null && serviceInfo.ExecuteMethodInfo != null
50 | ? ((TResult)serviceInfo.ExecuteMethodInfo.Invoke(executingService, [options]), true)
51 | : (default, false);
52 | }
53 |
54 | ///
55 | /// Execute parsing failure asynchronously.
56 | ///
57 | /// Original Arguments
58 | /// Command Line Parsing Errors
59 | /// Result, if handled
60 | public virtual (TResult? Result, bool Handled) ExecuteParsingFailure(string[] args, IEnumerable errors)
61 | {
62 | var service = serviceProvider.GetService>(); // Only create when needed
63 | return service == null
64 | ? (default, false)
65 | : (service.Execute(args, errors), true);
66 | }
67 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection/CommandLineParserAsyncExecutionFactory.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using CommandLineParser.DependencyInjection.Exceptions;
3 | using CommandLineParser.DependencyInjection.Extensions;
4 | using CommandLineParser.DependencyInjection.Interfaces;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using System;
7 | using System.Collections.Concurrent;
8 | using System.Collections.Generic;
9 | using System.Reflection;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 |
14 | namespace CommandLineParser.DependencyInjection;
15 |
16 | ///
17 | /// Default Factory that uses to get the appropriate async service to execute command line parser results asynchronously.
18 | ///
19 | public class CommandLineParserAsyncExecutionFactory(IServiceProvider serviceProvider) : ICommandLineParserAsyncExecutionFactory
20 | {
21 | // ReSharper disable once StaticMemberInGenericType
22 | private static readonly Type ExecuteCommandLineOptionsAsyncInterfaceType = typeof(IExecuteCommandLineOptionsAsync<,>);
23 |
24 | ///
25 | /// Options Type to Service Type Map
26 | ///
27 | // ReSharper disable once StaticMemberInGenericType
28 | private static readonly ConcurrentDictionary OptionTypeToServiceType = new();
29 |
30 | ///
31 | /// Priority. Higher number is higher priority.
32 | ///
33 | public int Priority => 0;
34 |
35 | ///
36 | /// Execute requested command asynchronously.
37 | ///
38 | /// Original Arguments
39 | /// Options Type
40 | /// Options
41 | /// Cancellation Token
42 | /// Result
43 | /// Exception thrown when the Command Line Parser was able to get the Options but there was no service found to handle it.
44 | public virtual async Task<(TResult? Result, bool Handled)> ExecuteCommandAsync(string[] args, Type optionsType, object options, CancellationToken ctx)
45 | {
46 | var serviceInfo = OptionTypeToServiceType.GetOrAdd(optionsType, t =>
47 | {
48 | var type = ExecuteCommandLineOptionsAsyncInterfaceType.MakeGenericType(optionsType, typeof(TResult));
49 | var methodType = type.GetMethod("ExecuteAsync");
50 | return (type, methodType);
51 | });
52 | var executingService = serviceProvider.GetService(serviceInfo.ServiceType);
53 | return executingService != null && serviceInfo.ExecuteMethodInfo != null
54 | ? (await serviceInfo.ExecuteMethodInfo.InvokeAsync(executingService, new[] { options, ctx }), true)
55 | : (default, false);
56 | }
57 |
58 | ///
59 | /// Execute parsing failure asynchronously.
60 | ///
61 | /// Original Arguments
62 | /// Command Line Parsing Errors
63 | /// Cancellation Token
64 | /// Result, if handled
65 | public virtual async Task<(TResult? Result, bool Handled)> ExecuteParsingFailureAsync(string[] args, IEnumerable errors, CancellationToken ctx)
66 | {
67 | var service = serviceProvider.GetService>(); // Only create when needed
68 | return service == null
69 | ? (default, false)
70 | : (await service.ExecuteAsync(args, errors, ctx), true);
71 | }
72 | }
--------------------------------------------------------------------------------
/CommandLineParser.DependencyInjection.Tests/CommandLineParserDiTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Threading.Tasks;
7 | using CommandLineParser.DependencyInjection.Interfaces;
8 | using CommandLineParser.DependencyInjection.Tests.Services;
9 | using FluentAssertions;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Xunit;
12 |
13 | namespace CommandLineParser.DependencyInjection.Tests;
14 |
15 | public class CommandLineParserDiTests
16 | {
17 | protected IServiceProvider ServiceProvider { get; set; }
18 |
19 | public CommandLineParserDiTests()
20 | {
21 | var collection = new ServiceCollection()
22 | .AddCommandLineParser(i => i.PublicOnly = false, typeof(CommandLineParserDiTests).Assembly)
23 | .AddSingleton()
24 | ;
25 | ServiceProvider = collection.BuildServiceProvider();
26 | }
27 |
28 | public static IEnumerable