├── scripts ├── mkdocs-serve.sh ├── tag-and-publish.sh ├── mkdocs-snippets.sh ├── mkdocs-update.sh └── rename-snippets.sh ├── requirements.txt ├── CommandDotNet.Example ├── build ├── run ├── brun ├── build-win10-x64-exe.bat ├── single-file-run.sh ├── Commands │ └── Math.cs ├── InteractiveMiddleware.cs ├── single-file-publish.sh ├── CommandDotNet.Example.csproj └── .editorconfig ├── .dockerignore ├── docs ├── index.md ├── img │ ├── banner.png │ ├── favicon.ico │ ├── definitions.png │ ├── parse-directive-screenshot.png │ ├── parse-directive-screenshot-2.png │ ├── parse-directive-screenshot-3.png │ ├── home-bkg.svg │ └── home-bkg-slate.svg ├── diagrams │ ├── ArgTermsExample.png │ ├── MiddlewarePipeline.png │ ├── ArgumentParseBehavior.png │ └── ArgumentPerspectives.png ├── Extensibility │ └── parameter-types.md ├── Diagnostics │ ├── app-version.md │ └── time-directive.md ├── Localization │ └── culture-directive.md ├── OtherFeatures │ └── iconsole.md ├── TipsFaqs │ └── cli-best-practices.md ├── stylesheets │ └── extra.css └── ReleaseNotes │ ├── CommandDotNet.NameCasing.md │ └── CommandDotNet.NewerReleasesAlerts.md ├── images ├── logo.png ├── social.png ├── logo.graffle ├── definitions.png └── nuget-icon.png ├── nuget-icon.png ├── CommandDotNet ├── IArgumentModel.cs ├── Help │ ├── HelpTextStyle.cs │ ├── IHelpProvider.cs │ ├── UsageAppNameStyle.cs │ ├── HelpTextProviderFactory.cs │ └── Resources.cs ├── HandleErrorDelegate.cs ├── InterceptorExecutionDelegate.cs ├── Tokens │ ├── TokenType.cs │ ├── ICommandLineStringSplitter.cs │ ├── TokenTransformation.cs │ └── Token.cs ├── DefaultCommandAttribute.cs ├── CharExtensions.cs ├── Parsing │ ├── IParseError.cs │ ├── IParser.cs │ ├── ExpectedFlagParseError.cs │ ├── ValueParsingException.cs │ ├── MissingOptionValueParseError.cs │ ├── UnrecognizedOptionParseError.cs │ ├── ParserFactory.cs │ ├── UnrecognizedArgumentParseError.cs │ ├── NotAllowedValueParseError.cs │ ├── SingleValueParser.cs │ ├── UnexpectedOptionValueParseError.cs │ └── CommandParser.OperandQueue.cs ├── INameAndDescription.cs ├── Extensions │ ├── IIndentableToString.cs │ ├── CollectionExtensions.cs │ ├── CustomAttributeProviderExtensions.cs │ ├── DisposableAction.cs │ ├── MemberInfoExtensions.cs │ ├── EmptyCollection.cs │ ├── DictionaryExtensions.cs │ └── CustomAttributesContainerExtensions.cs ├── Builders │ ├── ArgumentDefaults │ │ └── GetArgumentKeysDelegate.cs │ ├── ICommandBuilder.cs │ ├── IServicesContainer.cs │ ├── IDependencyResolver.cs │ ├── Resources.cs │ ├── ICustomAttributesContainer.cs │ ├── CommandBuilder.cs │ ├── DependencyResolverExtensions.cs │ ├── NameTransformation.cs │ └── IArgumentNode.cs ├── ArgumentMode.cs ├── Rendering │ ├── IConsoleBuffer.cs │ ├── IConsoleColor.cs │ ├── IStandardIn.cs │ ├── IStandardOut.cs │ ├── IConsoleCursor.cs │ ├── IConsoleWindow.cs │ ├── IStandardError.cs │ └── ForwardingTextWriter.cs ├── ClassModeling │ └── Definitions │ │ ├── ISourceDef.cs │ │ ├── IMethodDef.cs │ │ ├── ICommandDef.cs │ │ ├── ServicesContainerExtensions.cs │ │ ├── IArgumentDef.cs │ │ ├── NullCustomAttributeProvider.cs │ │ ├── MethodCommandDef.cs │ │ └── DelegateCommandDef.cs ├── Prompts │ ├── IArgumentPrompter.cs │ ├── Resources.cs │ ├── ConsoleKeyInfoExtensions.cs │ └── IPrompter.cs ├── Execution │ ├── ExecutionMiddleware.cs │ ├── OnRunCompletedEventArgs.cs │ ├── Resources.cs │ ├── ResolveStrategy.cs │ ├── CancellationMiddleware.cs │ ├── InvocationResultExtensions.cs │ └── ExecutionAppSettings.cs ├── ConsoleExtensions.cs ├── IArgumentArity.cs ├── BooleanMode.cs ├── Diagnostics │ ├── CommandLoggerConfig.cs │ ├── TimeDirective.cs │ └── NonSerializableWrapper.cs ├── TypeDescriptors │ ├── IAllowedValuesTypeDescriptor.cs │ ├── IArgumentTypeDescriptor.cs │ ├── EnumTypeDescriptor.cs │ ├── Resources.cs │ ├── DelegatedTypeDescriptor.cs │ ├── ComponentModelTypeDescriptor.cs │ └── BoolTypeDescriptor.cs ├── ValueProxy.cs ├── CommandDotNet.csproj.DotSettings ├── OriginalInput.cs ├── ExitCodes.cs ├── CommandAppSettings.cs ├── CommandContextExtensions.cs ├── InvalidConfigurationException.cs ├── ArgumentSeparatorStrategy.cs ├── ValueFromToken.cs ├── AppSettingAttribute.cs ├── Directives │ └── DirectivesTokenCollectionExtensions.cs ├── ITypeInfo.cs ├── EnvVarAttribute.cs ├── SubCommandAttribute.cs ├── ArgumentDefault.cs ├── OrderByPositionInClassAttribute.cs ├── CommandNodeType.cs ├── StringExtensions.cs ├── IServices.cs ├── Resources.cs ├── ArgumentGroupAttribute.cs ├── DescriptionMethodAttribute.cs └── LocalizationAppSettings.cs ├── CommandDotNet.DocExamples ├── BashSnippets │ ├── commands_2_git_pop.bash │ ├── commands_2_git_stash.bash │ ├── argument_types_login.bash │ ├── passwords_login_exe.bash │ ├── commands_default_command_process.bash │ ├── command_logger_default_no_directive.bash │ ├── passwords_prompt_exe.bash │ ├── subcommands_git_composed_stash.bash │ ├── getting-started-140-default-commands-add.bash │ ├── piped_arguments_list_ids_only.bash │ ├── subcommands_git_composed_stash_pop.bash │ ├── command_logger_custom_attribute_disabled.bash │ ├── getting-started-100-calculator-add.bash │ ├── getting-started-700-pipes_sum.bash │ ├── getting-started-800-ctrlc_sum.bash │ ├── getting-started-700-pipes_range.bash │ ├── getting-started-800-ctrlc_range.bash │ ├── subcommands_git_composed_commit.bash │ ├── piped_arguments_list.bash │ ├── arguments_arity_no_missing_args.bash │ ├── getting-started-100-calculator-add-invalid.bash │ ├── arguments_arity_collection_missing_args.bash │ ├── arguments_collections_exe_split_args_only.bash │ ├── arguments_arity_collection_no_missing_args.bash │ ├── arguments_attributes_windows_exe.bash │ ├── getting-started-140-default-commands-add-command-per-class.bash │ ├── arguments_attributes_powershell_exe.bash │ ├── arguments_collections_exe_split_directive_args_only.bash │ ├── getting-started-500-interceptors-get.bash │ ├── argument_separator_pass_thru_help.bash │ ├── arguments_arity_missing_args.bash │ ├── arguments_collections_exe.bash │ ├── piped_arguments_options_notify.bash │ ├── getting-started-140-default-commands-help.bash │ ├── piped_arguments_disable.bash │ ├── arguments_collections_exe_intermixed.bash │ ├── getting-started-100-calculator-add-help.bash │ ├── piped_arguments_options_notify_pipeto_directive.bash │ ├── getting-started-500-interceptors-get-help.bash │ ├── arguments_collections_exe_argument_separator_passthru.bash │ ├── getting-started-140-default-commands-add-help-command-per-class.bash │ ├── arguments_collections_help.bash │ ├── commands_calculator_sum_help.bash │ ├── argument_separator_pass_thru_option_mask_separator.bash │ ├── getting-started-100-calculator-help.bash │ ├── argument_separator_end_of_options_option_mask_no_separator.bash │ ├── argument_separator_end_of_options_unexpected_operand.bash │ ├── argument_separator_end_of_options_option_mask_separator.bash │ ├── argument_separator_end_of_options_unexpected_operand_ignored.bash │ ├── getting-started-120-subcommands-help.bash │ ├── getting-started-120-subcommands-trig-help.bash │ ├── getting-started-300-calculator-add-basic-help.bash │ ├── exceptions_throw.bash │ ├── getting-started-140-default-commands-help-command-per-class.bash │ ├── getting-started-300-calculator-add-help.bash │ ├── subcommands_git_nested_help.bash │ ├── dataannotations-1-table-create-help.bash │ ├── subcommands_git_composed_help.bash │ ├── data_annotations_validation_create_help.bash │ ├── argument_models_notify_with_model_help.bash │ ├── getting-started-500-interceptors-help.bash │ ├── argument_models_notify_without_model_help.bash │ ├── argument_models_notify_with_interceptor_help.bash │ ├── dataannotations-1-table-create.bash │ ├── getting-started-other-features_help.bash │ ├── argument_models_notify_with_model_composed_help.bash │ ├── argument_models_notify_with_nested_operands_help.bash │ ├── subcommands_git_composed_stash_help.bash │ ├── data_annotations_validation_create_invalid.bash │ ├── arguments_attributes_help.bash │ ├── argument_models_notify_with_invalid_nested_operands_help.bash │ ├── arguments_attributes_alt_help.bash │ ├── arguments_attributes_windows_help.bash │ ├── arguments_attributes_powershell_help.bash │ ├── fluent_validation_create_invalid.bash │ ├── fluent_validation_factory_create_invalid.bash │ ├── getting-started-300-calculator-help.bash │ ├── command_logger_include_machine_and_user_exe.bash │ ├── arguments_arity_collection_help.bash │ ├── command_logger_custom_attribute_enabled.bash │ ├── command_logger_default_directive.bash │ ├── command_logger_root_option_exe.bash │ ├── exceptions_throw_cmdlog.bash │ └── arguments_arity_help.bash ├── DocExamplesDefaultTestConfig.cs ├── OtherFeatures │ ├── Spectre_Examples.cs │ ├── DefaultMiddleware_Examples.cs │ ├── IEnvironment_Examples.cs │ ├── Suggest_Examples.cs │ └── NameCasing_Examples.cs ├── Diagnostics │ └── Diagnostics_Examples.cs ├── Commands │ └── Commands │ │ └── Commands_DefaultCommand.cs ├── GettingStarted │ └── Getting_Started_OtherFeatures.cs └── .editorconfig ├── CommandDotNet.TestTools ├── AssemblyInfo.cs ├── AssertFailedException.cs ├── Prompts │ ├── IPromptResponder.cs │ ├── UnexpectedPromptFailureException.cs │ ├── TextAnswer.cs │ ├── FailAnswer.cs │ ├── ListAnswer.cs │ ├── IAnswer.cs │ └── ConsoleKeyInfos.cs ├── IDefaultTestConfig.cs ├── Scenarios │ ├── IScenario.cs │ └── Scenario.cs ├── Ambient.cs ├── CommandDotNet.TestTools.csproj ├── ITestConsole.cs ├── AppRunnerResult.cs └── TestConsoleWriter.cs ├── localization_files └── simple_json │ └── en │ ├── CommandDotNet.DataAnnotations.json │ ├── CommandDotNet.Spectre.json │ └── CommandDotNet.FluentValidation.json ├── CommandDotNet.Tests ├── xunit.runner.json ├── FeatureTests │ ├── Arguments │ │ ├── Models │ │ │ ├── ArgsAsArgModels │ │ │ │ ├── IStructListArgumentModel.cs │ │ │ │ ├── IEnumListArgumentModel.cs │ │ │ │ ├── IObjectListArgumentModel.cs │ │ │ │ ├── NrtOperandsNoDefaultsStructListArgumentModel.cs │ │ │ │ ├── OperandsDefaultsStructListArgumentModel.cs │ │ │ │ ├── NrtOperandsNoDefaultsEnumListArgumentModel.cs │ │ │ │ ├── NrtOperandsNoDefaultsObjectListArgumentModel.cs │ │ │ │ ├── OperandsNoDefaultsStructListArgumentModel.cs │ │ │ │ ├── OperandsDefaultsEnumListArgumentModel.cs │ │ │ │ ├── OperandsNoDefaultsEnumListArgumentModel.cs │ │ │ │ ├── OperandsNoDefaultsObjectListArgumentModel.cs │ │ │ │ ├── OperandsDefaultsObjectListArgumentModel.cs │ │ │ │ ├── ISampleTypesArgumentsModel.cs │ │ │ │ ├── OperandsNoDefaultsSampleTypesModel.cs │ │ │ │ ├── OperandsDefaultsSampleTypesModel.cs │ │ │ │ ├── NrtOperandsNoDefaultsSampleTypesModel.cs │ │ │ │ └── OptionsNoDefaultsSampleTypesModel.cs │ │ │ ├── Models.md │ │ │ └── ArgsAsParams │ │ │ │ ├── IArgsNoDefaultsSampleTypesMethod.cs │ │ │ │ └── IArgsDefaultsSampleTypesMethod.cs │ │ └── Operands_PipedInput_Tests.cs │ ├── TokenizerTests.cs │ ├── Suggestions │ │ └── CafeApp.cs │ ├── CustomReturnCodeTests.cs │ ├── ClassCommands │ │ └── InvalidInterceptorSignatureTests.cs │ └── OnRunCompletedTests.cs ├── XunitTestExtensions.cs ├── AppRunnerBuilderExtensions.cs ├── CmdNetDefaultTestConfig.cs ├── Utils │ └── AssemblyExtensions.cs ├── UnitTests │ ├── Parsing │ │ └── LevenshteinDistanceTests.cs │ └── Framework │ │ └── GetUnderlyingTypeTests.cs ├── Ambient.cs ├── CommandDotNet.NameCasing │ └── CaseChangerTests.cs ├── CommandDotNet.IoC │ └── IoCApp.cs └── .editorconfig ├── .editorconfig ├── CommandDotNet.NameCasing ├── Case.cs ├── CaseChanger.cs └── CommandDotNet.NameCasing.csproj ├── CommandDotNet.NewerReleasesAlerts ├── OverrideHttpRequestCallback.cs ├── MiddlewareSteps.cs └── CommandDotNet.NewerReleasesAlerts.csproj ├── CommandDotNet.DataAnnotations ├── MiddlewareSteps.cs ├── Resources.cs ├── CommandDotNet.DataAnnotations.csproj └── ResourcesProxy.cs ├── CommandDotNet.FluentValidation ├── MiddlewareSteps.cs ├── InvalidValidatorException.cs ├── Resources.cs └── CommandDotNet.FluentValidation.csproj ├── .mrGitTags └── config.json ├── .gitignore ├── CommandDotNet.Example.Tests ├── ExampleDefaultTestConfig.cs └── CommandDotNet.Example.Tests.csproj ├── .github ├── ISSUE_TEMPLATE │ └── config.yml ├── workflows │ ├── release.yml │ └── test.yml └── dependabot.yml ├── CommandDotNet.Spectre ├── Resources.cs └── CommandDotNet.Spectre.csproj ├── netlify.toml ├── CommandDotNet.IoC.Autofac ├── AutofacResolver.cs └── CommandDotNet.IoC.Autofac.csproj ├── coverlet.runsettings ├── CommandDotNet.IoC.MicrosoftDependencyInjection ├── MicrosoftDependencyInjectionResolver.cs └── CommandDotNet.IoC.MicrosoftDependencyInjection.csproj ├── CommandDotNet.Spectre.Testing └── TestConsoleInputExtensions.cs ├── CommandDotNet.IoC.SimpleInjector ├── CommandDotNet.IoC.SimpleInjector.csproj └── SimpleInjectorResolver.cs └── LICENSE /scripts/mkdocs-serve.sh: -------------------------------------------------------------------------------- 1 | mkdocs serve -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material == 9.5.48 -------------------------------------------------------------------------------- /CommandDotNet.Example/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | dotnet build -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !docs 3 | !mkdocs.yml 4 | !requirements.txt -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | template: home.html 3 | title: CommandDotNet 4 | --- 5 | -------------------------------------------------------------------------------- /scripts/tag-and-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | git tag $1 4 | git push origin $1 -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/images/logo.png -------------------------------------------------------------------------------- /nuget-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/nuget-icon.png -------------------------------------------------------------------------------- /images/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/images/social.png -------------------------------------------------------------------------------- /docs/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/banner.png -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/favicon.ico -------------------------------------------------------------------------------- /images/logo.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/images/logo.graffle -------------------------------------------------------------------------------- /images/definitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/images/definitions.png -------------------------------------------------------------------------------- /images/nuget-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/images/nuget-icon.png -------------------------------------------------------------------------------- /docs/img/definitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/definitions.png -------------------------------------------------------------------------------- /CommandDotNet/IArgumentModel.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | public interface IArgumentModel 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /docs/diagrams/ArgTermsExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/diagrams/ArgTermsExample.png -------------------------------------------------------------------------------- /docs/diagrams/MiddlewarePipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/diagrams/MiddlewarePipeline.png -------------------------------------------------------------------------------- /docs/diagrams/ArgumentParseBehavior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/diagrams/ArgumentParseBehavior.png -------------------------------------------------------------------------------- /docs/diagrams/ArgumentPerspectives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/diagrams/ArgumentPerspectives.png -------------------------------------------------------------------------------- /docs/img/parse-directive-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/parse-directive-screenshot.png -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/commands_2_git_pop.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: commands_2_git_pop 2 | $ git stash pop 3 | pop 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/commands_2_git_stash.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: commands_2_git_stash 2 | $ git stash 3 | stash 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | dotnet bin/Debug/net7.0/CommandDotNet.Example.dll $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} -------------------------------------------------------------------------------- /CommandDotNet.TestTools/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("CommandDotNet.Tests")] 4 | -------------------------------------------------------------------------------- /docs/img/parse-directive-screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/parse-directive-screenshot-2.png -------------------------------------------------------------------------------- /docs/img/parse-directive-screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bilal-fazlani/commanddotnet/HEAD/docs/img/parse-directive-screenshot-3.png -------------------------------------------------------------------------------- /CommandDotNet/Help/HelpTextStyle.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Help; 2 | 3 | public enum HelpTextStyle 4 | { 5 | Basic = 0, 6 | Detailed = 1 7 | } -------------------------------------------------------------------------------- /CommandDotNet/Help/IHelpProvider.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Help; 2 | 3 | public interface IHelpProvider 4 | { 5 | string GetHelpText(Command command); 6 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_types_login.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_types_login 2 | $ myapp.exe Login roy rogers 3 | u:roy p:***** 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/passwords_login_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: passwords_login_exe 2 | $ myapp.exe Login roy rogers 3 | u:roy p:***** 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/commands_default_command_process.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: commands_default_command_process 2 | $ dotnet myapp.dll 3 | 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/HandleErrorDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet; 4 | 5 | public delegate int HandleErrorDelegate(CommandContext? ctx, Exception exception); -------------------------------------------------------------------------------- /CommandDotNet/InterceptorExecutionDelegate.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CommandDotNet; 4 | 5 | public delegate Task InterceptorExecutionDelegate(); -------------------------------------------------------------------------------- /CommandDotNet/Tokens/TokenType.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Tokens; 2 | 3 | public enum TokenType 4 | { 5 | Directive = 0, 6 | Argument = 1, 7 | Separator = 2 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_default_no_directive.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_default_no_directive 2 | $ example.exe Add 1 1 3 | 2 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/passwords_prompt_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: passwords_prompt_exe 2 | $ myapp.exe Prompt roy 3 | password: 4 | u:roy p:rogers 5 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_composed_stash.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_composed_stash 2 | $ git.exe stash 3 | changes stashed 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/brun: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | dotnet build 3 | clear 4 | dotnet bin/Debug/netcoreapp2.0/CommandDotNet.Example.dll $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-140-default-commands-add.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-140-default-commands-add 2 | $ add.exe 40 20 3 | 60 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/piped_arguments_list_ids_only.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: piped_arguments_list_ids_only 2 | $ users.exe List -i 3 | a1 4 | b1 5 | c3 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_composed_stash_pop.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_composed_stash_pop 2 | $ git.exe stash pop 3 | stash popped 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/DefaultCommandAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet; 4 | 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public class DefaultCommandAttribute : Attribute; -------------------------------------------------------------------------------- /docs/Extensibility/parameter-types.md: -------------------------------------------------------------------------------- 1 | # Parameter Types 2 | 3 | See [Supported Argument Types > Adding support for new types](../Arguments/argument-types.md#adding-support-for-other-types) 4 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_custom_attribute_disabled.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_custom_attribute_disabled 2 | $ example.exe Subtract 1 1 3 | 0 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-100-calculator-add.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-100-calculator-add 2 | $ dotnet calculator.dll Add 40 20 3 | 60 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-700-pipes_sum.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-700-pipes_sum 2 | $ dotnet linq.dll Sum 1 2 3 4 3 | 1 4 | 3 5 | 6 6 | 10 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-800-ctrlc_sum.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-800-ctrlc_sum 2 | $ dotnet linq.dll Sum 1 2 3 4 3 | 1 4 | 3 5 | 6 6 | 10 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/CharExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | internal static class CharExtensions 4 | { 5 | internal static bool IsAlphaNumeric(this char c) => char.IsLetterOrDigit(c); 6 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/IParseError.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Parsing; 2 | 3 | public interface IParseError 4 | { 5 | public string Message { get; } 6 | public Command Command { get; } 7 | } -------------------------------------------------------------------------------- /localization_files/simple_json/en/CommandDotNet.DataAnnotations.json: -------------------------------------------------------------------------------- 1 | {"The {0} field|The field {0}|The {0} property|The property {0}":"The {0} field|The field {0}|The {0} property|The property {0}"} -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-700-pipes_range.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-700-pipes_range 2 | $ dotnet linq.dll Range 1 4 3 | 1 4 | 2 5 | 3 6 | 4 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-800-ctrlc_range.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-800-ctrlc_range 2 | $ dotnet linq.dll Range 1 4 3 | 1 4 | 2 5 | 3 6 | 4 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_composed_commit.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_composed_commit 2 | $ git.exe commit -m "some refactoring" 3 | Commit successful 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/AssertFailedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.TestTools; 4 | 5 | [Serializable] 6 | public class AssertFailedException(string message) : Exception(message); -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/piped_arguments_list.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: piped_arguments_list 2 | $ users.exe List 3 | a1 Avery (active) 4 | b1 Beatrix (active) 5 | c3 Cris (active) 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Parsing/IParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Parsing; 4 | 5 | internal interface IParser 6 | { 7 | object? Parse(IArgument argument, IEnumerable values); 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_no_missing_args.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_no_missing_args 2 | $ app.exe true true http://google.com http://google.com true http://google.com 3 | 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-100-calculator-add-invalid.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-100-calculator-add-invalid 2 | $ dotnet calculator.dll Add a 20 3 | 'a' is not a valid Number 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/IPromptResponder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.TestTools.Prompts; 4 | 5 | public interface IPromptResponder 6 | { 7 | ConsoleKeyInfo OnReadKey(ITestConsole testConsole); 8 | } -------------------------------------------------------------------------------- /CommandDotNet/INameAndDescription.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | public interface INameAndDescription 4 | { 5 | string? Name { get; } 6 | string? Description { get; } 7 | string[]? DescriptionLines { get; } 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_collection_missing_args.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_collection_missing_args 2 | $ app.exe 3 | requiredBool is required 4 | requiredRefType is required 5 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_exe_split_args_only.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_exe_split_args_only 2 | $ mission-control.exe LaunchRocket mars earth jupiter -c aaron,alex 3 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Extensions/IIndentableToString.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace CommandDotNet.Extensions; 4 | 5 | [PublicAPI] 6 | public interface IIndentableToString 7 | { 8 | string ToString(Indent indent); 9 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_collection_no_missing_args.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_collection_no_missing_args 2 | $ app.exe -b true -b true -u http://google.com -u http://google.com 3 | 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_windows_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_windows_exe 2 | $ mission-control.exe LaunchRocket /turbo /a true mars 3 | planet=mars turbo=True abort=True 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-140-default-commands-add-command-per-class.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-140-default-commands-add-command-per-class 2 | $ dotnet calculator.dll Add 40 20 3 | 60 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/build-win10-x64-exe.bat: -------------------------------------------------------------------------------- 1 | dotnet build -f netcoreapp3.1 2 | dotnet publish -f netcoreapp3.1 -r win10-x64 -c Release /p:PublishSingleFile=true 3 | copy bin\Release\netcoreapp3.1\win10-x64\publish\CommandDotNet.Example.exe . -------------------------------------------------------------------------------- /CommandDotNet.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "diagnosticMessages": false, 4 | "parallelizeTestCollections": false, 5 | "preEnumerateTheories": true 6 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Informationen zu EDITORCONFIG-Dateien finden Sie unter https://aka.ms/editorconfigdocs 2 | 3 | # All files 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | 8 | # Xml files 9 | [*.xml] 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_powershell_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_powershell_exe 2 | $ mission-control.exe LaunchRocket -turbo -a true mars 3 | planet=mars turbo=True abort=True 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Builders/ArgumentDefaults/GetArgumentKeysDelegate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Builders.ArgumentDefaults; 4 | 5 | public delegate IEnumerable GetArgumentKeysDelegate(IArgument argument); -------------------------------------------------------------------------------- /CommandDotNet/Builders/ICommandBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Builders; 2 | 3 | public interface ICommandBuilder 4 | { 5 | Command Command { get; } 6 | void AddSubCommand(Command command); 7 | void AddArgument(IArgument argument); 8 | } -------------------------------------------------------------------------------- /CommandDotNet.NameCasing/Case.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.NameCasing; 2 | 3 | public enum Case 4 | { 5 | DontChange = 0, 6 | LowerCase = 1, 7 | CamelCase = 2, 8 | KebabCase = 3, 9 | PascalCase = 4, 10 | SnakeCase = 5 11 | } -------------------------------------------------------------------------------- /CommandDotNet/ArgumentMode.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | public enum ArgumentMode 4 | { 5 | /// aka: positional arguments 6 | Operand = 0, 7 | 8 | /// aka: named arguments 9 | Option = 1 10 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_exe_split_directive_args_only.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_exe_split_directive_args_only 2 | $ mission-control.exe [split:-] LaunchRocket mars earth jupiter -c aaron-alex 3 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-500-interceptors-get.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-500-interceptors-get 2 | $ dotnet curl.dll -u me -p pwd Get http://mysite.com -v 3 | [GET] http://me:*****@mysite.com/ verbose=True 4 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Builders/IServicesContainer.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Builders; 2 | 3 | public interface IServicesContainer 4 | { 5 | /// The services used by middleware and associated with this object 6 | IServices Services { get; } 7 | } -------------------------------------------------------------------------------- /docs/img/home-bkg.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /scripts/mkdocs-snippets.sh: -------------------------------------------------------------------------------- 1 | 2 | # install the tool first 3 | # dotnet tool install -g MarkdownSnippets.Tool 4 | 5 | # extract snippets from code and insert to docs 6 | mdsnippets -c InPlaceOverwrite --url-prefix "https://github.com/bilal-fazlani/commanddotnet/blob/master" -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_pass_thru_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_pass_thru_help 2 | $ example.exe PassThru -h 3 | Usage: example.exe PassThru [] [[--] ...] 4 | 5 | Arguments: 6 | 7 | arg1 8 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_missing_args.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_missing_args 2 | $ app.exe 3 | RequiredBool is required 4 | RequiredRefType is required 5 | requiredBool is required 6 | requiredRefType is required 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/single-file-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | pushd bin/Debug/net7.0/osx-arm64/publish/ 4 | # CommandDotNet.Example.exe $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} 5 | ./CommandDotNet.Example $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} 6 | popd -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IConsoleBuffer.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Rendering; 2 | 3 | public interface IConsoleBuffer 4 | { 5 | int BufferWidth { get; set; } 6 | int BufferHeight { get; set; } 7 | void SetBufferSize(int width, int height); 8 | void Clear(); 9 | } -------------------------------------------------------------------------------- /docs/img/home-bkg-slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_exe 2 | $ mission-control.exe LaunchRocket mars earth jupiter -c aaron -c alex 3 | launching rocket 4 | planets: mars,earth,jupiter 5 | crew: aaron,alex 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/piped_arguments_options_notify.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: piped_arguments_options_notify 2 | $ users.exe list -i | users.exe Welcome c3 --notify $* 3 | welcome Cris 4 | notify: a1 Avery (active) 5 | notify: b1 Beatrix (active) 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-140-default-commands-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-140-default-commands-help 2 | $ add.exe Add -h 3 | Usage: add.exe 4 | 5 | Arguments: 6 | 7 | x 8 | 9 | y 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/piped_arguments_disable.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: piped_arguments_disable 2 | $ users.exe list -i | grep 1 | users.exe Disable c3 3 | disabled c3 Cris (inactive) 4 | disabled a1 Avery (inactive) 5 | disabled b1 Beatrix (inactive) 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/ISourceDef.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Builders; 2 | 3 | namespace CommandDotNet.ClassModeling.Definitions; 4 | 5 | internal interface ISourceDef : ICustomAttributesContainer 6 | { 7 | string Name { get; } 8 | string SourcePath { get; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IConsoleColor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.Rendering; 4 | 5 | public interface IConsoleColor 6 | { 7 | ConsoleColor BackgroundColor { get; set; } 8 | ConsoleColor ForegroundColor { get; set; } 9 | void ResetColor(); 10 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IStandardIn.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommandDotNet.Rendering; 4 | 5 | public interface IStandardIn 6 | { 7 | TextReader In { get; } 8 | 9 | void SetIn(TextReader newIn); 10 | 11 | bool IsInputRedirected { get; } 12 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IStandardOut.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommandDotNet.Rendering; 4 | 5 | public interface IStandardOut 6 | { 7 | TextWriter Out { get; } 8 | 9 | void SetOut(TextWriter newOut); 10 | 11 | bool IsOutputRedirected { get; } 12 | } -------------------------------------------------------------------------------- /scripts/mkdocs-update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # pip install --upgrade pip 4 | # pip install --upgrade pipenv 5 | 6 | # set envvar to work around bug with PYUP 7 | # export PIPENV_PYUP_API_KEY="" 8 | # pipenv check 9 | 10 | # pipenv update 11 | pip3 install -r requirements.txt -------------------------------------------------------------------------------- /CommandDotNet.NewerReleasesAlerts/OverrideHttpRequestCallback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | 5 | namespace CommandDotNet.NewerReleasesAlerts; 6 | 7 | public delegate Task OverrideHttpRequestCallback(HttpClient client, Uri requestUri); -------------------------------------------------------------------------------- /CommandDotNet.DataAnnotations/MiddlewareSteps.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Execution; 2 | 3 | namespace CommandDotNet.DataAnnotations; 4 | 5 | public static class MiddlewareSteps 6 | { 7 | public static MiddlewareStep DataAnnotations { get; set; } = Execution.MiddlewareSteps.ValidateArity - 1100; 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_exe_intermixed.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_exe_intermixed 2 | $ mission-control.exe LaunchRocket mars -c aaron earth -c alex jupiter 3 | launching rocket 4 | planets: mars,earth,jupiter 5 | crew: aaron,alex 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.FluentValidation/MiddlewareSteps.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Execution; 2 | 3 | namespace CommandDotNet.FluentValidation; 4 | 5 | public static class MiddlewareSteps 6 | { 7 | public static MiddlewareStep FluentValidation { get; set; } = Execution.MiddlewareSteps.ValidateArity-1000; 8 | } -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/IMethodDef.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommandDotNet.Execution; 3 | 4 | namespace CommandDotNet.ClassModeling.Definitions; 5 | 6 | internal interface IMethodDef : IInvocation 7 | { 8 | IReadOnlyCollection ArgumentDefs { get; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Prompts/IArgumentPrompter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Prompts; 4 | 5 | public interface IArgumentPrompter 6 | { 7 | ICollection PromptForArgumentValues(CommandContext commandContext, IArgument argument, out bool isCancellationRequested); 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-100-calculator-add-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-100-calculator-add-help 2 | $ dotnet calculator.dll Add -h 3 | Usage: dotnet calculator.dll Add 4 | 5 | Arguments: 6 | 7 | x 8 | 9 | y 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.FluentValidation/InvalidValidatorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.FluentValidation; 4 | 5 | internal class InvalidValidatorException : Exception 6 | { 7 | public InvalidValidatorException(string message, Exception exception) : base(message, exception) 8 | { } 9 | } -------------------------------------------------------------------------------- /CommandDotNet.NewerReleasesAlerts/MiddlewareSteps.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Execution; 2 | 3 | namespace CommandDotNet.NewerReleasesAlerts; 4 | 5 | public static class MiddlewareSteps 6 | { 7 | public static MiddlewareStep NewerReleaseAlerts { get; set; } = new(MiddlewareStages.PostParseInputPreBindValues); 8 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/IDefaultTestConfig.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.TestTools; 2 | 3 | /// Implement this to provide a TestConfig for tests 4 | public interface IDefaultTestConfig 5 | { 6 | /// The TestConfig to use as a default 7 | TestConfig Default { get; } 8 | } -------------------------------------------------------------------------------- /CommandDotNet/Builders/IDependencyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace CommandDotNet.Builders; 5 | 6 | public interface IDependencyResolver 7 | { 8 | object? Resolve(Type type); 9 | bool TryResolve(Type type, [NotNullWhen(true)] out object? item); 10 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/piped_arguments_options_notify_pipeto_directive.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: piped_arguments_options_notify_pipeto_directive 2 | $ users.exe list -i | users.exe [pipeto:***] Welcome c3 --notify *** 3 | welcome Cris 4 | notify: a1 Avery (active) 5 | notify: b1 Beatrix (active) 6 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Builders/Resources.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 2 | // ReSharper disable InconsistentNaming 3 | namespace CommandDotNet; 4 | 5 | public partial class Resources 6 | { 7 | public virtual string ValueSource_EnvVar => "EnvVar"; 8 | public virtual string ValueSource_AppSetting => "AppSetting"; 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/ExecutionMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CommandDotNet.Execution; 4 | 5 | public delegate Task ExecutionDelegate(CommandContext commandContext); 6 | 7 | public delegate Task ExecutionMiddleware( 8 | CommandContext context, 9 | ExecutionDelegate next); -------------------------------------------------------------------------------- /.mrGitTags/config.json: -------------------------------------------------------------------------------- 1 | {"SkippedCommitsByProject":{"CommandDotNet.FluentValidation":"211b4b813d8aabeba831429e98615a8b457799f0","CommandDotNet.NameCasing":"211b4b813d8aabeba831429e98615a8b457799f0","CommandDotNet.Spectre":"211b4b813d8aabeba831429e98615a8b457799f0","CommandDotNet.TestTools":"211b4b813d8aabeba831429e98615a8b457799f0"}} -------------------------------------------------------------------------------- /CommandDotNet/Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Extensions; 4 | 5 | internal static class CollectionExtensions 6 | { 7 | internal static void AddRange(this ICollection collection, IEnumerable items) => 8 | items.ForEach(collection.Add); 9 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-500-interceptors-get-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-500-interceptors-get-help 2 | $ dotnet curl.dll Get -h 3 | Usage: dotnet curl.dll Get [options] 4 | 5 | Arguments: 6 | 7 | uri 8 | 9 | Options: 10 | 11 | -v | --verbose 12 | // end-snippet -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | obj 3 | bin 4 | output 5 | .vscode 6 | CommandDotNet.sln.DotSettings.user 7 | .vs/ 8 | *.ncrunchsolution.user 9 | public 10 | site/ 11 | Pipfile.lock 12 | *.user 13 | /CommandDotNet.Example/CommandDotNet.Example.exe 14 | DevDefaultTestConfig.cs 15 | .DS_Store 16 | Pipfile 17 | TestResults/ 18 | -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/UnexpectedPromptFailureException.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.TestTools.Prompts; 2 | 3 | public class UnexpectedPromptFailureException : AssertFailedException 4 | { 5 | public UnexpectedPromptFailureException(string promptText) 6 | : base($"unexpected prompt: {promptText}") 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/IStructListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 4 | 5 | public interface IStructListArgumentModel : IArgumentModel 6 | { 7 | List? StructListArg { get; set; } 8 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/OnRunCompletedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.Execution; 4 | 5 | public class OnRunCompletedEventArgs(CommandContext commandContext) : EventArgs 6 | { 7 | public CommandContext CommandContext { get; } = commandContext ?? throw new ArgumentNullException(nameof(commandContext)); 8 | } -------------------------------------------------------------------------------- /CommandDotNet/Prompts/Resources.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 2 | // ReSharper disable InconsistentNaming 3 | 4 | namespace CommandDotNet; 5 | 6 | public partial class Resources 7 | { 8 | public virtual string Prompt_Enter_once_for_new_value_twice_to_finish => "[ once to begin new value. twice to finish]"; 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IConsoleCursor.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Rendering; 2 | 3 | public interface IConsoleCursor 4 | { 5 | int CursorSize { get; set; } 6 | bool CursorVisible { get; set; } 7 | int CursorLeft { get; set; } 8 | int CursorTop { get; set; } 9 | void SetCursorPosition(int left, int top); 10 | } -------------------------------------------------------------------------------- /CommandDotNet.DataAnnotations/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DataAnnotations; 2 | 3 | public class Resources 4 | { 5 | public static Resources A = new(); 6 | 7 | public virtual string Error_DataAnnotation_phrases_to_replace_with_argument_name() => 8 | "The {0} field|The field {0}|The {0} property|The property {0}"; 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Builders/ICustomAttributesContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace CommandDotNet.Builders; 4 | 5 | public interface ICustomAttributesContainer 6 | { 7 | /// The attributes defined on the method or class that define this object 8 | ICustomAttributeProvider CustomAttributes { get; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet/ConsoleExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | public static class ConsoleExtensions 4 | { 5 | public static void Write(this IConsole console, object? value = null) => console.Out.Write(value); 6 | 7 | public static void WriteLine(this IConsole console, object? value = null) => console.Out.WriteLine(value); 8 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_exe_argument_separator_passthru.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_exe_argument_separator_passthru 2 | $ mission-control.exe LaunchRocket mars -c alex -- additional args here 3 | launching rocket 4 | planets: mars 5 | crew: alex 6 | separated: additional,args,here 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/IEnumListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public interface IEnumListArgumentModel : IArgumentModel 7 | { 8 | List? EnumListArg { get; set; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/IObjectListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public interface IObjectListArgumentModel : IArgumentModel 7 | { 8 | List? ObjectListArg { get; set; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-140-default-commands-add-help-command-per-class.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-140-default-commands-add-help-command-per-class 2 | $ dotnet calculator.dll Add -h 3 | Usage: dotnet calculator.dll Add 4 | 5 | Arguments: 6 | 7 | x 8 | 9 | y 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Prompts/ConsoleKeyInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.Prompts; 4 | 5 | internal static class ConsoleKeyInfoExtensions 6 | { 7 | internal static bool IsCtrlC(this ConsoleKeyInfo key) 8 | { 9 | return key.Key == ConsoleKey.C && key.Modifiers.HasFlag(ConsoleModifiers.Control); 10 | } 11 | } -------------------------------------------------------------------------------- /localization_files/simple_json/en/CommandDotNet.Spectre.json: -------------------------------------------------------------------------------- 1 | {"[grey](Move up and down to reveal more {0})[/]":"[grey](Move up and down to reveal more {0})[/]","[grey](Press [blue]\u003Cspace\u003E[/] to toggle the {0}, [green]\u003Center\u003E[/] to accept)[/]":"[grey](Press [blue]\u003Cspace\u003E[/] to toggle the {0}, [green]\u003Center\u003E[/] to accept)[/]"} -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_collections_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_collections_help 2 | $ mission-control.exe LaunchRocket --help 3 | Usage: mission-control.exe LaunchRocket [options] 4 | 5 | Arguments: 6 | 7 | planets (Multiple) 8 | 9 | Options: 10 | 11 | -c | --crew (Multiple) 12 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/commands_calculator_sum_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: commands_calculator_sum_help 2 | $ dotnet calculator.dll Sum --help 3 | sums all the numbers provided 4 | 5 | Usage: sum [ ...] 6 | 7 | Arguments: 8 | 9 | numbers (Multiple) 10 | 11 | more details and examples could be provided here 12 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Scenarios/IScenario.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.TestTools.Scenarios; 2 | 3 | /// A command line scenario 4 | public interface IScenario 5 | { 6 | /// The starting context 7 | ScenarioWhen When { get; } 8 | 9 | /// The expectations 10 | ScenarioThen Then { get; } 11 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/ExpectedFlagParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Tokens; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | [PublicAPI] 7 | public record ExpectedFlagParseError( 8 | Command Command, 9 | Token ClubbedToken, 10 | string ShortName, 11 | Option? Option, 12 | string Message) 13 | : IParseError; -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_pass_thru_option_mask_separator.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_pass_thru_option_mask_separator 2 | $ example.exe PassThru expected -- pass-thru 3 | IgnoreUnexpectedOperands: False 4 | DefaultArgumentSeparatorStrategy: EndOfOptions 5 | 6 | arg1: expected 7 | separated: pass-thru 8 | remaining: 9 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-100-calculator-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-100-calculator-help 2 | $ dotnet calculator.dll --help 3 | Usage: dotnet calculator.dll [command] 4 | 5 | Commands: 6 | 7 | Add 8 | Subtract 9 | 10 | Use "dotnet calculator.dll [command] --help" for more information about a command. 11 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_end_of_options_option_mask_no_separator.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_end_of_options_option_mask_no_separator 2 | $ example.exe EndOfOptions --option-mask 3 | Unrecognized option '--option-mask' 4 | 5 | Usage: example.exe EndOfOptions [] 6 | 7 | Arguments: 8 | 9 | arg1 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/XunitTestExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace CommandDotNet.Tests; 5 | 6 | public static class XunitTestExtensions 7 | { 8 | public static IEnumerable ToObjectArrays(this IEnumerable items) 9 | { 10 | return items.Select(item => new object[] { item! }); 11 | } 12 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_end_of_options_unexpected_operand.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_end_of_options_unexpected_operand 2 | $ example.exe EndOfOptions expected unexpected 3 | Unrecognized command or argument 'unexpected' 4 | 5 | Usage: example.exe EndOfOptions [] 6 | 7 | Arguments: 8 | 9 | arg1 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IConsoleWindow.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Rendering; 2 | 3 | public interface IConsoleWindow 4 | { 5 | int WindowLeft { get; set; } 6 | int WindowTop { get; set; } 7 | int WindowWidth { get; set; } 8 | int WindowHeight { get; set; } 9 | void SetWindowPosition(int left, int top); 10 | void SetWindowSize(int width, int height); 11 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_end_of_options_option_mask_separator.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_end_of_options_option_mask_separator 2 | $ example.exe EndOfOptions -- --option-mask 3 | IgnoreUnexpectedOperands: False 4 | DefaultArgumentSeparatorStrategy: EndOfOptions 5 | 6 | arg1: --option-mask 7 | separated: --option-mask 8 | remaining: 9 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_separator_end_of_options_unexpected_operand_ignored.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_separator_end_of_options_unexpected_operand_ignored 2 | $ example.exe EndOfOptions expected unexpected 3 | IgnoreUnexpectedOperands: True 4 | DefaultArgumentSeparatorStrategy: EndOfOptions 5 | 6 | arg1: expected 7 | separated: 8 | remaining: unexpected 9 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-120-subcommands-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-120-subcommands-help 2 | $ dotnet calculator.dll --help 3 | Usage: dotnet calculator.dll [command] 4 | 5 | Commands: 6 | 7 | Add 8 | Algo 9 | Subtract 10 | Trig 11 | 12 | Use "dotnet calculator.dll [command] --help" for more information about a command. 13 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-120-subcommands-trig-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-120-subcommands-trig-help 2 | $ dotnet calculator.dll Trig --help 3 | Usage: dotnet calculator.dll Trig [command] 4 | 5 | Commands: 6 | 7 | Cosine 8 | Sine 9 | 10 | Use "dotnet calculator.dll Trig [command] --help" for more information about a command. 11 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-300-calculator-add-basic-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-300-calculator-add-basic-help 2 | $ dotnet calculator.dll Add -h 3 | Adds two numbers 4 | 5 | Usage: Add 1 2 6 | dotnet calculator.dll Add 1 2 7 | 8 | Arguments: 9 | x first value 10 | y second value 11 | 12 | single line of extended help here 13 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/NrtOperandsNoDefaultsStructListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 4 | 5 | public class NrtOperandsNoDefaultsStructListArgumentModel : IStructListArgumentModel 6 | { 7 | [Operand] 8 | public List? StructListArg { get; set; } 9 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsDefaultsStructListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 4 | 5 | public class OperandsDefaultsStructListArgumentModel : IStructListArgumentModel 6 | { 7 | [Operand] 8 | public List? StructListArg { get; set; } = [3, 4]; 9 | } -------------------------------------------------------------------------------- /CommandDotNet/Extensions/CustomAttributeProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace CommandDotNet.Extensions; 5 | 6 | internal static class CustomAttributeProviderExtensions 7 | { 8 | internal static bool HasAttribute(this ICustomAttributeProvider attributeProvider) where T : Attribute => 9 | attributeProvider.IsDefined(typeof(T), true); 10 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/exceptions_throw.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: exceptions_throw 2 | $ example.exe Throw yikes 3 | System.ArgumentException: yikes (Parameter 'message') 4 | Properties: 5 | Message: yikes (Parameter 'message') 6 | ParamName: message 7 | Data: 8 | method: Throw 9 | 10 | Usage: example.exe Throw 11 | 12 | Arguments: 13 | 14 | message 15 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-140-default-commands-help-command-per-class.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-140-default-commands-help-command-per-class 2 | $ dotnet calculator.dll --help 3 | Usage: dotnet calculator.dll [command] 4 | 5 | Commands: 6 | 7 | Add 8 | Subtract 9 | 10 | Use "dotnet calculator.dll [command] --help" for more information about a command. 11 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Tokens/ICommandLineStringSplitter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Tokens; 4 | 5 | public interface ICommandLineStringSplitter 6 | { 7 | // copied from System.CommandLine 8 | // https://github.com/dotnet/command-line-api/blob/master/src/System.CommandLine/Parsing/ICommandLineStringSplitter.cs 9 | 10 | IEnumerable Split(string commandLine); 11 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-300-calculator-add-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-300-calculator-add-help 2 | $ dotnet calculator.dll Add -h 3 | Adds two numbers 4 | 5 | Usage: Add 1 2 6 | dotnet calculator.dll Add 1 2 7 | 8 | Arguments: 9 | 10 | x 11 | first value 12 | 13 | y 14 | second value 15 | 16 | single line of extended help here 17 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_nested_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_nested_help 2 | $ git.exe -h 3 | Fake git application 4 | 5 | Usage: git.exe [command] 6 | 7 | Commands: 8 | 9 | commit Commits all staged changes 10 | stash Stashes all changes when executed without any arguments 11 | 12 | Use "git.exe [command] --help" for more information about a command. 13 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/NrtOperandsNoDefaultsEnumListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class NrtOperandsNoDefaultsEnumListArgumentModel : IEnumListArgumentModel 7 | { 8 | [Operand] 9 | public List? EnumListArg { get; set; } 10 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/NrtOperandsNoDefaultsObjectListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class NrtOperandsNoDefaultsObjectListArgumentModel : IObjectListArgumentModel 7 | { 8 | [Operand] 9 | public List? ObjectListArg { get; set; } 10 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/dataannotations-1-table-create-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: dataannotations-1-table-create-help 2 | $ dotnet table.dll create --help 3 | Usage: dotnet table.dll create [options] 4 | 5 | Arguments: 6 | 7 | name 8 | 9 | Options: 10 | 11 | --owner 12 | 13 | --server 14 | 15 | -s | --silent 16 | 17 | -v | --verbose 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_composed_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_composed_help 2 | $ git.exe -h 3 | Fake git application 4 | 5 | Usage: git.exe [command] 6 | 7 | Commands: 8 | 9 | commit Commits all staged changes 10 | stash Stashes all changes when executed without any arguments 11 | 12 | Use "git.exe [command] --help" for more information about a command. 13 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/data_annotations_validation_create_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: data_annotations_validation_create_help 2 | $ dotnet table.dll create --help 3 | Usage: dotnet table.dll create [options] 4 | 5 | Arguments: 6 | 7 | name 8 | 9 | Options: 10 | 11 | --owner 12 | 13 | --server 14 | 15 | -q | --quiet 16 | 17 | -v | --verbose 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/IArgumentArity.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | /// Arity describes how many values a user can or must provide for an argument 4 | public interface IArgumentArity 5 | { 6 | /// The minimum number of values the user must provide 7 | int Minimum { get; } 8 | 9 | /// The maximum number of values the user must provide 10 | int Maximum { get; } 11 | } -------------------------------------------------------------------------------- /CommandDotNet.NameCasing/CaseChanger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.NameCasing; 4 | 5 | public class CaseChanger 6 | { 7 | private readonly Func _changeCase; 8 | 9 | public CaseChanger(Func changeCase) => 10 | _changeCase = changeCase ?? throw new ArgumentNullException(nameof(changeCase)); 11 | 12 | public string ChangeCase(string name) => _changeCase(name); 13 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_with_model_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_with_model_help 2 | $ myapp.exe Notify --help 3 | Usage: myapp.exe Notify [options] 4 | 5 | Arguments: 6 | 7 | Message 8 | 9 | Recipients (Multiple) 10 | 11 | Options: 12 | 13 | --dryrun 14 | 15 | -v | --Verbose 16 | 17 | -q | --Quite 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-500-interceptors-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-500-interceptors-help 2 | $ dotnet curl.dll --help 3 | Usage: dotnet curl.dll [command] [options] 4 | 5 | Options: 6 | 7 | -u | --username 8 | 9 | -p | --password 10 | 11 | Commands: 12 | 13 | Get 14 | 15 | Use "dotnet curl.dll [command] --help" for more information about a command. 16 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Help/UsageAppNameStyle.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Help; 2 | 3 | public enum UsageAppNameStyle 4 | { 5 | /// 6 | /// else if the file extension is '.exe'
7 | /// else 8 | ///
9 | Adaptive, 10 | 11 | /// "dotnet {fileName}" 12 | DotNet, 13 | 14 | /// "{fileName}" 15 | Executable 16 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_without_model_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_without_model_help 2 | $ myapp.exe Notify --help 3 | Usage: myapp.exe Notify [options] 4 | 5 | Arguments: 6 | 7 | message 8 | 9 | recipients (Multiple) 10 | 11 | Options: 12 | 13 | --dryrun 14 | 15 | -v | --verbose 16 | 17 | -q | --quiet 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsNoDefaultsStructListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | #pragma warning disable 8767 4 | 5 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 6 | 7 | public class OperandsNoDefaultsStructListArgumentModel : IStructListArgumentModel 8 | { 9 | [Operand] 10 | public List StructListArg { get; set; } = null!; 11 | } -------------------------------------------------------------------------------- /CommandDotNet/Builders/CommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace CommandDotNet.Builders; 4 | 5 | [PublicAPI] 6 | public class CommandBuilder(Command command) : ICommandBuilder 7 | { 8 | public Command Command { get; } = command; 9 | 10 | public void AddSubCommand(Command command) => Command.AddArgumentNode(command); 11 | 12 | public void AddArgument(IArgument argument) => Command.AddArgumentNode(argument); 13 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_with_interceptor_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_with_interceptor_help 2 | $ myapp.exe Notify --help 3 | Usage: myapp.exe Notify [options] 4 | 5 | Arguments: 6 | 7 | Message 8 | 9 | Recipients (Multiple) 10 | 11 | Options: 12 | 13 | --dryrun 14 | 15 | -v | --Verbose 16 | 17 | -q | --Quite 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsDefaultsEnumListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class OperandsDefaultsEnumListArgumentModel : IEnumListArgumentModel 7 | { 8 | [Operand] 9 | public List? EnumListArg { get; set; } = [DayOfWeek.Monday, DayOfWeek.Tuesday]; 10 | } -------------------------------------------------------------------------------- /CommandDotNet/BooleanMode.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | /// 4 | /// When Explicit, boolean options require a 'true' or 'false' value be specified.
5 | /// When Implicit, boolean options are treated as Flags, considered false unless it's specified 6 | /// and the next argument will be considered a new argument. 7 | ///
8 | public enum BooleanMode 9 | { 10 | Implicit = 1, 11 | Explicit = 2 12 | } -------------------------------------------------------------------------------- /CommandDotNet/Diagnostics/CommandLoggerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Diagnostics; 5 | 6 | internal record CommandLoggerConfig( 7 | Func?>? WriterFactory, 8 | bool IncludeSystemInfo, 9 | bool IncludeAppConfig, 10 | bool IncludeMachineAndUser, 11 | Func?>? AdditionalHeadersCallback); -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/dataannotations-1-table-create.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: dataannotations-1-table-create 2 | $ dotnet hr.dll create TooLongTableName --server bossman --owner abc -sv 3 | silent and verbose are mutually exclusive. There can be only one! 4 | 'server' is not a valid fully-qualified http, https, or ftp URL. 5 | 'name' must be a string or array type with a maximum length of '10'. 6 | 'owner' is not a valid e-mail address. 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-other-features_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-other-features_help 2 | $ dotnet calculator.dll --help 3 | Usage: dotnet calculator.dll [command] [options] 4 | 5 | Options: 6 | 7 | -v | --version 8 | Show version information 9 | 10 | Commands: 11 | 12 | Add 13 | Subtract 14 | 15 | Use "dotnet calculator.dll [command] --help" for more information about a command. 16 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example.Tests/ExampleDefaultTestConfig.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.TestTools; 2 | 3 | namespace CommandDotNet.Example.Tests; 4 | 5 | public class ExampleDefaultTestConfig : IDefaultTestConfig 6 | { 7 | public TestConfig Default => new() 8 | { 9 | //PrintCommandDotNetLogs = true, 10 | OnSuccess = {Print = {ConsoleOutput = true}}, 11 | OnError = {Print = {ConsoleOutput = true, CommandContext = true}} 12 | }; 13 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Operands_PipedInput_Tests.cs: -------------------------------------------------------------------------------- 1 | using Xunit.Abstractions; 2 | 3 | namespace CommandDotNet.Tests.FeatureTests.Arguments; 4 | 5 | public class Operands_PipedInput_Tests : AppendPipedInputTestsBase 6 | { 7 | public Operands_PipedInput_Tests(ITestOutputHelper output) : base(output) 8 | { 9 | } 10 | 11 | protected override AppRunner AppRunner() where T : class => 12 | new AppRunner(); 13 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_with_model_composed_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_with_model_composed_help 2 | $ myapp.exe Notify --help 3 | Usage: myapp.exe Notify [options] 4 | 5 | Arguments: 6 | 7 | Message 8 | 9 | Recipients (Multiple) 10 | 11 | Options: 12 | 13 | --dryrun 14 | 15 | -v | --Verbose 16 | 17 | -q | --Quite 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_with_nested_operands_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_with_nested_operands_help 2 | $ myapp.exe Notify --help 3 | Usage: myapp.exe Notify [options] 4 | 5 | Arguments: 6 | 7 | Message 8 | 9 | Recipients (Multiple) 10 | 11 | Options: 12 | 13 | --dryrun 14 | 15 | -v | --Verbose 16 | 17 | -q | --Quite 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/subcommands_git_composed_stash_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: subcommands_git_composed_stash_help 2 | $ git.exe stash -h 3 | Stashes all changes when executed without any arguments 4 | 5 | Usage: git.exe stash [command] 6 | 7 | Commands: 8 | 9 | list Lists all stashed changes 10 | pop Applies last stashed changes 11 | 12 | Use "git.exe stash [command] --help" for more information about a command. 13 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/Commands/Math.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace CommandDotNet.Example.Commands; 5 | 6 | public class Math 7 | { 8 | public void Add(IConsole console, int x, int y) => console.WriteLine(x + y); 9 | public void AddRange(IConsole console, IEnumerable values) => console.WriteLine(values.Sum()); 10 | public void Subtract(IConsole console, int x, int y) => console.WriteLine(x - y); 11 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsNoDefaultsEnumListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | #pragma warning disable 8767 5 | 6 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 7 | 8 | public class OperandsNoDefaultsEnumListArgumentModel : IEnumListArgumentModel 9 | { 10 | [Operand] 11 | public List EnumListArg { get; set; } = null!; 12 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsNoDefaultsObjectListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | #pragma warning disable 8767 5 | 6 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 7 | 8 | public class OperandsNoDefaultsObjectListArgumentModel : IObjectListArgumentModel 9 | { 10 | [Operand] 11 | public List ObjectListArg { get; set; } = null!; 12 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/IAllowedValuesTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.TypeDescriptors; 4 | 5 | /// 6 | /// Implement for TypeDescriptors to provide a list of values for the help command 7 | /// 8 | public interface IAllowedValuesTypeDescriptor 9 | { 10 | /// The values allowed for the given type 11 | IEnumerable GetAllowedValues(IArgument argument); 12 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/AppRunnerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.Tests; 4 | 5 | public static class AppRunnerBuilderExtensions 6 | { 7 | public static AppRunner OnCommandCreated(this AppRunner appRunner, Action onCommandCreated) 8 | { 9 | return appRunner.Configure(cfg => 10 | cfg.BuildEvents.OnCommandCreated += args => 11 | onCommandCreated(args.CommandBuilder.Command)); 12 | } 13 | } -------------------------------------------------------------------------------- /scripts/rename-snippets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | rename() { 4 | echo "$1" 5 | sed -br -i 's///g' "$1" 6 | sed -br -i 's///g' "$1" 7 | sed -br -i 's///g' "$1" 8 | } 9 | 10 | find -type f -path '*/docs/*' -name "*.md" -print0 | while IFS= read -r -d $'\0' file; do 11 | rename "$file"; 12 | done 13 | 14 | 15 | find -type f -path '*/CommandDotNet.DocExamples/*' -name "*.cs" -print0 | while IFS= read -r -d $'\0' file; do 16 | rename "$file"; 17 | done -------------------------------------------------------------------------------- /localization_files/simple_json/en/CommandDotNet.FluentValidation.json: -------------------------------------------------------------------------------- 1 | {"\u0027{0}\u0027 is invalid":"\u0027{0}\u0027 is invalid","Could not create instance of {0}. Please ensure it\u0027s injected via IoC or has a default constructor.\nThis exception could also occur if default constructor threw an exception":"Could not create instance of {0}. Please ensure it\u0027s injected via IoC or has a default constructor.\nThis exception could also occur if default constructor threw an exception"} -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/data_annotations_validation_create_invalid.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: data_annotations_validation_create_invalid 2 | $ dotnet table.dll create TooLongTableName --server bossman --owner abc -qv 3 | 'server' is not a valid fully-qualified http, https, or ftp URL. 4 | 'name' must be a string or array type with a maximum length of '10'. 5 | 'owner' is not a valid e-mail address. 6 | quiet and verbose are mutually exclusive. There can be only one! 7 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Extensions/DisposableAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.Extensions; 4 | 5 | internal class DisposableAction : IDisposable 6 | { 7 | internal static DisposableAction Null { get; } = new(); 8 | 9 | private readonly Action? _action; 10 | 11 | private DisposableAction() 12 | { 13 | } 14 | 15 | public DisposableAction(Action action) => _action = action.ThrowIfNull(); 16 | 17 | public void Dispose() => _action?.Invoke(); 18 | } -------------------------------------------------------------------------------- /CommandDotNet.Example/InteractiveMiddleware.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Example; 2 | 3 | public static class InteractiveMiddleware 4 | { 5 | public static AppRunner UseInteractiveMode(this AppRunner appRunner, string appName) 6 | { 7 | return appRunner.Configure(c => 8 | { 9 | // use the existing appRunner to reuse the configuration. 10 | c.UseParameterResolver(ctx => new InteractiveSession(appRunner, appName, ctx)); 11 | }); 12 | } 13 | } -------------------------------------------------------------------------------- /CommandDotNet/Help/HelpTextProviderFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Help; 2 | 3 | internal static class HelpTextProviderFactory 4 | { 5 | internal static IHelpProvider Create(AppSettings appSettings) => 6 | appSettings.Help.TextStyle switch 7 | { 8 | HelpTextStyle.Basic => new BasicHelpTextProvider(appSettings), 9 | HelpTextStyle.Detailed => new HelpTextProvider(appSettings), 10 | _ => new HelpTextProvider(appSettings) 11 | }; 12 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/IStandardError.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommandDotNet.Rendering; 4 | 5 | public interface IStandardError 6 | { 7 | /// Gets the standard error output stream. 8 | TextWriter Error { get; } 9 | 10 | void SetError(TextWriter newError); 11 | 12 | /// Gets a value that indicates whether the error output stream has been redirected from the standard error stream. 13 | bool IsErrorRedirected { get; } 14 | } -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/ICommandDef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.ClassModeling.Definitions; 5 | 6 | internal interface ICommandDef: ISourceDef 7 | { 8 | Type? CommandHostClassType { get; } 9 | bool IsExecutable { get; } 10 | bool HasInterceptor { get; } 11 | IReadOnlyCollection SubCommands { get; } 12 | IMethodDef? InterceptorMethodDef { get; } 13 | IMethodDef? InvokeMethodDef { get; } 14 | } -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/ServicesContainerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.ClassModeling.Definitions; 2 | 3 | internal static class ServicesContainerExtensions 4 | { 5 | internal static ICommandDef? GetCommandDef(this Command servicesContainer) => 6 | servicesContainer.Services.GetOrDefault(); 7 | 8 | internal static IArgumentDef? GetArgumentDef(this IArgument servicesContainer) => 9 | servicesContainer.Services.GetOrDefault(); 10 | } -------------------------------------------------------------------------------- /CommandDotNet/Rendering/ForwardingTextWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using JetBrains.Annotations; 5 | 6 | namespace CommandDotNet.Rendering; 7 | 8 | [PublicAPI] 9 | public class ForwardingTextWriter(Action write) : TextWriter 10 | { 11 | public override Encoding Encoding { get; } = Encoding.Default; 12 | 13 | public override void Write(string? value) => write(value); 14 | 15 | public override void Write(char value) => write(value.ToString()); 16 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Question / Discussion 4 | url: https://github.com/bilal-fazlani/commanddotnet/discussions 5 | about: Ask questions and discuss ideas with the community 6 | - name: 💬 Discord Chat 7 | url: https://discord.gg/QFxKSeG 8 | about: Chat with the community and maintainers 9 | - name: 📚 Documentation 10 | url: https://commanddotnet.bilal-fazlani.com 11 | about: Read the comprehensive documentation 12 | -------------------------------------------------------------------------------- /CommandDotNet.Spectre/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Spectre; 2 | 3 | public class Resources 4 | { 5 | public static Resources A = new(); 6 | 7 | public virtual string Selection_paging_instructions(string argumentName) => 8 | $"[grey](Move up and down to reveal more {argumentName})[/]"; 9 | 10 | public virtual string MultiSelection_selection_instructions(string argumentName) => 11 | $"[grey](Press [blue][/] to toggle the {argumentName}, [green][/] to accept)[/]"; 12 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_help 2 | $ mission-control.exe LaunchRocket --help 3 | Usage: mission-control.exe LaunchRocket [options] 4 | 5 | Arguments: 6 | 7 | planet 8 | Name of the planet you wish the rocket to go 9 | 10 | Options: 11 | 12 | -t | --turbo 13 | Do you want to go fast? 14 | 15 | -a | --abort 16 | Abort the launch before takeoff 17 | Allowed values: true, false 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsDefaultsObjectListArgumentModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class OperandsDefaultsObjectListArgumentModel : IObjectListArgumentModel 7 | { 8 | [Operand] 9 | public List? ObjectListArg { get; set; } = 10 | [ 11 | new Uri("http://google.com"), 12 | new Uri("http://github.com") 13 | ]; 14 | } -------------------------------------------------------------------------------- /docs/Diagnostics/app-version.md: -------------------------------------------------------------------------------- 1 | # App Version 2 | 3 | Enable the feature with `appRunner.UseVersionMiddleware()` or `appRunner.UseDefaultMiddleware()`. 4 | 5 | Example: 6 | 7 | ```bash 8 | $ dotnet example.dll -v 9 | example.dll 10 | 1.0.0 11 | ``` 12 | 13 | By default, the version info is taken from the entry assembly's metadata. 14 | 15 | A version can be provided via 16 | 17 | ```cs 18 | var version = new VersionInfo("blah", "1.0.0"); 19 | appRunner.Configure(c => c.Services.AddOrUpdate(version)) 20 | ``` 21 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/argument_models_notify_with_invalid_nested_operands_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: argument_models_notify_with_invalid_nested_operands_help 2 | $ myapp.exe Notify --help 3 | CommandDotNet.InvalidConfigurationException: Operand property must be attributed with OperandAttribute or OrderByPositionInClassAttribute to guarantee consistent order. Properties: 4 | CommandDotNet.DocExamples.Arguments.Arguments.Argument_Models+Program_WithInvalidhNestedOperands+NotifyModel.NotificationArgs 5 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_alt_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_alt_help 2 | $ mission-control.exe LaunchRocket --help 3 | Usage: mission-control.exe LaunchRocket [options] 4 | 5 | Arguments: 6 | 7 | planet 8 | Name of the planet you wish the rocket to go 9 | 10 | Options: 11 | 12 | -t | --turbo 13 | Do you want to go fast? 14 | 15 | -a | --abort 16 | Abort the launch before takeoff 17 | Allowed values: true, false 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/DocExamplesDefaultTestConfig.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Builders; 2 | using CommandDotNet.TestTools; 3 | 4 | namespace CommandDotNet.DocExamples; 5 | 6 | public class DocExamplesDefaultTestConfig : IDefaultTestConfig 7 | { 8 | public TestConfig Default => new() 9 | { 10 | AppInfoOverride = new AppInfo( 11 | false, false, false, 12 | typeof(DocExamplesDefaultTestConfig).Assembly, 13 | "doc-examples.dll", "doc-examples.dll", "1.1.1.1") 14 | }; 15 | } -------------------------------------------------------------------------------- /CommandDotNet/ValueProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// Proxy to get and set the value of an argument 7 | [PublicAPI] 8 | public class ValueProxy(Func getter, Action setter) 9 | { 10 | /// The function to get the value 11 | public Func Getter { get; } = getter; 12 | 13 | /// The function to set the value 14 | public Action Setter { get; } = setter; 15 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/ValueParsingException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | /// indicates user error. 7 | [PublicAPI] 8 | public class ValueParsingException : Exception 9 | { 10 | public ValueParsingException(string message) : base(message) 11 | { 12 | } 13 | 14 | public ValueParsingException(string message, Exception innerException) : base(message, innerException) 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_windows_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_windows_help 2 | $ mission-control.exe LaunchRocket --help 3 | Usage: mission-control.exe LaunchRocket [options] 4 | 5 | Arguments: 6 | 7 | planet 8 | Name of the planet you wish the rocket to go 9 | 10 | Options: 11 | 12 | -t | --turbo 13 | Do you want to go fast? 14 | 15 | -a | --abort 16 | Abort the launch before takeoff 17 | Allowed values: true, false 18 | // end-snippet -------------------------------------------------------------------------------- /docs/Localization/culture-directive.md: -------------------------------------------------------------------------------- 1 | # Culture Directive 2 | 3 | ## TLDR, How to enable 4 | Enable the feature with `appRunner.UseLocalizeDirective()`. 5 | 6 | !!! Note 7 | The method is called `UseLocalizeDirective()` but the directive syntax is `[culture:name]`. 8 | 9 | ## Culture Directive 10 | 11 | When the `[culture:name]` [directive](../Extensibility/directives.md) is used, the `CultureInfo.CurrentCulture` is set to the specified culture. 12 | 13 | ```bash 14 | $ dotnet example.dll [culture:en-gb] Add 1 2 15 | ``` 16 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_attributes_powershell_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_attributes_powershell_help 2 | $ mission-control.exe LaunchRocket --help 3 | Usage: mission-control.exe LaunchRocket [options] 4 | 5 | Arguments: 6 | 7 | planet 8 | Name of the planet you wish the rocket to go 9 | 10 | Options: 11 | 12 | -t | --turbo 13 | Do you want to go fast? 14 | 15 | -a | --abort 16 | Abort the launch before takeoff 17 | Allowed values: true, false 18 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Scenarios/Scenario.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | 3 | namespace CommandDotNet.TestTools.Scenarios; 4 | 5 | public class Scenario : IScenario 6 | { 7 | /// The user input 8 | public ScenarioWhen When { get; } = new(); 9 | 10 | /// The expectations 11 | public ScenarioThen Then { get; } = new(); 12 | 13 | public override string ToString() 14 | { 15 | return $"{nameof(Scenario)}: args={When.Args ?? When.ArgsArray?.ToCsv(" ")}"; 16 | } 17 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/fluent_validation_create_invalid.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: fluent_validation_create_invalid 2 | $ dotnet table.dll create TooLongTableName --server bossman --owner abc -qv 3 | 'Table' is invalid 4 | The length of 'Name' must be 10 characters or fewer. You entered 16 characters. 5 | 'Owner' is not a valid email address. 6 | 'Host' is invalid 7 | sever is not a valid fully-qualified http, https, or ftp URL 8 | 'Verbosity' is invalid 9 | quiet and verbose are mutually exclusive. There can be only one! 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/OtherFeatures/Spectre_Examples.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | 3 | namespace CommandDotNet.DocExamples.OtherFeatures; 4 | 5 | public static class Spectre_Examples 6 | { 7 | // begin-snippet: spectre_ansi_console_usage 8 | public class Calculator 9 | { 10 | public void Sum(IAnsiConsole console, int x, int y) 11 | { 12 | var result = x + y; 13 | console.MarkupLine($"[green]{x}[/] + [green]{y}[/] = [bold yellow]{result}[/]"); 14 | } 15 | } 16 | // end-snippet 17 | } 18 | -------------------------------------------------------------------------------- /CommandDotNet/CommandDotNet.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | Yes 3 | True -------------------------------------------------------------------------------- /CommandDotNet/OriginalInput.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommandDotNet.Tokens; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet; 6 | 7 | [PublicAPI] 8 | public class OriginalInput(string[] args, TokenCollection tokens) 9 | { 10 | /// The original string array passed to the program 11 | public IReadOnlyCollection Args { get; } = args; 12 | 13 | /// The original tokens before any input transformations were applied 14 | public TokenCollection Tokens { get; } = tokens; 15 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/CmdNetDefaultTestConfig.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Builders; 2 | using CommandDotNet.TestTools; 3 | 4 | namespace CommandDotNet.Tests; 5 | 6 | public class CmdNetDefaultTestConfig : IDefaultTestConfig 7 | { 8 | public TestConfig Default => new() 9 | { 10 | OnError = {Print = {ConsoleOutput = true, CommandContext = true}}, 11 | AppInfoOverride = new AppInfo( 12 | false, false, false, 13 | typeof(CmdNetDefaultTestConfig).Assembly, 14 | "testhost.dll", "testhost.dll", "1.1.1.1") 15 | }; 16 | } -------------------------------------------------------------------------------- /CommandDotNet/ExitCodes.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CommandDotNet; 4 | 5 | // begin-snippet: ExitCodes_class 6 | public static class ExitCodes 7 | { 8 | public static int Success => 0; 9 | public static int Error => 1; 10 | public static int ValidationError => 2; 11 | 12 | public static Task SuccessAsync => Task.FromResult(Success); 13 | public static Task ErrorAsync => Task.FromResult(Error); 14 | public static Task ValidationErrorAsync => Task.FromResult(ValidationError); 15 | } 16 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/fluent_validation_factory_create_invalid.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: fluent_validation_factory_create_invalid 2 | $ dotnet table.dll create TooLongTableName --server bossman --owner abc -qv 3 | 'Table' is invalid 4 | The length of 'Name' must be 10 characters or fewer. You entered 16 characters. 5 | 'Owner' is not a valid email address. 6 | 'Host' is invalid 7 | sever is not a valid fully-qualified http, https, or ftp URL 8 | 'Verbosity' is invalid 9 | quiet and verbose are mutually exclusive. There can be only one! 10 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/Parsing/MissingOptionValueParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | /// An option was specified without a value 7 | [PublicAPI] 8 | public class MissingOptionValueParseError(Command command, Option option) : IParseError 9 | { 10 | public string Message { get; } = Resources.A.Parse_Missing_value_for_option(option.Name); 11 | public Command Command { get; } = command.ThrowIfNull(); 12 | public Option Option { get; } = option.ThrowIfNull(); 13 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/IArgumentTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.TypeDescriptors; 4 | 5 | public interface IArgumentTypeDescriptor 6 | { 7 | /// Returns true when the type can be described by this descriptor 8 | bool CanSupport(Type type); 9 | 10 | /// Returns the name that will be displayed in help documentation 11 | string GetDisplayName(IArgument argument); 12 | 13 | /// Parses the string 14 | object? ParseString(IArgument argument, string value); 15 | } -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Directory (relative to root of your repo) that contains the deploy-ready 3 | # HTML files and assets generated by the build. If a base directory has 4 | # been specified, include it in the publish directory path. 5 | publish = "site/" 6 | 7 | # Default build command. 8 | command = "mkdocs build" 9 | 10 | ignore = "if [ ${COMMIT_REF} == ${CACHED_COMMIT_REF} ] ; then false ; else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF mkdocs.yml requirements.txt netlify.toml docs/ ; fi" 11 | 12 | [build.environment] 13 | PYTHON_VERSION = "3.8" -------------------------------------------------------------------------------- /CommandDotNet.FluentValidation/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.FluentValidation; 2 | 3 | public class Resources 4 | { 5 | public static Resources A = new(); 6 | 7 | public virtual string Error_Argument_model_is_invalid(string modelName) => $"'{modelName}' is invalid"; 8 | 9 | public virtual string Error_Could_not_create_instance_of(string name) => 10 | $"Could not create instance of {name}. Please ensure it's injected via IoC or has a default constructor.\n" + 11 | "This exception could also occur if default constructor threw an exception"; 12 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/getting-started-300-calculator-help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: getting-started-300-calculator-help 2 | $ dotnet calculator.dll --help 3 | Performs mathematical calculations 4 | 5 | Usage: dotnet calculator.dll [command] 6 | 7 | Commands: 8 | 9 | Add Adds two numbers 10 | Subtract Subtracts two numbers 11 | 12 | Use "dotnet calculator.dll [command] --help" for more information about a command. 13 | 14 | Include multiple lines of text 15 | Extended help of the root command is a good place to describe directives for the app 16 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Example/single-file-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # dotnet publish -r win-x64 -f net7.0 --self-contained -p:PublishSingleFile=true 4 | dotnet publish -r osx-arm64 -f net7.0 --self-contained -c release -p:PublishSingleFile=true 5 | 6 | # to enable debugging 7 | # cp bin/Debug/net7.0/win-x64/mscordbi.dll bin/Debug/net7.0/win-x64/publish/mscordbi.dll 8 | cp bin/Debug/net7.0/osx-arm64/mscordbi.dll bin/Debug/net7.0/osx-arm64/publish/mscordbi.dll 9 | 10 | echo 11 | echo files 12 | echo 13 | 14 | #ls bin/Debug/net7.0/win-x64/publish 15 | ls bin/Debug/net7.0/osx-arm64/publish -------------------------------------------------------------------------------- /docs/OtherFeatures/iconsole.md: -------------------------------------------------------------------------------- 1 | # IConsole 2 | 3 | CommandDotNet comes with a fairly comprehensive wrapper for System.Console, named SystemConsole. 4 | 5 | SystemConsole implements IConsole which can be included in any command or interceptor method. 6 | 7 | This makes it easy to test your command input and output. Also makig it easier to test is the [TestConsole](../TestTools/overview.md#testconsole) 8 | described in the TestTools overview. 9 | 10 | SystemConsole and TestConsole can be inherited for simpler adaptation resiliant to breakimg changes in IConsole if new members are added. 11 | -------------------------------------------------------------------------------- /CommandDotNet/Parsing/UnrecognizedOptionParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Tokens; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | /// 7 | /// is not a valid command 8 | /// and there is no available operand to assign the value to. 9 | /// 10 | [PublicAPI] 11 | public class UnrecognizedOptionParseError(Command command, Token token, string optionPrefix, string? message = null) 12 | : UnrecognizedArgumentParseError(command, token, optionPrefix, 13 | message ?? Resources.A.Parse_Unrecognized_option(token.RawValue)); -------------------------------------------------------------------------------- /docs/TipsFaqs/cli-best-practices.md: -------------------------------------------------------------------------------- 1 | # CLI best practices 2 | 3 | [GNU Standards for Command Line Interfaces](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html) 4 | 5 | The following contains some good guidelines. 6 | 7 | Some terminology and standards has deviated from long established standards. For example, they refer to positional arguments (Operands) as Arguments and named arguments (Options) as Flags. 8 | 9 | [12 Factor CLI Apps](https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46) 10 | 11 | [Command Line Interface Guidelines](https://clig.dev/) 12 | 13 | -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/IArgumentDef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.ClassModeling.Definitions; 4 | 5 | internal interface IArgumentDef: ISourceDef 6 | { 7 | string ArgumentDefType { get; } 8 | CommandNodeType CommandNodeType { get; } 9 | Type Type { get; } 10 | bool HasDefaultValue { get; } 11 | object? DefaultValue { get; } 12 | ValueProxy ValueProxy { get; } 13 | bool IsOptional { get; } 14 | BooleanMode? BooleanMode { get; } 15 | IArgumentArity Arity { get; } 16 | char? Split { get; set; } 17 | string? Group { get; set; } 18 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_include_machine_and_user_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_include_machine_and_user_exe 2 | $ example.exe [cmdlog] Add 1 1 3 | 4 | *************************************** 5 | Original input: 6 | [cmdlog] Add 1 1 7 | 8 | command: Add 9 | 10 | arguments: 11 | 12 | x 13 | value: 1 14 | inputs: 1 15 | default: 16 | 17 | y 18 | value: 1 19 | inputs: 1 20 | default: 21 | 22 | Machine = my-machine 23 | Username = \my-machine\username 24 | *************************************** 25 | 2 26 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/Diagnostics/Diagnostics_Examples.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Diagnostics; 2 | 3 | namespace CommandDotNet.DocExamples.Diagnostics; 4 | 5 | public static class Diagnostics_Examples 6 | { 7 | public class App 8 | { 9 | public void Process() { } 10 | } 11 | 12 | // begin-snippet: diagnostics_debug_directive 13 | class Program 14 | { 15 | static int Main(string[] args) 16 | { 17 | Debugger.AttachIfDebugDirective(args); 18 | return new AppRunner().Run(args); 19 | } 20 | } 21 | // end-snippet 22 | } 23 | -------------------------------------------------------------------------------- /CommandDotNet/Tokens/TokenTransformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Tokens; 5 | 6 | [PublicAPI] 7 | public class TokenTransformation( 8 | string name, 9 | int order, 10 | Func transformation) 11 | { 12 | public string Name { get; } = name; 13 | public int Order { get; } = order; 14 | public Func Transformation { get; } = transformation; 15 | 16 | public override string ToString() => $"{nameof(TokenTransformation)}: {Name} ({Order})"; 17 | } -------------------------------------------------------------------------------- /CommandDotNet/CommandAppSettings.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | [PublicAPI] 7 | public class CommandAppSettings : IIndentableToString 8 | { 9 | // begin-snippet: CommandAppSettings 10 | /// When true, methods on base classes will be included as commands. 11 | public bool InheritCommandsFromBaseClasses { get; set; } 12 | // end-snippet 13 | 14 | public override string ToString() => ToString(new Indent()); 15 | 16 | public string ToString(Indent indent) => this.ToStringFromPublicProperties(indent); 17 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_collection_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_collection_help 2 | $ app.exe --help 3 | Usage: app.exe [options] 4 | 5 | Options: 6 | 7 | -b | --requiredBool (Multiple) 8 | Allowed values: true, false 9 | 10 | -u | --requiredRefType (Multiple) 11 | 12 | --nullableBool (Multiple) 13 | Allowed values: true, false 14 | 15 | --nullableRefType (Multiple) 16 | 17 | --optionalBool (Multiple) 18 | Allowed values: true, false 19 | 20 | --optionalRefType (Multiple) 21 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/CommandContextExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet; 2 | 3 | public static class CommandContextExtensions 4 | { 5 | /// 6 | /// Prints help for a provided command, else the target command if parsed, else the root command. 7 | /// 8 | public static void PrintHelp(this CommandContext commandContext, Command? command = null) 9 | { 10 | command ??= commandContext.ParseResult?.TargetCommand ?? commandContext.RootCommand!; 11 | var helpText = commandContext.AppConfig.HelpProvider.GetHelpText(command); 12 | commandContext.Console.Out.WriteLine(helpText); 13 | } 14 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_custom_attribute_enabled.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_custom_attribute_enabled 2 | $ example.exe Add 1 1 3 | 4 | *************************************** 5 | Original input: 6 | Add 1 1 7 | 8 | command: Add 9 | 10 | arguments: 11 | 12 | x 13 | value: 1 14 | inputs: 1 15 | default: 16 | 17 | y 18 | value: 1 19 | inputs: 1 20 | default: 21 | 22 | Tool version = doc-examples.dll 1.1.1.1 23 | .Net version = .NET 5.0.13 24 | OS version = Microsoft Windows 10.0.12345 25 | *************************************** 26 | 2 27 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/Utils/AssemblyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace CommandDotNet.Tests.Utils; 6 | 7 | public static class AssemblyExtensions 8 | { 9 | private const string CommandDotNet = "CommandDotNet"; 10 | 11 | public static IEnumerable GetAllCommandDotNetAssemblies() 12 | { 13 | return Assembly 14 | .GetExecutingAssembly() 15 | .GetReferencedAssemblies() 16 | .Where(a => a.FullName == CommandDotNet || a.FullName.StartsWith(CommandDotNet)) 17 | .Select(Assembly.Load); 18 | } 19 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_default_directive.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_default_directive 2 | $ example.exe [cmdlog] Add 1 1 3 | 4 | *************************************** 5 | Original input: 6 | [cmdlog] Add 1 1 7 | 8 | command: Add 9 | 10 | arguments: 11 | 12 | x 13 | value: 1 14 | inputs: 1 15 | default: 16 | 17 | y 18 | value: 1 19 | inputs: 1 20 | default: 21 | 22 | Tool version = doc-examples.dll 1.1.1.1 23 | .Net version = .NET 5.0.13 24 | OS version = Microsoft Windows 10.0.12345 25 | *************************************** 26 | 2 27 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/TokenizerTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CommandDotNet.Tests.FeatureTests; 4 | 5 | public class TokenizerTests 6 | { 7 | // Tokenizer Tests 8 | // - Directives identified, unless feature is disabled 9 | // - Directives after separator are just values 10 | 11 | // TokenCollection Tests 12 | // - indexer, count & enumerator work as expected 13 | // - Directives, Arguments & Separated are populated correctly 14 | // - Multiple separators work as expected 15 | 16 | 17 | [Fact] 18 | public void SeparatedDirectivesAreNotIncludedInDirectivesList() 19 | { 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/NullCustomAttributeProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace CommandDotNet.ClassModeling.Definitions; 5 | 6 | internal class NullCustomAttributeProvider : ICustomAttributeProvider 7 | { 8 | internal static readonly NullCustomAttributeProvider Instance = new(); 9 | 10 | private NullCustomAttributeProvider() 11 | { 12 | } 13 | 14 | public object[] GetCustomAttributes(bool inherit) => []; 15 | 16 | public object[] GetCustomAttributes(Type attributeType, bool inherit) => []; 17 | 18 | public bool IsDefined(Type attributeType, bool inherit) => false; 19 | } -------------------------------------------------------------------------------- /CommandDotNet/Prompts/IPrompter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Prompts; 4 | 5 | public interface IPrompter 6 | { 7 | string? PromptForValue(string promptText, out bool isCancellationRequested, bool isPassword = false); 8 | IEnumerable PromptForValues(string promptText, out bool isCancellationRequested, bool isPassword = false); 9 | bool TryPromptForValue(string promptText, out string? value, out bool isCancellationRequested, bool isPassword = false); 10 | bool TryPromptForValues(string promptText, out IEnumerable values, out bool isCancellationRequested, bool isPassword = false); 11 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/Resources.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 2 | namespace CommandDotNet; 3 | 4 | public partial class Resources 5 | { 6 | public virtual string Arity_is_required(string argumentName) => $"{argumentName} is required"; 7 | public virtual string Arity_min_not_reached(string argumentName, string expected, string actual) => 8 | $"{argumentName} requires at least {expected} values but {actual} were provided."; 9 | public virtual string Arity_max_exceeded(string argumentName, string expected, string actual) => 10 | $"{argumentName} can have no more than {expected} values but {actual} were provided."; 11 | } -------------------------------------------------------------------------------- /CommandDotNet/InvalidConfigurationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// indicates exceptions that can be fixed by developers. 8 | /// These exceptions are not a result of user error. 9 | /// 10 | [PublicAPI] 11 | public class InvalidConfigurationException : Exception 12 | { 13 | public InvalidConfigurationException(string message) : base(message) 14 | { 15 | } 16 | 17 | public InvalidConfigurationException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | } 20 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/Models.md: -------------------------------------------------------------------------------- 1 | The purpose of these models is to enforce testing of all sample types across tests. 2 | When models are defined per test, it is easy to miss some of the domain, especially when adding to the domain later. 3 | 4 | Because only one argument list can be specified, the lists have to be split into different arg models or methods. 5 | The string list is kept with the other sample types because 6 | 1. there will be times the other lists won't need to be tested 7 | 1. this lets us test lists along with single valued arguments 8 | 9 | note: This is an experiment. It may turn out it's better to nest these classes within their test cases. 10 | -------------------------------------------------------------------------------- /CommandDotNet.IoC.Autofac/AutofacResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Autofac; 4 | using CommandDotNet.Builders; 5 | 6 | namespace CommandDotNet.IoC.Autofac; 7 | 8 | public class AutofacResolver : IDependencyResolver 9 | { 10 | private readonly IContainer _container; 11 | 12 | public AutofacResolver(IContainer container) 13 | { 14 | _container = container; 15 | } 16 | 17 | public object Resolve(Type type) 18 | { 19 | return _container.Resolve(type); 20 | } 21 | 22 | public bool TryResolve(Type type, [NotNullWhen(true)] out object? item) 23 | { 24 | return _container.TryResolve(type, out item); 25 | } 26 | } -------------------------------------------------------------------------------- /docs/Diagnostics/time-directive.md: -------------------------------------------------------------------------------- 1 | # Timing Commands 2 | 3 | ## TLDR, How to enable 4 | Enable the feature with `appRunner.UseTimeDirective()` or `appRunner.UseDefaultMiddleware()`. 5 | 6 | ## Time Directive 7 | 8 | Some shells come with the handy [time command](https://linuxize.com/post/linux-time-command/). 9 | 10 | .Net Core can be executed in environments where the shells do not have the time command, so we've added an alternative 11 | 12 | When the `[time]` [directive](../Extensibility/directives.md) is used, a execution time will output to the console after the command has completed. 13 | 14 | ```bash 15 | $ dotnet example.dll [time] Add 1 2 16 | 17 | 4 18 | 19 | time: 00:00:00.0009052 20 | ``` 21 | -------------------------------------------------------------------------------- /CommandDotNet/Execution/ResolveStrategy.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Builders; 2 | 3 | namespace CommandDotNet.Execution; 4 | 5 | /// 6 | /// Specifies whether or is used. 7 | /// When Resolve is used, if the the Resolve method returns null instead of throwing an exception, 8 | /// the framework will attempt to instantiate an instance of the type. 9 | /// 10 | public enum ResolveStrategy 11 | { 12 | /// Call resolve on container. 13 | Resolve, 14 | /// Call resolve on container. If the container returns null, throw an 15 | ResolveOrThrow, 16 | TryResolve 17 | } -------------------------------------------------------------------------------- /CommandDotNet/ArgumentSeparatorStrategy.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Parsing; 2 | 3 | namespace CommandDotNet; 4 | 5 | /// 6 | /// Determines whether arguments after the argument selector '--' are 7 | /// parsed and collected or just collected. 8 | /// 9 | public enum ArgumentSeparatorStrategy 10 | { 11 | /// 12 | /// Arguments after the argument separator '--' are parsed as operands 13 | /// and also added to 14 | /// 15 | EndOfOptions, 16 | /// 17 | /// Arguments after the argument separator '--' are added to 18 | /// 19 | PassThru 20 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsParams/IArgsNoDefaultsSampleTypesMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsParams; 5 | 6 | public interface IArgsNoDefaultsSampleTypesMethod 7 | { 8 | void ArgsNoDefault( 9 | bool boolArg, 10 | string stringArg, 11 | int structArg, 12 | int? structNArg, 13 | DayOfWeek enumArg, 14 | Uri objectArg, 15 | List stringListArg); 16 | 17 | void StructListNoDefault(List structListArg); 18 | void EnumListNoDefault(List enumListArg); 19 | void ObjectListNoDefault(List objectListArg); 20 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/Commands/Commands/Commands_DefaultCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DocExamples.Commands.Commands; 2 | 3 | public class Commands_DefaultCommand 4 | { 5 | // begin-snippet: commands_default_command 6 | public class Program 7 | { 8 | static int Main(string[] args) => AppRunner.Run(args); 9 | public static AppRunner AppRunner => new AppRunner(); 10 | 11 | [DefaultCommand] 12 | public void Process() 13 | { 14 | // do very important stuff 15 | } 16 | } 17 | // end-snippet 18 | 19 | public static BashSnippet Stash = new("commands_default_command_process", 20 | Program.AppRunner, "dotnet myapp.dll", "", 0, ""); 21 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/ISampleTypesArgumentsModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | /// 7 | /// Implement this interface for a select 8 | /// sample of .Net Types to feel confident 9 | /// all types will be handled 10 | /// 11 | public interface ISampleTypesArgumentsModel: IArgumentModel 12 | { 13 | bool BoolArg { get; set; } 14 | string? StringArg { get; set; } 15 | int StructArg { get; set; } 16 | int? StructNArg { get; set; } 17 | DayOfWeek EnumArg { get; set; } 18 | Uri? ObjectArg { get; set; } 19 | List? StringListArg { get; set; } 20 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/CancellationMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CommandDotNet.Execution; 4 | 5 | internal static class CancellationMiddleware 6 | { 7 | internal static AppRunner UseCancellationHandlers(AppRunner appRunner) 8 | { 9 | return appRunner.Configure(c => 10 | { 11 | c.UseMiddleware(AddCancellationTokens, MiddlewareSteps.CancellationHandler); 12 | c.OnRunCompleted += args => CancellationHandlers.EndRun(args.CommandContext); 13 | }); 14 | } 15 | 16 | private static Task AddCancellationTokens(CommandContext ctx, ExecutionDelegate next) 17 | { 18 | CancellationHandlers.BeginRun(ctx); 19 | return next(ctx); 20 | } 21 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/UnitTests/Parsing/LevenshteinDistanceTests.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Parsing.Typos; 2 | using FluentAssertions; 3 | using Xunit; 4 | 5 | namespace CommandDotNet.Tests.UnitTests.Parsing; 6 | 7 | public class LevenshteinDistanceTests 8 | { 9 | // https://www.eximiaco.tech/en/2019/11/17/computing-the-levenshtein-edit-distance-of-two-strings-using-c/ 10 | 11 | [Theory] 12 | [InlineData("ant", "aunt", 1)] 13 | [InlineData("fast", "cats", 3)] 14 | [InlineData("Elemar", "Vilmar", 3)] 15 | [InlineData("kitten", "sitting", 3)] 16 | public void ComputeTheDistanceBetween(string s1, string s2, int expectedDistance) 17 | { 18 | Levenshtein.ComputeDistance(s1, s2).Should().Be(expectedDistance); 19 | } 20 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/ParserFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommandDotNet.TypeDescriptors; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | internal class ParserFactory(AppSettings appSettings) 7 | { 8 | public IParser CreateInstance(IArgument argument) => 9 | argument.Arity.AllowsMany() 10 | ? new ListParser( 11 | argument.TypeInfo.Type, 12 | argument.TypeInfo.UnderlyingType, 13 | GetDescriptor(argument.TypeInfo.UnderlyingType)) 14 | : new SingleValueParser(GetDescriptor(argument.TypeInfo.Type)); 15 | 16 | private IArgumentTypeDescriptor GetDescriptor(Type argumentType) => 17 | appSettings.ArgumentTypeDescriptors.GetDescriptorOrThrow(argumentType); 18 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "CommandDotNet*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v5 13 | - name: Get all git tags 14 | run: git fetch --prune --unshallow --tags -f 15 | - uses: olegtarasov/get-tag@v2.1 16 | id: tagName 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v5 19 | with: 20 | dotnet-version: | 21 | 8.0.x 22 | 9.0.x 23 | - name: Restore dependencies 24 | run: dotnet restore 25 | - name: Deploy 26 | env: 27 | NUGET_API_KEY_COMMANDDOTNET: ${{ secrets.NUGET_API_KEY_COMMANDDOTNET }} 28 | run: ./deploy.sh 29 | -------------------------------------------------------------------------------- /CommandDotNet/Parsing/UnrecognizedArgumentParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using CommandDotNet.Tokens; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.Parsing; 6 | 7 | /// 8 | /// is not a valid command 9 | /// and there is no available operand to assign the value to. 10 | /// 11 | [PublicAPI] 12 | public class UnrecognizedArgumentParseError(Command command, Token token, string? optionPrefix, string message) 13 | : IParseError 14 | { 15 | public string Message { get; } = message.ThrowIfNull(); 16 | public Command Command { get; } = command.ThrowIfNull(); 17 | public Token Token { get; } = token.ThrowIfNull(); 18 | public string? OptionPrefix { get; } = optionPrefix; 19 | } -------------------------------------------------------------------------------- /CommandDotNet/ValueFromToken.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Tokens; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | [PublicAPI] 7 | public class ValueFromToken(string value, Token? valueToken, Token? optionToken) 8 | { 9 | public string Value { get; } = value; 10 | public Token? ValueToken { get; } = valueToken; 11 | public Token? OptionToken { get; } = optionToken; 12 | 13 | public string? TokenSourceName => ValueToken?.SourceName ?? OptionToken?.SourceName; 14 | public Token? TokensSourceToken => ValueToken?.SourceToken ?? OptionToken?.SourceToken; 15 | 16 | public override string ToString() => 17 | $"{nameof(ValueFromToken)}={Value} {nameof(ValueToken)}:{ValueToken} {nameof(OptionToken)}:{OptionToken}"; 18 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/command_logger_root_option_exe.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: command_logger_root_option_exe 2 | $ example.exe --logcmd Add 1 1 3 | 4 | *************************************** 5 | Original input: 6 | --logcmd Add 1 1 7 | 8 | command: Add 9 | 10 | arguments: 11 | 12 | x 13 | value: 1 14 | inputs: 1 15 | default: 16 | 17 | y 18 | value: 1 19 | inputs: 1 20 | default: 21 | 22 | options: 23 | 24 | logcmd 25 | value: True 26 | inputs: true (from: --logcmd) 27 | default: 28 | 29 | Tool version = doc-examples.dll 1.1.1.1 30 | .Net version = .NET 5.0.13 31 | OS version = Microsoft Windows 10.0.12345 32 | *************************************** 33 | 2 34 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/AppSettingAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// Sets the default value of this argument from the AppSettings with the given key.
8 | /// NOTE: register with `appRunner.UseDefaultsFromAppSetting` 9 | ///
10 | [PublicAPI] 11 | [AttributeUsage(AttributeTargets.Property|AttributeTargets.Parameter)] 12 | public class AppSettingAttribute : Attribute 13 | { 14 | /// The key of the AppSettings to use for a default value 15 | public string Key { get; } 16 | 17 | /// 18 | /// The key of the AppSettings to use for a default value 19 | public AppSettingAttribute(string key) => Key = key; 20 | } -------------------------------------------------------------------------------- /CommandDotNet/Directives/DirectivesTokenCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Linq; 4 | using CommandDotNet.Tokens; 5 | 6 | namespace CommandDotNet.Directives; 7 | 8 | public static class DirectivesTokenCollectionExtensions 9 | { 10 | public static bool TryGetDirective(this TokenCollection tokens, string directiveName, 11 | [NotNullWhen(true)] out string? tokenValue) 12 | { 13 | tokenValue = tokens.Directives.FirstOrDefault(t => 14 | t.Value.Equals(directiveName, StringComparison.OrdinalIgnoreCase) 15 | || t.Value.StartsWith($"{directiveName}:", StringComparison.OrdinalIgnoreCase)) 16 | ?.Value; 17 | return tokenValue != null; 18 | } 19 | } -------------------------------------------------------------------------------- /CommandDotNet/ITypeInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommandDotNet.TypeDescriptors; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// The .Net type information for the given argument 7 | public interface ITypeInfo 8 | { 9 | /// The parameter or property type of the argument 10 | Type Type { get; } 11 | 12 | /// 13 | /// If the is nullable or collection, this is the generic argument type. 14 | /// Otherwise it will be the same value as 15 | /// 16 | Type UnderlyingType { get; } 17 | 18 | /// The DisplayName as determined by the for this type 19 | string? DisplayName { get; set; } 20 | } -------------------------------------------------------------------------------- /coverlet.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | cobertura 8 | [*.Tests]*,[*.DocExamples]*,[*.Example]*,[CommandDotNet]CommandDotNet.Logging.LogProviders.* 9 | **/*Designer.cs,**/*.g.cs,**/*.g.i.cs 10 | GeneratedCode,CompilerGenerated,ExcludeFromCodeCoverage 11 | false 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /CommandDotNet/EnvVarAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// Sets the default value of this argument from the environment variable with the given key.
8 | /// NOTE: register with `appRunner.UseDefaultsFromEnvVar` 9 | ///
10 | [PublicAPI] 11 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] 12 | public class EnvVarAttribute : Attribute 13 | { 14 | /// The key of the environment variable to use for a default value 15 | public string Key { get; } 16 | 17 | /// 18 | /// The key of the environment variable to use for a default value 19 | public EnvVarAttribute(string key) => Key = key; 20 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsParams/IArgsDefaultsSampleTypesMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsParams; 5 | 6 | public interface IArgsDefaultsSampleTypesMethod 7 | { 8 | void ArgsDefaults( 9 | bool boolArg = true, 10 | string stringArg = "lala", 11 | int structArg = 3, 12 | int? structNArg = 4, 13 | DayOfWeek enumArg = DayOfWeek.Wednesday, 14 | Uri? objectArg = null, 15 | List? stringListArg = null); 16 | 17 | void StructListDefaults(List? structListArg = null); 18 | void EnumListDefaults(List? enumListArg = null); 19 | void ObjectListDefaults(List? objectListArg = null); 20 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/TextAnswer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.TestTools.Prompts; 4 | 5 | [Obsolete("Use Answer instead")] 6 | public class TextAnswer : Answer 7 | { 8 | /// Constructs a response for prompt of a single value 9 | /// The response value. This is converted to an enumerable of . 10 | /// Applied to the prompt text. Use this to ensure the answer is for the correct prompt. 11 | /// When false, this answer is discarded after use. 12 | public TextAnswer(string value, Predicate? promptFilter = null, bool reuse = false) 13 | : base(value.ToConsoleKeyInfos(), promptFilter, reuse) 14 | { 15 | } 16 | } -------------------------------------------------------------------------------- /CommandDotNet/SubCommandAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet; 4 | 5 | /// 6 | /// Defines a class as a subcommand of another class.
7 | /// Can be used on a property or nested class. 8 | ///
9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] 10 | public class SubcommandAttribute : Attribute 11 | { 12 | /// 13 | /// Overrides the command name defined in the 14 | /// or generated from the class name. 15 | /// 16 | public string? RenameAs { get; set; } 17 | } 18 | 19 | [Obsolete("use SubcommandAttribute instead. Lowercase C")] 20 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] 21 | public class SubCommandAttribute : SubcommandAttribute 22 | { 23 | 24 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/FailAnswer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommandDotNet.Extensions; 4 | 5 | namespace CommandDotNet.TestTools.Prompts; 6 | 7 | public class FailAnswer : IAnswer 8 | { 9 | public ICollection ConsoleKeys { get; } = EmptyCollection.Instance; 10 | public bool Reuse => false; 11 | public Predicate? PromptFilter { get; } 12 | public bool ShouldFail => true; 13 | 14 | /// Constructs a response for prompt of a single value 15 | /// Applied to the prompt text. Use this to ensure the answer is for the correct prompt. 16 | public FailAnswer(Predicate? promptFilter = null) 17 | { 18 | PromptFilter = promptFilter; 19 | } 20 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Suggestions/CafeApp.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.Tests.FeatureTests.Suggestions; 2 | // example borrowed from 3 | // https://github.com/dotnet/command-line-api/blob/master/src/System.CommandLine.Tests/SuggestionTests.cs 4 | 5 | public enum Fruit { Apple, Banana, Cherry } 6 | public enum Vegetable { Asparagus, Broccoli, Carrot } 7 | public enum Meal { Breakfast, Lunch, Dinner } 8 | 9 | public class CafeApp 10 | { 11 | public void Eat(IConsole console, 12 | [Operand] Meal meal, 13 | [Option] Vegetable vegetable, 14 | [Option] Fruit fruit) 15 | { 16 | console.Out.WriteLine($"{nameof(meal)} :{meal}"); 17 | console.Out.WriteLine($"{nameof(fruit)} :{fruit}"); 18 | console.Out.WriteLine($"{nameof(vegetable)}:{vegetable}"); 19 | } 20 | } -------------------------------------------------------------------------------- /CommandDotNet/Extensions/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace CommandDotNet.Extensions; 4 | 5 | internal static class MemberInfoExtensions 6 | { 7 | internal static string FullName(this MemberInfo memberInfo, bool includeNamespace = false) => 8 | includeNamespace 9 | ? $"{memberInfo.DeclaringType?.FullName}.{memberInfo.Name}" 10 | : $"{memberInfo.DeclaringType?.Name}.{memberInfo.Name}"; 11 | 12 | internal static string FullName(this ParameterInfo parameterInfo, bool includeNamespace = false) => 13 | includeNamespace 14 | ? $"{parameterInfo.Member.DeclaringType?.FullName}.{parameterInfo.Member.Name}.{parameterInfo.Name}" 15 | : $"{parameterInfo.Member.DeclaringType?.Name}.{parameterInfo.Member.Name}.{parameterInfo.Name}"; 16 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/NotAllowedValueParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using CommandDotNet.Tokens; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.Parsing; 6 | 7 | /// The value is not in 8 | [PublicAPI] 9 | public class NotAllowedValueParseError(Command command, IArgument argument, Token token) : IParseError 10 | { 11 | public string Message { get; } = Resources.A.Parse_Unrecognized_value_for(token.RawValue, 12 | argument is Option ? Resources.A.Common_option_lc : Resources.A.Common_argument_lc, 13 | argument.Name); 14 | 15 | public Command Command { get; } = command.ThrowIfNull(); 16 | public IArgument Argument { get; } = argument.ThrowIfNull(); 17 | public Token Token { get; } = token.ThrowIfNull(); 18 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/SingleValueParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommandDotNet.Extensions; 3 | using CommandDotNet.TypeDescriptors; 4 | 5 | namespace CommandDotNet.Parsing; 6 | 7 | internal class SingleValueParser(IArgumentTypeDescriptor argumentTypeDescriptor) : IParser 8 | { 9 | public object? Parse(IArgument argument, IEnumerable values) 10 | { 11 | var value = values.SingleOrDefaultOrThrow(() => ThrowMultiForSingleEx(argument)); 12 | return value is null 13 | ? null 14 | : argumentTypeDescriptor.ParseString(argument, value); 15 | } 16 | 17 | private static void ThrowMultiForSingleEx(IArgument argument) => 18 | throw new ValueParsingException( 19 | Resources.A.Parse_ArgumentArity_Expected_single_value(argument.Name)); 20 | } -------------------------------------------------------------------------------- /CommandDotNet/ArgumentDefault.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// The values provided as the default for an argument 7 | [PublicAPI] 8 | public class ArgumentDefault(string source, string key, object value) 9 | { 10 | /// The source of the default value 11 | public string Source { get; } = source.ThrowIfNull(); 12 | 13 | /// The key of the default value 14 | public string Key { get; } = key.ThrowIfNull(); 15 | 16 | /// The text values 17 | public object Value { get; } = value.ThrowIfNull(); 18 | 19 | public override string ToString() => 20 | // do not include value in case it's a password 21 | $"{nameof(ArgumentDefault)}: {Source}.{Key}"; 22 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/EnumTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.TypeDescriptors; 6 | 7 | [PublicAPI] 8 | // begin-snippet: type_descriptors_enum 9 | public class EnumTypeDescriptor : 10 | IArgumentTypeDescriptor, 11 | IAllowedValuesTypeDescriptor 12 | { 13 | public bool CanSupport(Type type) => type.IsEnum; 14 | 15 | public string GetDisplayName(IArgument argument) => 16 | argument.TypeInfo.UnderlyingType.Name; 17 | 18 | public object ParseString(IArgument argument, string value) => 19 | Enum.Parse(argument.TypeInfo.UnderlyingType, value); 20 | 21 | public IEnumerable GetAllowedValues(IArgument argument) => 22 | Enum.GetNames(argument.TypeInfo.UnderlyingType); 23 | } 24 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/OrderByPositionInClassAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet; 6 | 7 | /// 8 | /// Used to determine the position of s, s and nested s within the class. 9 | /// 10 | /// 11 | /// The value is defaulted by . Leave blank to let the position of the property determine the order. 12 | /// 13 | [PublicAPI] 14 | [AttributeUsage(AttributeTargets.Property)] 15 | // ReSharper disable once InconsistentNaming 16 | public class OrderByPositionInClassAttribute([CallerLineNumber] int __callerLineNumber = 0) : Attribute 17 | { 18 | public int CallerLineNumber { get; } = __callerLineNumber; 19 | } -------------------------------------------------------------------------------- /CommandDotNet/Builders/DependencyResolverExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet.Builders; 5 | 6 | [PublicAPI] 7 | public static class DependencyResolverExtensions 8 | { 9 | public static T? Resolve(this IDependencyResolver resolver) where T : class? 10 | => (T?)resolver.Resolve(typeof(T)); 11 | 12 | public static bool TryResolve(this IDependencyResolver resolver, 13 | [NotNullWhen(true)] out T? item) 14 | where T : class? 15 | { 16 | var result = resolver.TryResolve(typeof(T), out var localItem); 17 | item = (T?) localItem; 18 | return result; 19 | } 20 | 21 | public static T? ResolveOrDefault(this IDependencyResolver resolver) where T: class? 22 | => resolver.TryResolve(out T? item) ? item : null; 23 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/InvocationResultExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace CommandDotNet.Execution; 5 | 6 | internal static class InvocationResultExtensions 7 | { 8 | internal static async Task GetResultCodeAsync(this object? value) 9 | { 10 | switch (value) 11 | { 12 | case Task resultCodeTask: 13 | return await resultCodeTask.ConfigureAwait(false); 14 | case Task task: 15 | await task.ConfigureAwait(false); 16 | return 0; 17 | case int resultCode: 18 | return resultCode; 19 | case null: 20 | return 0; 21 | default: 22 | throw new NotSupportedException($"Unexpected value type: {value} ({value.GetType()})"); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/UnexpectedOptionValueParseError.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using CommandDotNet.Tokens; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.Parsing; 6 | 7 | /// 8 | /// A value was provided for an option that didn't expect it.
9 | /// The option has an arity of 1 but was given multiple values
10 | /// Or the option has an arity of 0 but was given a value. 11 | ///
12 | [PublicAPI] 13 | public class UnexpectedOptionValueParseError(Command command, Option option, Token token) : IParseError 14 | { 15 | public string Message { get; } = Resources.A.Parse_Unexpected_value_for_option(token.RawValue, option.Name); 16 | public Command Command { get; } = command.ThrowIfNull(); 17 | public Option Option { get; } = option.ThrowIfNull(); 18 | public Token Token { get; } = token.ThrowIfNull(); 19 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/Resources.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 2 | // ReSharper disable InconsistentNaming 3 | 4 | namespace CommandDotNet; 5 | 6 | public partial class Resources 7 | { 8 | public virtual string Type_Boolean => "Boolean"; 9 | public virtual string Type_Character => "Character"; 10 | public virtual string Type_Decimal => "Decimal"; 11 | public virtual string Type_Double => "Double"; 12 | public virtual string Type_Number => "Number"; 13 | public virtual string Type_Text => "Text"; 14 | 15 | public virtual string Error_Value_is_not_valid_for_type(string value, string typeDisplayName) 16 | => $"'{value}' is not a valid {typeDisplayName}"; 17 | 18 | public virtual string Error_Failed_parsing_value_for_type(string value, string typeDisplayName) 19 | => $"failed parsing '{value}' for {typeDisplayName}"; 20 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/OtherFeatures/DefaultMiddleware_Examples.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DocExamples.OtherFeatures; 2 | 3 | public static class DefaultMiddleware_Examples 4 | { 5 | public class ValidationApp 6 | { 7 | public void Process(string input) { } 8 | } 9 | 10 | // begin-snippet: default_middleware_basic 11 | public static int BasicExample(string[] args) 12 | { 13 | return new AppRunner() 14 | .UseDefaultMiddleware() 15 | .Run(args); 16 | } 17 | // end-snippet 18 | 19 | // begin-snippet: default_middleware_exclude_debug 20 | public static int ExcludeDebugDirective(string[] args) 21 | { 22 | return new AppRunner() 23 | .UseDefaultMiddleware(excludeDebugDirective: true) 24 | .Run(args); 25 | } 26 | // end-snippet 27 | } 28 | -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsNoDefaultsSampleTypesModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | #pragma warning disable 8767 5 | 6 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 7 | 8 | public class OperandsNoDefaultsSampleTypesModel : ISampleTypesArgumentsModel 9 | { 10 | [Operand] 11 | public bool BoolArg { get; set; } 12 | 13 | [Operand] 14 | public string StringArg { get; set; } = null!; 15 | 16 | [Operand] 17 | public int StructArg { get; set; } 18 | 19 | [Operand] 20 | public int? StructNArg { get; set; } 21 | 22 | [Operand] 23 | public DayOfWeek EnumArg { get; set; } 24 | 25 | [Operand] 26 | public Uri ObjectArg { get; set; } = null!; 27 | 28 | [Operand] 29 | public List StringListArg { get; set; } = null!; 30 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/exceptions_throw_cmdlog.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: exceptions_throw_cmdlog 2 | $ example.exe Throw yikes 3 | System.ArgumentException: yikes (Parameter 'message') 4 | Properties: 5 | Message: yikes (Parameter 'message') 6 | ParamName: message 7 | Data: 8 | method: Throw 9 | 10 | *************************************** 11 | Original input: 12 | Throw yikes 13 | 14 | command: Throw 15 | 16 | arguments: 17 | 18 | message 19 | value: yikes 20 | inputs: yikes 21 | default: 22 | 23 | Tool version = doc-examples.dll 1.1.1.1 24 | .Net version = .NET 5.0.13 25 | OS version = Microsoft Windows 10.0.12345 26 | Machine = my-machine 27 | Username = \my-machine\username 28 | *************************************** 29 | 30 | Usage: example.exe Throw 31 | 32 | Arguments: 33 | 34 | message 35 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/DelegatedTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommandDotNet.Extensions; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.TypeDescriptors; 6 | 7 | [PublicAPI] 8 | public class DelegatedTypeDescriptor(string displayName, Func parseValueDelegate) 9 | : IArgumentTypeDescriptor 10 | { 11 | private readonly string _displayName = displayName.ThrowIfNull(); 12 | private readonly Func _parseValueDelegate = parseValueDelegate.ThrowIfNull(); 13 | 14 | public bool CanSupport(Type type) => type == typeof(T); 15 | 16 | public string GetDisplayName(IArgument argument) => _displayName; 17 | 18 | public object ParseString(IArgument argument, string value) => _parseValueDelegate(value); 19 | 20 | public override string ToString() => $"DelegatedTypeDescriptor<{typeof(T).Name}>: '{_displayName}'"; 21 | } -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | [data-md-color-scheme=slate] { 2 | --md-typeset-a-color: #3c8aff !important; 3 | } 4 | 5 | #features-link { 6 | font-weight: bold; 7 | color: #94fff0; 8 | text-decoration: underline; 9 | } 10 | 11 | [data-md-color-scheme=slate] #features-link { 12 | color: #00e2c3; 13 | } 14 | 15 | #features-page { 16 | display: grid; 17 | grid-template-columns: repeat(1, 1fr); 18 | } 19 | 20 | .feature { 21 | padding: 1em; 22 | background-color: rgb(233, 233, 233); 23 | border-radius: 1em; 24 | margin: 1em 0; 25 | } 26 | 27 | @media (min-width:60em) { 28 | #features-page { 29 | grid-template-columns: repeat(2, 1fr); 30 | } 31 | 32 | .feature { 33 | margin: 2em 1em; 34 | } 35 | } 36 | 37 | [data-md-color-scheme=slate] .feature { 38 | background-color: rgb(163 163 163 / 5%); 39 | } 40 | 41 | .feature>h2 { 42 | margin: 0; 43 | text-align: center; 44 | } -------------------------------------------------------------------------------- /CommandDotNet.NewerReleasesAlerts/CommandDotNet.NewerReleasesAlerts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.NewerReleasesAlerts 5 | Print alerts if current version of the app is not the latest published version 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OperandsDefaultsSampleTypesModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class OperandsDefaultsSampleTypesModel : ISampleTypesArgumentsModel 7 | { 8 | [Operand] 9 | public bool BoolArg { get; set; } = true; 10 | 11 | [Operand] 12 | public string? StringArg { get; set; } = "lala"; 13 | 14 | [Operand] 15 | public int StructArg { get; set; } = 3; 16 | 17 | [Operand] 18 | public int? StructNArg { get; set; } = 4; 19 | 20 | [Operand] 21 | public DayOfWeek EnumArg { get; set; } = DayOfWeek.Tuesday; 22 | 23 | [Operand] 24 | public Uri? ObjectArg { get; set; } = new("http://google.com"); 25 | 26 | [Operand] 27 | public List? StringListArg { get; set; } = ["red", "blue"]; 28 | } -------------------------------------------------------------------------------- /CommandDotNet/CommandNodeType.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | [PublicAPI] 7 | public class CommandNodeType 8 | { 9 | public static readonly CommandNodeType Command = new(nameof(Command)) {IsCommand = true}; 10 | public static readonly CommandNodeType Operand = new(nameof(Operand)) {IsOperand = true}; 11 | public static readonly CommandNodeType Option = new(nameof(Option)) {IsOption = true}; 12 | 13 | private readonly string _name; 14 | 15 | public bool IsCommand { get; private set; } 16 | public bool IsOperand { get; private set; } 17 | public bool IsOption { get; private set; } 18 | public bool IsArgument => IsOperand || IsOption; 19 | 20 | private CommandNodeType(string name) => _name = name.ThrowIfNull(); 21 | 22 | public override string ToString() => $"{nameof(CommandNodeType)}: {_name}"; 23 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/BashSnippets/arguments_arity_help.bash: -------------------------------------------------------------------------------- 1 | // begin-snippet: arguments_arity_help 2 | $ app.exe --help 3 | Usage: app.exe [ ] 4 | 5 | Arguments: 6 | 7 | RequiredBool 8 | Allowed values: true, false 9 | 10 | DefaultBool [True] 11 | Allowed values: true, false 12 | 13 | RequiredRefType 14 | 15 | DefaultRefType [http://apple.com/] 16 | 17 | requiredBool 18 | Allowed values: true, false 19 | 20 | requiredRefType 21 | 22 | nullableBool 23 | Allowed values: true, false 24 | 25 | nullableRefType 26 | 27 | optionalBool [False] 28 | Allowed values: true, false 29 | 30 | optionalRefType 31 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Ambient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace CommandDotNet.TestTools; 5 | 6 | internal static class Ambient 7 | { 8 | private static readonly AsyncLocal Internal = new(); 9 | 10 | public static T? Instance 11 | { 12 | get => Internal.Value; 13 | set => Internal.Value = value ?? throw new ArgumentNullException(nameof(value)); 14 | } 15 | 16 | public static T InstanceOrThrow => Internal.Value 17 | ?? throw new InvalidOperationException($"Ambient<{typeof(T).Name}> has not been set for the current test"); 18 | 19 | public static void Set(T instance) => Internal.Value = instance ?? throw new ArgumentNullException(nameof(instance)); 20 | 21 | public static void SetOrClear(T? instance) => Internal.Value = instance; 22 | 23 | public static void Clear() => Internal.Value = default; 24 | } -------------------------------------------------------------------------------- /CommandDotNet.Example/CommandDotNet.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /CommandDotNet.IoC.MicrosoftDependencyInjection/MicrosoftDependencyInjectionResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using CommandDotNet.Builders; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace CommandDotNet.IoC.MicrosoftDependencyInjection; 7 | 8 | public class MicrosoftDependencyInjectionResolver : IDependencyResolver 9 | { 10 | private readonly IServiceProvider _serviceProvider; 11 | 12 | public MicrosoftDependencyInjectionResolver(IServiceProvider serviceProvider) 13 | { 14 | _serviceProvider = serviceProvider; 15 | } 16 | 17 | public object Resolve(Type type) 18 | { 19 | return _serviceProvider.GetRequiredService(type); 20 | } 21 | 22 | public bool TryResolve(Type type, [NotNullWhen(true)] out object? item) 23 | { 24 | item = _serviceProvider.GetService(type); 25 | return item is not null; 26 | } 27 | } -------------------------------------------------------------------------------- /CommandDotNet/Tokens/Token.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace CommandDotNet.Tokens; 4 | 5 | [PublicAPI] 6 | public class Token( 7 | string rawValue, 8 | string value, 9 | TokenType tokenType) 10 | { 11 | /// 12 | /// The raw value from the user input. 13 | /// This will contain the punctuation used to denote option and argument names. 14 | /// 15 | public string RawValue { get; } = rawValue; 16 | 17 | /// Can be an Option name or an argument value 18 | public string Value { get; } = value; 19 | 20 | /// The 21 | public TokenType TokenType { get; } = tokenType; 22 | 23 | public string? SourceName { get; internal set; } 24 | 25 | public Token? SourceToken { get; internal set; } 26 | 27 | public override string ToString() => $"{nameof(Token)}:{TokenType}>{RawValue}"; 28 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/CommandDotNet.TestTools.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.TestTools 5 | Test your CommandDotNet application 6 | dotnet core; console; argument parse; test; 7 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.testtools/ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/ListAnswer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CommandDotNet.TestTools.Prompts; 6 | 7 | [Obsolete("Use Answer instead")] 8 | public class ListAnswer : Answer 9 | { 10 | /// Constructs a response for prompt of a list of values 11 | /// The response values. This is converted to an enumerable of delimited by Enter. 12 | /// Applied to the prompt text. Use this to ensure the answer is for the correct prompt. 13 | /// When false, this answer is discarded after use. 14 | public ListAnswer(IEnumerable valueList, Predicate? promptFilter = null, bool reuse = false) 15 | : base(valueList.SelectMany(v => v.ToConsoleKeyInfos().AppendEnterKey()), promptFilter, reuse) 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/Ambient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommandDotNet.TestTools; 3 | using Xunit.Abstractions; 4 | 5 | namespace CommandDotNet.Tests; 6 | 7 | public static class Ambient 8 | { 9 | public static ITestOutputHelper? Output 10 | { 11 | get => Ambient.Instance; 12 | set => Ambient.Instance = value; 13 | } 14 | 15 | public static Action WriteLine 16 | { 17 | get 18 | { 19 | var output = Output; 20 | if (output == null) 21 | { 22 | throw new InvalidOperationException($"{nameof(Ambient)}.{nameof(Output)} has not been set for the current test"); 23 | } 24 | 25 | return msg => 26 | { 27 | // XUnit does not like null values 28 | msg ??= ""; 29 | output.WriteLine(msg); 30 | }; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/ComponentModelTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.TypeDescriptors; 6 | 7 | [PublicAPI] 8 | // begin-snippet: type_descriptors_type_convertor 9 | public class ComponentModelTypeDescriptor : IArgumentTypeDescriptor 10 | { 11 | public bool CanSupport(Type type) => 12 | TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string)); 13 | 14 | public string GetDisplayName(IArgument argument) => 15 | argument.TypeInfo.UnderlyingType.Name; 16 | 17 | public object ParseString(IArgument argument, string value) 18 | { 19 | var typeConverter = argument.Arity.AllowsMany() 20 | ? TypeDescriptor.GetConverter(argument.TypeInfo.UnderlyingType) 21 | : TypeDescriptor.GetConverter(argument.TypeInfo.Type); 22 | return typeConverter.ConvertFrom(value)!; 23 | } 24 | // end-snippet 25 | } -------------------------------------------------------------------------------- /CommandDotNet/Builders/NameTransformation.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace CommandDotNet.Builders; 4 | 5 | /// 6 | /// Add a name transformation to enforce name consistency across commands, operands and options.
7 | /// To enforce casing rules, configure `appRunner.UseNameCasing(...)` from the nuget package CommandDotNet.NameCasing 8 | ///
9 | /// the for the parameter or member the name is for. 10 | /// The name of the class, method, property or parameter defining the class or argument 11 | /// The name provided via attribute or other extensibility point 12 | /// the the name is for 13 | public delegate string NameTransformation(ICustomAttributeProvider attributes, string memberName, string? nameOverride, CommandNodeType commandNodeType); -------------------------------------------------------------------------------- /CommandDotNet.Spectre.Testing/TestConsoleInputExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Spectre.Console.Testing; 3 | 4 | namespace CommandDotNet.Spectre.Testing; 5 | 6 | public static class TestConsoleInputExtensions 7 | { 8 | public static void PushTextsWithEnter(this TestConsoleInput testConsoleInput, params string[] inputs) 9 | { 10 | foreach (var input in inputs) 11 | { 12 | testConsoleInput.PushTextWithEnter(input); 13 | } 14 | } 15 | 16 | public static void PushCharacters(this TestConsoleInput testConsoleInput, string inputs) 17 | { 18 | foreach (var input in inputs) 19 | { 20 | testConsoleInput.PushCharacter(input); 21 | } 22 | } 23 | 24 | public static void PushKeys(this TestConsoleInput testConsoleInput, params ConsoleKey[] inputs) 25 | { 26 | foreach (var input in inputs) 27 | { 28 | testConsoleInput.PushKey(input); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/NrtOperandsNoDefaultsSampleTypesModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class NrtOperandsNoDefaultsSampleTypesModel : ISampleTypesArgumentsModel 7 | { 8 | //[Obsolete("no value here")] 9 | [Operand] 10 | public bool BoolArg { get; set; } 11 | 12 | [Operand] 13 | public string? StringArg { get; set; } 14 | 15 | //[Obsolete("no value here")] 16 | [Operand] 17 | public int StructArg { get; set; } 18 | 19 | //[Obsolete("no value here")] 20 | [Operand] 21 | public int? StructNArg { get; set; } 22 | 23 | //[Obsolete("no value here")] 24 | [Operand] 25 | public DayOfWeek EnumArg { get; set; } 26 | 27 | [Operand] 28 | public Uri? ObjectArg { get; set; } 29 | 30 | [Operand] 31 | public List? StringListArg { get; set; } 32 | } -------------------------------------------------------------------------------- /docs/ReleaseNotes/CommandDotNet.NameCasing.md: -------------------------------------------------------------------------------- 1 | # CommandDotNet.NameCasing 2 | 3 | ## 6.1.0 4 | 5 | * Accidental increment. No features or fixes. Apologies for the noise. 6 | 7 | ## 5.0.0 8 | 9 | * add support for net9 10 | * drop support for net6 and net7 11 | 12 | ## 4.0.2 13 | 14 | * support dotnet 7 15 | 16 | ## 4.0.1 17 | 18 | * update to dotnet 6 19 | 20 | ## 3.0.1 21 | 22 | remove nuget package refs no longer required after move to net5.0 23 | 24 | ## 3.0.0 25 | 26 | CommandDotNet.NameCasing targets net5.0 instead of netstandard2.0. This will allow us to take advantage of new framework features. 27 | We're holding off on net6.0 at the moment because it's new enough many companies will not be able to adopt it yet. 28 | 29 | ## 2.0.0 30 | 31 | ## 1.0.1 32 | 33 | improve stack traces with AppOutputBase and PathMap project settings 34 | ``` 35 | $(MSBuildProjectDirectory)\ 36 | $(AppOutputBase)=CommandDotNet/ 37 | ``` 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | - 'images/**' 8 | pull_request: 9 | paths-ignore: 10 | - 'docs/**' 11 | - 'images/**' 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v5 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v5 20 | with: 21 | dotnet-version: | 22 | 8.0.x 23 | 9.0.x 24 | - name: Restore dependencies 25 | run: dotnet restore 26 | - name: Build 27 | run: dotnet build --no-restore 28 | - name: Test with Coverage 29 | run: dotnet test --no-build --verbosity normal --settings coverlet.runsettings --collect:"XPlat Code Coverage" 30 | - name: Upload coverage to Codecov 31 | uses: codecov/codecov-action@v5 32 | with: 33 | files: '**/coverage.cobertura.xml' 34 | fail_ci_if_error: false 35 | verbose: true 36 | -------------------------------------------------------------------------------- /CommandDotNet.NameCasing/CommandDotNet.NameCasing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.NameCasing 5 | Uses Humanizer to transform the case of command and argument names 6 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.namecasing/ 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/CustomReturnCodeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | 5 | namespace CommandDotNet.Tests.FeatureTests; 6 | 7 | public class CustomReturnCodeTests 8 | { 9 | public CustomReturnCodeTests(ITestOutputHelper output) 10 | { 11 | Ambient.Output = output; 12 | } 13 | 14 | [Theory] 15 | [InlineData("VoidMethodThatHasNoException", 0)] 16 | [InlineData("IntMethodWithNoException", 4)] 17 | public void Test(string commandName, int expectedExitCode) 18 | { 19 | var result = new AppRunner() 20 | .RunInMem([commandName]); 21 | 22 | result.ExitCode.Should().Be(expectedExitCode); 23 | } 24 | 25 | private class App 26 | { 27 | public void VoidMethodThatHasNoException() 28 | { 29 | 30 | } 31 | 32 | public int IntMethodWithNoException() 33 | { 34 | return 4; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /CommandDotNet/Extensions/EmptyCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace CommandDotNet.Extensions; 7 | 8 | internal class EmptyCollection : IReadOnlyCollection, ICollection 9 | { 10 | internal static readonly EmptyCollection Instance = new(); 11 | 12 | public int Count { get; } = 0; 13 | public bool IsReadOnly { get; } = true; 14 | public IEnumerator GetEnumerator() => Enumerable.Empty().GetEnumerator(); 15 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 16 | 17 | public void Add(T item) => throw new NotImplementedException(); 18 | public void Clear() => throw new NotImplementedException(); 19 | public bool Contains(T item) => throw new NotImplementedException(); 20 | public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException(); 21 | public bool Remove(T item) => throw new NotImplementedException(); 22 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/CommandDotNet.NameCasing/CaseChangerTests.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.NameCasing; 2 | using CommandDotNet.TestTools.Scenarios; 3 | using Xunit; 4 | using Xunit.Abstractions; 5 | 6 | namespace CommandDotNet.Tests.CommandDotNet.NameCasing; 7 | 8 | public class CaseChangerTests 9 | { 10 | public CaseChangerTests(ITestOutputHelper output) 11 | { 12 | Ambient.Output = output; 13 | } 14 | 15 | [Fact] 16 | public void Works() 17 | { 18 | new AppRunner() 19 | .UseNameCasing(Case.KebabCase) 20 | .Verify(new Scenario 21 | { 22 | When = {Args = "go"}, 23 | Then = {Output = "case-changer-tests"} 24 | }); 25 | } 26 | 27 | public class App 28 | { 29 | public void Go(CommandContext context, IConsole console) 30 | { 31 | console.Write(context.Services.GetOrThrow().ChangeCase(nameof(CaseChangerTests))); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /CommandDotNet/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using CommandDotNet.Extensions; 4 | 5 | namespace CommandDotNet; 6 | 7 | internal static class StringExtensions 8 | { 9 | internal static bool IsNullOrEmpty(this string? value) => 10 | string.IsNullOrEmpty(value); 11 | 12 | internal static bool IsNullOrWhitespace(this string? value) => 13 | string.IsNullOrWhiteSpace(value); 14 | 15 | internal static string? UnlessNullOrWhitespace(this string? value, Func? map = null) => 16 | value.IsNullOrWhitespace() 17 | ? null 18 | : map == null 19 | ? value 20 | : map(value!); 21 | 22 | internal static string[] SplitIntoLines(this string text, StringSplitOptions stringSplitOptions = StringSplitOptions.None) => 23 | text.Split(["\r\n", "\r", "\n"], stringSplitOptions); 24 | 25 | internal static string Repeat(this string value, int count) => Enumerable.Repeat(value, count).ToCsv(""); 26 | } -------------------------------------------------------------------------------- /CommandDotNet/Diagnostics/TimeDirective.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Threading.Tasks; 3 | using CommandDotNet.Directives; 4 | using CommandDotNet.Execution; 5 | 6 | namespace CommandDotNet.Diagnostics; 7 | 8 | internal static class TimeDirective 9 | { 10 | internal static AppRunner UseTimeDirective(this AppRunner appRunner) 11 | { 12 | return appRunner.Configure(c => c.UseMiddleware(TimeCommand, MiddlewareSteps.DebugDirective + 1)); 13 | } 14 | 15 | private static Task TimeCommand(CommandContext context, ExecutionDelegate next) 16 | { 17 | if (context.Original.Tokens.TryGetDirective(Resources.A.Time_time, out _)) 18 | { 19 | var sw = Stopwatch.StartNew(); 20 | var result = next(context); 21 | sw.Stop(); 22 | context.Console.WriteLine(); 23 | context.Console.WriteLine($"{Resources.A.Time_time}: {sw.Elapsed}"); 24 | return result; 25 | } 26 | 27 | return next(context); 28 | } 29 | } -------------------------------------------------------------------------------- /CommandDotNet/TypeDescriptors/BoolTypeDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using JetBrains.Annotations; 5 | 6 | namespace CommandDotNet.TypeDescriptors; 7 | 8 | [PublicAPI] 9 | // begin-snippet: type_descriptors_bool 10 | public class BoolTypeDescriptor : 11 | IArgumentTypeDescriptor, 12 | IAllowedValuesTypeDescriptor 13 | { 14 | public bool CanSupport(Type type) => type == typeof(bool); 15 | 16 | public string GetDisplayName(IArgument argument) => 17 | argument.Arity.RequiresNone() 18 | ? "" // no display name for flags 19 | : Resources.A.Type_Boolean; 20 | 21 | public object ParseString(IArgument argument, string value) => bool.Parse(value); 22 | 23 | public IEnumerable GetAllowedValues(IArgument argument) => 24 | argument.Arity.RequiresNone() 25 | ? Enumerable.Empty() // no values allowed for flags 26 | : new List { "true", "false" }; 27 | } 28 | // end-snippet -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/IAnswer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommandDotNet.Prompts; 4 | 5 | namespace CommandDotNet.TestTools.Prompts; 6 | 7 | /// 8 | /// Responses for a prompt when 9 | /// or are used 10 | /// 11 | public interface IAnswer 12 | { 13 | /// 14 | /// The s to write to the console 15 | /// 16 | ICollection ConsoleKeys { get; } 17 | 18 | /// 19 | /// When false, the answer is discarded after use, otherwise it will be reused. 20 | /// 21 | bool Reuse { get; } 22 | 23 | /// 24 | /// Applied to the prompt text. Use this to ensure the answer is for the correct prompt. 25 | /// 26 | Predicate? PromptFilter { get; } 27 | 28 | /// 29 | /// If true, an UnexpectedPrompt 30 | /// 31 | bool ShouldFail { get; } 32 | } -------------------------------------------------------------------------------- /CommandDotNet.IoC.Autofac/CommandDotNet.IoC.Autofac.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.IoC.Autofac 5 | Configures Autofac for dependency injection 6 | dotnet core; console; argument parse; autofac; 7 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.ioc.autofac/ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CommandDotNet.Tests/UnitTests/Framework/GetUnderlyingTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommandDotNet.Extensions; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace CommandDotNet.Tests.UnitTests.Framework; 7 | 8 | public class GetUnderlyingTypeTests 9 | { 10 | [Fact] 11 | public void GivenGenericList_ShouldBe_FirstGenericArgument() 12 | { 13 | typeof(List).GetUnderlyingType().Should().Be(); 14 | typeof(List).GetUnderlyingType().Should().Be(); 15 | } 16 | 17 | [Fact] 18 | public void GivenGenericEnumerable_ShouldBe_FirstGenericArgument() 19 | { 20 | typeof(IEnumerable).GetUnderlyingType().Should().Be(); 21 | typeof(IEnumerable).GetUnderlyingType().Should().Be(); 22 | } 23 | 24 | [Fact] 25 | public void GivenArray_ShouldBe_ElementType() 26 | { 27 | typeof(int[]).GetUnderlyingType().Should().Be(); 28 | typeof(object[]).GetUnderlyingType().Should().Be(); 29 | } 30 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/ITestConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.TestTools; 5 | 6 | public interface ITestConsole: IConsole 7 | { 8 | /// 9 | /// The combination of and 10 | /// in the order they were written from the app.
11 | /// This is how the output would appear in the shell. 12 | ///
13 | string? AllText(); 14 | 15 | /// 16 | /// The accumulated text of the stream. 17 | /// 18 | string? OutText(); 19 | 20 | /// 21 | /// The accumulated text of the stream. 22 | /// 23 | string? ErrorText(); 24 | 25 | ITestConsole Mock(IEnumerable pipedInput, bool overwrite = false); 26 | ITestConsole Mock(Func onReadLine, bool overwrite = false); 27 | ITestConsole Mock(Func? onReadKey, bool overwrite = false); 28 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/OtherFeatures/IEnvironment_Examples.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DocExamples.OtherFeatures; 2 | 3 | public static class IEnvironment_Examples 4 | { 5 | // begin-snippet: ienvironment_inject_example 6 | public class DeployCommand 7 | { 8 | public void Deploy( 9 | IConsole console, 10 | IEnvironment environment, 11 | string target) 12 | { 13 | console.Out.WriteLine($"Deploying to {target}"); 14 | console.Out.WriteLine($"Current user: {environment.UserName}"); 15 | console.Out.WriteLine($"Machine: {environment.MachineName}"); 16 | 17 | var apiKey = environment.GetEnvironmentVariable("DEPLOY_API_KEY"); 18 | if (string.IsNullOrEmpty(apiKey)) 19 | { 20 | console.Error.WriteLine("DEPLOY_API_KEY environment variable not set"); 21 | return; 22 | } 23 | 24 | // Use apiKey for deployment 25 | } 26 | } 27 | // end-snippet 28 | } 29 | -------------------------------------------------------------------------------- /CommandDotNet.TestTools/AppRunnerResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommandDotNet.TestTools; 4 | 5 | public class AppRunnerResult 6 | { 7 | internal AppRunner Runner { get; } 8 | internal TestConfig Config { get; } 9 | 10 | public int ExitCode { get; } 11 | 12 | public ITestConsole Console { get; } 13 | 14 | /// The used during the run 15 | public CommandContext CommandContext { get; } 16 | 17 | /// The exception that escaped from
18 | public Exception? EscapedException { get; } 19 | 20 | public AppRunnerResult(int exitCode, AppRunner runner, 21 | CommandContext commandContext, ITestConsole testConsole, 22 | TestConfig config, Exception? escapedException = null) 23 | { 24 | ExitCode = exitCode; 25 | Runner = runner; 26 | CommandContext = commandContext; 27 | Console = testConsole; 28 | Config = config; 29 | EscapedException = escapedException; 30 | } 31 | } -------------------------------------------------------------------------------- /CommandDotNet/Help/Resources.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 2 | // ReSharper disable InconsistentNaming 3 | namespace CommandDotNet; 4 | 5 | public partial class Resources 6 | { 7 | public virtual string Command_help => "help"; 8 | public virtual string Command_help_description => "Show help information"; 9 | 10 | public virtual string Help_Allowed_values => "Allowed values"; 11 | public virtual string Help_arg => "arg"; 12 | public virtual string Help_Arguments => "Arguments"; 13 | public virtual string Help_Commands => "Commands"; 14 | public virtual string Help_for_more_information_about_a_command => "for more information about a command"; 15 | public virtual string Help_Multiple => "Multiple"; 16 | public virtual string Help_Options => "Options"; 17 | public virtual string Help_Options_also_available_for_subcommands => "Options also available for subcommands"; 18 | public virtual string Help_Usage => "Usage"; 19 | public virtual string Help_usage_lc => "usage"; 20 | public virtual string Help_Use => "Use"; 21 | } -------------------------------------------------------------------------------- /CommandDotNet/Execution/ExecutionAppSettings.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.Extensions; 2 | using CommandDotNet.Help; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet.Execution; 6 | 7 | /// Settings specific to application execution and invocation 8 | [PublicAPI] 9 | public class ExecutionAppSettings : IIndentableToString 10 | { 11 | // begin-snippet: ExecutionAppSettings 12 | /// Specify what AppName to use in usage examples, help text, and generated scripts 13 | public UsageAppNameStyle UsageAppNameStyle { get; set; } = UsageAppNameStyle.Adaptive; 14 | 15 | /// 16 | /// The application name used in usage examples, help text, and generated scripts.
17 | /// When specified, is ignored. 18 | ///
19 | public string? UsageAppName { get; set; } 20 | // end-snippet 21 | 22 | public override string ToString() => ToString(new Indent()); 23 | 24 | public string ToString(Indent indent) => this.ToStringFromPublicProperties(indent); 25 | } 26 | -------------------------------------------------------------------------------- /CommandDotNet.DataAnnotations/CommandDotNet.DataAnnotations.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.DataAnnotations 5 | Uses System.ComponentModel.DataAnnotations to validate arguments 6 | dotnet core; console; argument parse; DataAnnotations; validation; 7 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.dataannotations/ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates for NuGet packages 4 | # Only notify for security updates - manual updates preferred for features 5 | - package-ecosystem: "nuget" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | day: "monday" 10 | open-pull-requests-limit: 5 11 | labels: 12 | - "dependencies" 13 | - "nuget" 14 | # Only create PRs for security updates and version-update:semver-major 15 | # This prevents noise from minor/patch updates 16 | # Update manually when needed for new features 17 | ignore: 18 | - dependency-name: "*" 19 | update-types: ["version-update:semver-minor", "version-update:semver-patch"] 20 | 21 | # Enable version updates for GitHub Actions 22 | # Always update GitHub Actions (they're workflow dependencies, not library dependencies) 23 | - package-ecosystem: "github-actions" 24 | directory: "/" 25 | schedule: 26 | interval: "weekly" 27 | day: "monday" 28 | labels: 29 | - "dependencies" 30 | - "github-actions" 31 | -------------------------------------------------------------------------------- /CommandDotNet.IoC.SimpleInjector/CommandDotNet.IoC.SimpleInjector.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.IoC.SimpleInjector 5 | Configures SimpleInjector for dependency injection 6 | dotnet core; console; argument parse; simpleinjector; 7 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.ioc.simpleinjector/ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CommandDotNet/IServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// Services and context data that can be stored with 8 | /// an object for later use.
9 | /// i.e. Middleware can store settings within the 10 | /// AppConfig.Services during initialization for use 11 | /// in the middleware pipeline. 12 | ///
13 | public interface IServices 14 | { 15 | void Add(T value) where T : class; 16 | void Add(Type type, object value); 17 | void AddOrUpdate(T value) where T : class; 18 | void AddOrUpdate(Type type, object value); 19 | T GetOrAdd(Func factory) where T : class; 20 | object GetOrAdd(Type type, Func factory); 21 | T? GetOrDefault() where T : class; 22 | object? GetOrDefault(Type type); 23 | T GetOrThrow() where T : class; 24 | object GetOrThrow(Type type); 25 | ICollection> GetAll(); 26 | bool Contains() where T : class; 27 | bool Contains(Type type); 28 | T GetOrCreate() where T : class, new(); 29 | } -------------------------------------------------------------------------------- /CommandDotNet/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace CommandDotNet.Extensions; 6 | 7 | internal static class DictionaryExtensions 8 | { 9 | internal static TValue? GetValueOrDefault(this IDictionary dictionary, string key) => 10 | dictionary.Contains(key) ? (TValue)dictionary[key]! : default; 11 | 12 | internal static TValue GetOrAdd( 13 | this IDictionary dictionary, TKey key, Func createDefault) 14 | { 15 | if (!dictionary.TryGetValue(key, out TValue? value)) 16 | { 17 | dictionary[key] = value = createDefault(key); 18 | } 19 | 20 | return value; 21 | } 22 | 23 | internal static TValue? GetValueOrDefault( 24 | this IDictionary dictionary, TKey key, Func? factory = null) 25 | where TValue : class => 26 | dictionary.ThrowIfNull().TryGetValue(key, out var value) 27 | ? value 28 | : factory?.Invoke(); 29 | } -------------------------------------------------------------------------------- /CommandDotNet.Example.Tests/CommandDotNet.Example.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /CommandDotNet.IoC.MicrosoftDependencyInjection/CommandDotNet.IoC.MicrosoftDependencyInjection.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.IoC.MicrosoftDependencyInjection 5 | Configures Microsoft.Extensions.DependencyInjection for dependency injection 6 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.ioc.microsoftdependencyinjection/ 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CommandDotNet.IoC.SimpleInjector/SimpleInjectorResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using CommandDotNet.Builders; 4 | using SimpleInjector; 5 | 6 | namespace CommandDotNet.IoC.SimpleInjector; 7 | 8 | public class SimpleInjectorResolver : IDependencyResolver 9 | { 10 | private readonly Container _container; 11 | 12 | public SimpleInjectorResolver(Container container) 13 | { 14 | _container = container; 15 | } 16 | 17 | public object Resolve(Type type) 18 | { 19 | return _container.GetInstance(type); 20 | } 21 | 22 | public bool TryResolve(Type type, [NotNullWhen(true)] out object? item) 23 | { 24 | // SimpleInjector's GetInstance will throw an exception if type is not registered. 25 | // Converting to IServiceProvider will return null if not registered. 26 | // https://stackoverflow.com/questions/10076206/prevent-simple-injector-to-throw-an-exception-when-resolving-an-unregistered-ser 27 | item = ((IServiceProvider) _container).GetService(type); 28 | return item is not null; 29 | } 30 | } -------------------------------------------------------------------------------- /docs/ReleaseNotes/CommandDotNet.NewerReleasesAlerts.md: -------------------------------------------------------------------------------- 1 | # CommandDotNet.NewerReleasesAlerts 2 | 3 | ## 5.1.0 4 | 5 | * Accidental increment. No features or fixes. Apologies for the noise. 6 | 7 | ## 5.0.0 8 | 9 | * add support for net9 10 | * drop support for net6 and net7 11 | 12 | ## 4.0.2 13 | 14 | * support dotnet 7 15 | 16 | ## 4.0.1 17 | 18 | * update to dotnet 6 19 | 20 | ## 3.0.1 21 | 22 | remove nuget package refs no longer required after move to net5.0 23 | 24 | ## 3.0.0 25 | 26 | CommandDotNet.NewerReleasesAlerts targets net5.0 instead of netstandard2.0. This will allow us to take advantage of new framework features. 27 | We're holding off on net6.0 at the moment because it's new enough many companies will not be able to adopt it yet. 28 | 29 | ## 2.0.1 30 | 31 | Just some code cleanup. No new features. 32 | 33 | ## 2.0.0 34 | 35 | ### Nullable Reference Types 36 | 37 | The library has been updated to support Nullable Reference Types 38 | 39 | ### MiddlewareSteps 40 | 41 | add MiddlewareSteps.NewerReleaseAlerts, declaring the stage and order within the stage the middleware is registered 42 | -------------------------------------------------------------------------------- /CommandDotNet.Spectre/CommandDotNet.Spectre.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommandDotNet.Spectre 5 | Integrate Spectre Console utilities into CommandDotNet 6 | dotnet core; console; prompt; ansi; tables 7 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.spectre/ 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/ClassCommands/InvalidInterceptorSignatureTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | 5 | namespace CommandDotNet.Tests.FeatureTests.ClassCommands; 6 | 7 | public class InvalidInterceptorSignatureTests 8 | { 9 | public InvalidInterceptorSignatureTests(ITestOutputHelper output) 10 | { 11 | Ambient.Output = output; 12 | } 13 | 14 | [Fact] 15 | public void VoidInterceptorThrowsDescriptiveException() 16 | { 17 | new AppRunner().RunInMem("Do") 18 | .Console.ErrorText().Should() 19 | .Contain( 20 | "`CommandDotNet.Tests.FeatureTests.ClassCommands.InvalidInterceptorSignatureTests+VoidInterceptor.Intercept` " + 21 | "must return type of System.Threading.Tasks.Task`1[System.Int32]"); 22 | } 23 | 24 | class VoidInterceptor 25 | { 26 | public void Intercept(InterceptorExecutionDelegate next) 27 | { 28 | next().Wait(); 29 | } 30 | 31 | public void Do() 32 | { 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /CommandDotNet/Diagnostics/NonSerializableWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using CommandDotNet.Extensions; 4 | 5 | namespace CommandDotNet.Diagnostics; 6 | 7 | /// 8 | /// A NonSerializableWrapper is a wrapper to contain non-serializable objects 9 | /// within Exception.Data which requires all items to be serializable. 10 | /// This is required for older versions of the dotnet. 11 | /// 12 | [Serializable] 13 | internal class NonSerializableWrapper(object? item, bool skipPrint = false) : ISerializable, IIndentableToString 14 | { 15 | public object? Item { get; } = item; 16 | public bool SkipPrint { get; } = skipPrint; 17 | 18 | public T As() => (T)Item! ?? throw new InvalidConfigurationException($"{nameof(NonSerializableWrapper)}.{nameof(Item)} cannot be null when using As"); 19 | 20 | void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) => 21 | info.AddValue(Item?.GetType().Namespace ?? "???", Item?.ToString()); 22 | 23 | public string ToString(Indent indent) => Item.ToIndentedString(indent) ?? ""; 24 | } -------------------------------------------------------------------------------- /CommandDotNet/Extensions/CustomAttributesContainerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using CommandDotNet.Builders; 5 | using JetBrains.Annotations; 6 | 7 | namespace CommandDotNet.Extensions; 8 | 9 | [PublicAPI] 10 | public static class CustomAttributesContainerExtensions 11 | { 12 | public static bool HasAttribute(this ICustomAttributesContainer container) where T : Attribute => 13 | container.ThrowIfNull().CustomAttributes.HasAttribute(); 14 | 15 | public static T? GetCustomAttribute(this ICustomAttributesContainer container) where T : Attribute => 16 | container 17 | .GetCustomAttributes() 18 | .SingleOrDefaultOrThrow( 19 | () => throw new InvalidConfigurationException($"attempted to get a single {typeof(T).Name} from {container} but multiple exist")); 20 | 21 | public static IEnumerable GetCustomAttributes(this ICustomAttributesContainer container) where T : Attribute => 22 | container.ThrowIfNull().CustomAttributes.GetCustomAttributes(typeof(T), false).Cast(); 23 | } -------------------------------------------------------------------------------- /CommandDotNet/Resources.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | [SuppressMessage("ReSharper", "InconsistentNaming")] 7 | [PublicAPI] 8 | public partial class Resources 9 | { 10 | public static Resources A = new(); 11 | 12 | public virtual string Common_default_lc => "default"; 13 | public virtual string Common_from_lc => "from"; 14 | public virtual string Common_key_lc => "key"; 15 | public virtual string Common_value_lc => "value"; 16 | public virtual string Common_source_lc => "source"; 17 | public virtual string Common_command_lc => "command"; 18 | public virtual string Common_argument_lc => "argument"; 19 | public virtual string Common_option_lc => "option"; 20 | public virtual string Common_Flag => "flag"; 21 | public virtual string Common_commands_lc => "commands"; 22 | public virtual string Common_arguments_lc => "arguments"; 23 | public virtual string Common_options_lc => "options"; 24 | 25 | public virtual string Error_File_not_found(string fullPath) => $"File not found: {fullPath}"; 26 | } -------------------------------------------------------------------------------- /CommandDotNet.DataAnnotations/ResourcesProxy.cs: -------------------------------------------------------------------------------- 1 | // this file generated by ResourceGenerators.RegenerateProxyClasses 2 | 3 | using System; 4 | using JetBrains.Annotations; 5 | 6 | namespace CommandDotNet.DataAnnotations 7 | { 8 | // this class generated by ResourcesDef.GenerateProxyClass 9 | [PublicAPI] 10 | public class ResourcesProxy(Func localize, bool memberNameAsKey = false) : Resources 11 | { 12 | private readonly Func _localize = localize ?? throw new ArgumentNullException(nameof(localize)); 13 | 14 | private static string? Format(string? value, params object?[] args) => 15 | value is null ? null : string.Format(value, args); 16 | 17 | public override string Error_DataAnnotation_phrases_to_replace_with_argument_name() => 18 | _localize(memberNameAsKey 19 | ? "Error_DataAnnotation_phrases_to_replace_with_argument_name" 20 | : base.Error_DataAnnotation_phrases_to_replace_with_argument_name()) 21 | ?? base.Error_DataAnnotation_phrases_to_replace_with_argument_name(); 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/CommandDotNet.IoC/IoCApp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using CommandDotNet.Execution; 4 | 5 | namespace CommandDotNet.Tests.CommandDotNet.IoC; 6 | 7 | public class SomeIoCService : ISomeIoCService { } 8 | 9 | public interface ISomeIoCService { } 10 | 11 | internal class IoCApp 12 | { 13 | public readonly ISomeIoCService FromCtor; 14 | public ISomeIoCService? FromInterceptor; 15 | 16 | public IoCApp(ISomeIoCService fromCtor) 17 | { 18 | FromCtor = fromCtor; 19 | } 20 | 21 | public Task Intercept(CommandContext context, ExecutionDelegate next) 22 | { 23 | FromInterceptor = (ISomeIoCService?) context.AppConfig.DependencyResolver?.Resolve(typeof(ISomeIoCService)); 24 | return next(context); 25 | } 26 | 27 | public void Do([Option('i')] bool expectFromInterceptor) 28 | { 29 | if(FromCtor == null) 30 | throw new Exception("SomeService was not injected via ctor"); 31 | if (FromInterceptor == null) 32 | throw new Exception("SomeService was not resolved in Interceptor"); 33 | } 34 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/Arguments/Models/ArgsAsArgModels/OptionsNoDefaultsSampleTypesModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommandDotNet.Tests.FeatureTests.Arguments.Models.ArgsAsArgModels; 5 | 6 | public class OptionsNoDefaultsSampleTypesModel : 7 | ISampleTypesArgumentsModel, IStructListArgumentModel, IEnumListArgumentModel, IObjectListArgumentModel 8 | { 9 | [Option] 10 | public bool BoolArg { get; set; } 11 | 12 | [Option] 13 | public string? StringArg { get; set; } 14 | 15 | [Option] 16 | public int StructArg { get; set; } 17 | 18 | [Option] 19 | public int? StructNArg { get; set; } 20 | 21 | [Option] 22 | public DayOfWeek EnumArg { get; set; } 23 | 24 | [Option] 25 | public Uri? ObjectArg { get; set; } 26 | 27 | [Option] 28 | public List? StringListArg { get; set; } 29 | 30 | [Option] 31 | public List? StructListArg { get; set; } 32 | 33 | [Option] 34 | public List? EnumListArg { get; set; } 35 | 36 | [Option] 37 | public List? ObjectListArg { get; set; } 38 | } -------------------------------------------------------------------------------- /CommandDotNet.FluentValidation/CommandDotNet.FluentValidation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | CommandDotNet.FluentValidation 4 | Uses FluentValidation validators to validate arguments defined using an IArgumentModel 5 | dotnet core; console; argument parse; FluentValidation; validation; 6 | https://commanddotnet.bilal-fazlani.com/releasenotes/commanddotnet.fluentvalidation/ 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/MethodCommandDef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using CommandDotNet.Execution; 5 | using CommandDotNet.Extensions; 6 | 7 | namespace CommandDotNet.ClassModeling.Definitions; 8 | 9 | internal class MethodCommandDef(MethodInfo method, Type commandHostClassType, AppConfig appConfig) 10 | : ICommandDef 11 | { 12 | public string Name { get; } = method.BuildName(CommandNodeType.Command, appConfig); 13 | public string SourcePath => method.FullName(includeNamespace: true); 14 | public Type CommandHostClassType { get; } = commandHostClassType; 15 | public ICustomAttributeProvider CustomAttributes => method; 16 | public bool IsExecutable => true; 17 | public bool HasInterceptor => false; 18 | public IReadOnlyCollection SubCommands { get; } = new List().AsReadOnly(); 19 | public IMethodDef? InterceptorMethodDef { get; } = null; 20 | public IMethodDef InvokeMethodDef { get; } = new MethodDef(method, appConfig, false); 21 | 22 | public override string ToString() => $"Method:{SourcePath} > {Name}"; 23 | } -------------------------------------------------------------------------------- /CommandDotNet/Parsing/CommandParser.OperandQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace CommandDotNet.Parsing; 5 | 6 | internal partial class CommandParser 7 | { 8 | private class OperandQueue(IEnumerable operands) 9 | { 10 | private readonly Queue _operands = new(operands); 11 | private Operand? _listOperand; 12 | 13 | public bool TryDequeue([NotNullWhen(true)] out Operand? operand) 14 | { 15 | operand = Dequeue(); 16 | return operand is not null; 17 | } 18 | 19 | private Operand? Dequeue() 20 | { 21 | // there can be only one list operand and it 22 | // is always the last operand 23 | if (_listOperand is not null) return _listOperand; 24 | if (_operands.Count == 0) return null; 25 | 26 | var operand = _operands.Dequeue(); 27 | if (operand.Arity.AllowsMany()) 28 | { 29 | _listOperand = operand; 30 | } 31 | return operand; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/GettingStarted/Getting_Started_OtherFeatures.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DocExamples.GettingStarted; 2 | 3 | public class Getting_Started_OtherFeatures 4 | { 5 | public class Program 6 | { 7 | static int Main(string[] args) => AppRunner.Run(args); 8 | 9 | public static AppRunner AppRunner => 10 | // begin-snippet: getting-started-other-features 11 | new AppRunner() 12 | .UseDefaultMiddleware(); 13 | // end-snippet 14 | 15 | public void Add(IConsole console, int x, int y) => console.WriteLine(x + y); 16 | 17 | public void Subtract(IConsole console, int x, int y) => console.WriteLine(x - y); 18 | } 19 | 20 | 21 | public static BashSnippet Help = new("getting-started-other-features_help", 22 | Program.AppRunner, 23 | "dotnet calculator.dll", "--help", 0, 24 | @"Usage: {0} [command] [options] 25 | 26 | Options: 27 | 28 | -v | --version 29 | Show version information 30 | 31 | Commands: 32 | 33 | Add 34 | Subtract 35 | 36 | Use ""{0} [command] --help"" for more information about a command."); 37 | } -------------------------------------------------------------------------------- /CommandDotNet.Tests/FeatureTests/OnRunCompletedTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Xunit; 4 | using Xunit.Abstractions; 5 | 6 | namespace CommandDotNet.Tests.FeatureTests; 7 | 8 | public class OnRunCompletedTests 9 | { 10 | public OnRunCompletedTests(ITestOutputHelper output) 11 | { 12 | Ambient.Output = output; 13 | } 14 | 15 | [Fact] 16 | public void OnRunCompleted_IsCalled_AfterPipelineIsExecuted() 17 | { 18 | bool wasCalled = false; 19 | new AppRunner() 20 | .Configure(b => b.OnRunCompleted += args => 21 | { 22 | // ensure OnRunCompleted waits for the pipeline to complete 23 | App.Executed.Should().BeTrue(); 24 | wasCalled = true; 25 | }) 26 | .RunInMem("Do"); 27 | 28 | wasCalled.Should().BeTrue(); 29 | } 30 | 31 | private class App 32 | { 33 | public static bool Executed { get; set; } 34 | 35 | public async Task Do() 36 | { 37 | await Task.Delay(1000); 38 | Executed = true; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bilal Fazlani, Drew Burlingame 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 | 23 | -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/OtherFeatures/Suggest_Examples.cs: -------------------------------------------------------------------------------- 1 | namespace CommandDotNet.DocExamples.OtherFeatures; 2 | 3 | public static class Suggest_Examples 4 | { 5 | // begin-snippet: suggest_basic_commands 6 | public class GitApp 7 | { 8 | [Command] 9 | public void Commit(string message) { } 10 | 11 | [Command] 12 | public void Push() { } 13 | 14 | [Command] 15 | public void Pull() { } 16 | } 17 | // end-snippet 18 | 19 | // begin-snippet: suggest_enum_values 20 | public enum LogLevel { Debug, Info, Warning, Error } 21 | 22 | public class App 23 | { 24 | public void SetLogLevel(LogLevel level) 25 | { 26 | System.Console.WriteLine($"Log level set to {level}"); 27 | } 28 | } 29 | // end-snippet 30 | 31 | // begin-snippet: suggest_nested_subcommands 32 | public class App2 33 | { 34 | [Subcommand] 35 | public class Remote 36 | { 37 | public void Add(string name, string url) { } 38 | public void Remove(string name) { } 39 | } 40 | } 41 | // end-snippet 42 | } 43 | -------------------------------------------------------------------------------- /CommandDotNet.Example/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | 4 | # can be marked as static 5 | dotnet_diagnostic.CA1822.severity=none 6 | # non-constant fields should not be visible 7 | dotnet_diagnostic.CA2211.severity=none 8 | resharper_arrange_type_member_modifiers_highlighting=none 9 | resharper_class_never_instantiated_global_highlighting=none 10 | resharper_inconsistent_naming_highlighting=none 11 | resharper_member_can_be_internal_highlighting=none 12 | resharper_member_can_be_private_global_highlighting=none 13 | resharper_member_hides_static_from_outer_class_highlighting=none 14 | resharper_method_overload_with_optional_parameter_highlighting=none 15 | resharper_type_member_is_never_used=none 16 | resharper_unused_auto_property_accessor_global_highlighting=none 17 | resharper_unused_auto_property_accessor_local_highlighting=none 18 | resharper_unused_member_global_highlighting=none 19 | resharper_unused_member_local_highlighting=none 20 | resharper_unused_parameter_highlighting=none 21 | resharper_unused_parameter_local_highlighting=none 22 | resharper_unused_type_global_highlighting=none 23 | resharper_unused_type_local_highlighting=none 24 | resharper_unused_variable_highlighting=none -------------------------------------------------------------------------------- /CommandDotNet.Tests/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | 4 | # can be marked as static 5 | dotnet_diagnostic.CA1822.severity=none 6 | # non-constant fields should not be visible 7 | dotnet_diagnostic.CA2211.severity=none 8 | resharper_arrange_type_member_modifiers_highlighting=none 9 | resharper_class_never_instantiated_global_highlighting=none 10 | resharper_inconsistent_naming_highlighting=none 11 | resharper_member_can_be_internal_highlighting=none 12 | resharper_member_can_be_private_global_highlighting=none 13 | resharper_member_hides_static_from_outer_class_highlighting=none 14 | resharper_method_overload_with_optional_parameter_highlighting=none 15 | resharper_type_member_is_never_used=none 16 | resharper_unused_auto_property_accessor_global_highlighting=none 17 | resharper_unused_auto_property_accessor_local_highlighting=none 18 | resharper_unused_member_global_highlighting=none 19 | resharper_unused_member_local_highlighting=none 20 | resharper_unused_parameter_highlighting=none 21 | resharper_unused_parameter_local_highlighting=none 22 | resharper_unused_type_global_highlighting=none 23 | resharper_unused_type_local_highlighting=none 24 | resharper_unused_variable_highlighting=none -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | 4 | # can be marked as static 5 | dotnet_diagnostic.CA1822.severity=none 6 | # non-constant fields should not be visible 7 | dotnet_diagnostic.CA2211.severity=none 8 | resharper_arrange_type_member_modifiers_highlighting=none 9 | resharper_class_never_instantiated_global_highlighting=none 10 | resharper_inconsistent_naming_highlighting=none 11 | resharper_member_can_be_internal_highlighting=none 12 | resharper_member_can_be_private_global_highlighting=none 13 | resharper_member_hides_static_from_outer_class_highlighting=none 14 | resharper_method_overload_with_optional_parameter_highlighting=none 15 | resharper_type_member_is_never_used=none 16 | resharper_unused_auto_property_accessor_global_highlighting=none 17 | resharper_unused_auto_property_accessor_local_highlighting=none 18 | resharper_unused_member_global_highlighting=none 19 | resharper_unused_member_local_highlighting=none 20 | resharper_unused_parameter_highlighting=none 21 | resharper_unused_parameter_local_highlighting=none 22 | resharper_unused_type_global_highlighting=none 23 | resharper_unused_type_local_highlighting=none 24 | resharper_unused_variable_highlighting=none -------------------------------------------------------------------------------- /CommandDotNet.DocExamples/OtherFeatures/NameCasing_Examples.cs: -------------------------------------------------------------------------------- 1 | using CommandDotNet.NameCasing; 2 | 3 | namespace CommandDotNet.DocExamples.OtherFeatures; 4 | 5 | public static class NameCasing_Examples 6 | { 7 | // begin-snippet: name_casing_kebab_case 8 | public class App 9 | { 10 | public void MigrateUser([Option]bool dryRun) 11 | { 12 | // With kebab-case transformation: 13 | // command: migrate-user 14 | // option: --dry-run 15 | } 16 | } 17 | // end-snippet 18 | 19 | // begin-snippet: name_casing_override_attribute 20 | public class App2 21 | { 22 | [Command("migrateUser")] 23 | public void MigrateUser([Option]bool dryRun) 24 | { 25 | // Command name is "migrateUser" (not transformed) 26 | // Option is still "--dry-run" (transformed) 27 | } 28 | } 29 | // end-snippet 30 | 31 | // begin-snippet: name_casing_usage 32 | public static int Main(string[] args) 33 | { 34 | return new AppRunner() 35 | .UseNameCasing(Case.KebabCase) 36 | .Run(args); 37 | } 38 | // end-snippet 39 | } 40 | -------------------------------------------------------------------------------- /CommandDotNet.TestTools/TestConsoleWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace CommandDotNet.TestTools; 5 | 6 | internal class TestConsoleWriter : TextWriter 7 | { 8 | internal TextWriter? Replaced { get; set; } 9 | private readonly TestConsoleWriter? _inner; 10 | private readonly bool _trimEnd; 11 | private readonly StringBuilder _stringBuilder = new(); 12 | 13 | public TestConsoleWriter(TestConsoleWriter? inner = null, bool trimEnd = false) 14 | { 15 | _inner = inner; 16 | _trimEnd = trimEnd; 17 | } 18 | 19 | public override void Write(char value) 20 | { 21 | Replaced?.Write(value); 22 | _inner?.Write(value); 23 | if (value == '\b' && _stringBuilder.Length > 0) 24 | { 25 | _stringBuilder.Length -= 1; 26 | } 27 | else 28 | { 29 | _stringBuilder.Append(value); 30 | } 31 | } 32 | 33 | public override Encoding Encoding { get; } = Encoding.Unicode; 34 | 35 | public override string ToString() => 36 | _trimEnd 37 | ? _stringBuilder.ToString().TrimEnd() 38 | : _stringBuilder.ToString(); 39 | } -------------------------------------------------------------------------------- /CommandDotNet/ClassModeling/Definitions/DelegateCommandDef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using CommandDotNet.Execution; 5 | using CommandDotNet.Extensions; 6 | 7 | namespace CommandDotNet.ClassModeling.Definitions; 8 | 9 | /// This can be used for methods passed into 10 | internal class DelegateCommandDef(string name, Delegate handlerDelegate, AppConfig appConfig) : ICommandDef 11 | { 12 | public string Name { get; } = name; 13 | public string SourcePath => handlerDelegate.Method.FullName(includeNamespace: true); 14 | public Type? CommandHostClassType { get; } = null; 15 | public ICustomAttributeProvider CustomAttributes => handlerDelegate.Method; 16 | public bool IsExecutable => true; 17 | public bool HasInterceptor => false; 18 | public IReadOnlyCollection SubCommands { get; } = new List().AsReadOnly(); 19 | public IMethodDef? InterceptorMethodDef { get; } = null; 20 | public IMethodDef InvokeMethodDef { get; } = new MethodDef(handlerDelegate.Method, appConfig, false); 21 | 22 | public override string ToString() => $"Delegate:{SourcePath} > {Name}"; 23 | } -------------------------------------------------------------------------------- /CommandDotNet.TestTools/Prompts/ConsoleKeyInfos.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using CommandDotNet.Extensions; 5 | 6 | namespace CommandDotNet.TestTools.Prompts; 7 | 8 | public static class ConsoleKeyInfos 9 | { 10 | public static ConsoleKeyInfo BackSpaceKey = new('\b', ConsoleKey.Backspace, false, false, false); 11 | public static ConsoleKeyInfo EnterKey = new('\r', ConsoleKey.Enter, false, false, false); 12 | public static ConsoleKeyInfo CtrlCKey = new('c', ConsoleKey.C, false, false, true); 13 | public static ConsoleKeyInfo EscapeKey = new(' ', ConsoleKey.Escape, false, false, false); 14 | 15 | public static IEnumerable AppendEnterKey(this IEnumerable consoleKeyInfos) => 16 | consoleKeyInfos.Concat(EnterKey.ToEnumerable()); 17 | 18 | public static IEnumerable AppendCtrlCKey(this IEnumerable consoleKeyInfos) => 19 | consoleKeyInfos.Concat(CtrlCKey.ToEnumerable()); 20 | 21 | public static IEnumerable AppendEscapeKey(this IEnumerable consoleKeyInfos) => 22 | consoleKeyInfos.Concat(EscapeKey.ToEnumerable()); 23 | } -------------------------------------------------------------------------------- /CommandDotNet/ArgumentGroupAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// Specifies a default group name for all options defined in an . 8 | /// This group will be inherited by all option properties in the model and any nested models, 9 | /// unless overridden by a property-level or a nested model's 10 | /// . 11 | /// 12 | [PublicAPI] 13 | [AttributeUsage(AttributeTargets.Class)] 14 | public class ArgumentGroupAttribute : Attribute 15 | { 16 | /// 17 | /// The default group name for all options in this argument model. 18 | /// 19 | public string GroupName { get; } 20 | 21 | /// 22 | /// Specifies a default group name for all options defined in this . 23 | /// 24 | /// The group name to apply to all options in this model 25 | public ArgumentGroupAttribute(string groupName) 26 | { 27 | GroupName = groupName ?? throw new ArgumentNullException(nameof(groupName)); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /CommandDotNet/DescriptionMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace CommandDotNet; 5 | 6 | /// 7 | /// Specifies a static method that will be called to generate the description dynamically at runtime. 8 | /// The method must be static, take no parameters, and return a string. 9 | /// 10 | [PublicAPI] 11 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] 12 | public class DescriptionMethodAttribute : Attribute 13 | { 14 | /// 15 | /// Gets the name of the method to call for generating the description. 16 | /// 17 | public string MethodName { get; } 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The name of the static method to call for generating the description. 23 | /// Thrown when is null. 24 | public DescriptionMethodAttribute(string methodName) 25 | { 26 | MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CommandDotNet/Builders/IArgumentNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CommandDotNet.Builders; 4 | 5 | /// 6 | /// Represents the nested nature of the arguments that can be expressed in the command line; 7 | /// , and .
8 | /// s are included as they are arguments of other commands.
9 | ///
10 | public interface IArgumentNode : ICustomAttributesContainer, IServicesContainer 11 | { 12 | /// The name 13 | string Name { get; } 14 | 15 | /// The description 16 | string? Description { get; } 17 | 18 | /// 19 | /// The that hosts this . 20 | /// Is null for the root command. Some parent commands are not executable 21 | /// but are defined only to group for other commands. 22 | /// 23 | Command? Parent { get; } 24 | 25 | /// The aliases defined for this argument 26 | IReadOnlyCollection Aliases { get; } 27 | 28 | /// The source that defined this argument node 29 | string? DefinitionSource { get; } 30 | } -------------------------------------------------------------------------------- /CommandDotNet/LocalizationAppSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommandDotNet.Extensions; 3 | using JetBrains.Annotations; 4 | 5 | namespace CommandDotNet; 6 | 7 | [PublicAPI] 8 | public class LocalizationAppSettings : IIndentableToString 9 | { 10 | // begin-snippet: LocalizationAppSettings 11 | /// When specified, this function will be used to localize user output from the framework 12 | public Func? Localize { get; set; } 13 | 14 | /// 15 | /// By default, the keys passed to the delegate 16 | /// are the values define in the Resources class.
17 | /// Setting this to true will use the property or method names instead of the values.
18 | /// Example: for property - `Common_argument_lc => "argument"`
19 | /// the default key is "argument".
20 | /// When is set to true, "Common_argument_lc" is the key. 21 | ///
22 | public bool UseMemberNamesAsKeys { get; set; } 23 | // end-snippet 24 | 25 | public override string ToString() => ToString(new Indent()); 26 | 27 | public string ToString(Indent indent) => this.ToStringFromPublicProperties(indent); 28 | } --------------------------------------------------------------------------------