{"id":13414922,"url":"https://github.com/rushfive/RunInfoBuilder","last_synced_at":"2025-03-14T22:32:27.283Z","repository":{"id":144137160,"uuid":"142170662","full_name":"rushfive/RunInfoBuilder","owner":"rushfive","description":"A unique command line parser for .NET that utilizes object trees for commands.","archived":false,"fork":false,"pushed_at":"2019-01-21T02:53:43.000Z","size":540,"stargazers_count":43,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-11T11:08:35.455Z","etag":null,"topics":["cli","csharp","dotnet"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rushfive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-07-24T14:32:22.000Z","updated_at":"2024-07-29T18:17:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"138db9f8-4fe9-48f2-ab07-e53a56d9d1ab","html_url":"https://github.com/rushfive/RunInfoBuilder","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rushfive%2FRunInfoBuilder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rushfive%2FRunInfoBuilder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rushfive%2FRunInfoBuilder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rushfive%2FRunInfoBuilder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rushfive","download_url":"https://codeload.github.com/rushfive/RunInfoBuilder/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243658057,"owners_count":20326459,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","csharp","dotnet"],"created_at":"2024-07-30T21:00:39.908Z","updated_at":"2025-03-14T22:32:27.008Z","avatar_url":"https://github.com/rushfive.png","language":"C#","readme":"# Command Line Parser for NetStandard\n\n[![Build status](https://img.shields.io/appveyor/ci/rushfive/runinfobuilder.svg)](https://ci.appveyor.com/project/rushfive/runinfobuilder)\n[![Nuget](https://img.shields.io/nuget/v/r5.runinfobuilder.svg)](https://www.nuget.org/packages/R5.RunInfoBuilder/)\n\nThis library provides a clean and simple API for parsing program arguments into a `RunInfo` object.\n\n_Targets NET Standard 2.0_\n\n### Core Features at a Glance\n\n- Supports core command line abstractions such as `Commands`, `Subcommands`, `Arguments`, `Options`, etc.\n- Comes with many different `Argument` types to handle common use cases, such as 1-to-1 property mappings, binding values to a sequence, mutually-exclusive sets, etc.\n- Comes with a `Parser` that handles the most common system types out of the box. Easily configurable to handle any arbitrary types.\n- Provides a cleanly formatted `help` menu by default, with options to configure further.\n- Several `hooks` which provide custom extensibility points in various stages of the build process.\n\n### Code Configuration over Attributes\n\nThere are no attributes used to mark the resulting `RunInfo` class properties. Rather, you configure commands by providing a `Command` object as a representation of an object tree. C# provides a clean way to express nested objects through its object initializers so RunInfoBuilder makes use of that.\n\nUsing attributes to _tell_ a command line parser how to interpret things works for very simple binding schemes. But if you need to go beyond that, for example, by using custom callbacks for validations or as extensibility points, using pure code to define the commands works better.\n\n---\n\n### Getting Started\n\nInstall via __NuGet__ or __DotNet__:\n\n```\nInstall-Package R5.RunInfoBuilder\n```\n\n```\ndotnet add package R5.RunInfoBuilder\n```\n\n---\n\n### A Simple Example\n\nA program is desired that can read some message from the program arguments, and then do one of many things as determined by a command.\n\nFor example, it may take the message and send it off to some HTTP endpoint. Also, the user can _optionally_ specify that the request should be retried on fail.\n\nThe required information for this has been collected into this `RunInfo` class:\n\n```\npublic class SendRequestRunInfo\n{\n    public string RequestUrl { get; set; }\n    public string Message { get; set; }\n    public int DelayMinutes { get; set; }\n    public bool RetryOnFail { set; set; }\n}\n```\n\nThe program should take three program arguments and simply bind them to the properties. \nTo do this, a `Command` called `sendhttp` is added to the _CommandStore_:\n\n```\n// initialize a builder instance\nvar builder = new RunInfoBuilder();\n\n// add the 'sendhttp' command to the store\nbuilder.Commands.Add(new Command\u003cSendRequestRunInfo\u003e\n{\n    Key = \"sendhttp\",\n    Arguments =\n    {\n        new PropertyArgument\u003cSendRequestRunInfo, string\u003e\n        {\n            Property = ri =\u003e ri.RequestUrl\n        },\n        new PropertyArgument\u003cSendRequestRunInfo, string\u003e\n        {\n            Property = ri =\u003e ri.Message\n        },\n        new PropertyArgument\u003cSendRequestRunInfo, int\u003e\n        {\n            Property = ri =\u003e ri.DelayMinutes\n        }\n    },\n    Options =\n    {\n        new Option\u003cSendRequestRunInfo, bool\u003e\n        {\n            Key = \"retry | r\",\n            Property = ri =\u003e ri.RetryOnFail\n        }\n    }\n});\n\n// build the run info object by passing program arguments (led by the command's key)\nvar args = new string[] { \"sendhttp\", \"http://www.somewhere.com\", \"hello from program!\", \"3\", \"--retry\" };\nvar runInfo = builder.Build(args);\n```\n\nThe resulting `runInfo` variable will be of type `SendRequestRunInfo` with the expected values:\n\n```\n{\n    RequestUrl: 'http://www.somewhere.com',\n    Message: 'hello from program!',\n    DelayMinutes: 3,\n    RetryOnFail: true\n}\n```\n\nThe values were parsed from the program arguments and bound to the RunInfo properties as configured. Also, the `RetryOnFail` property was set to `true` because the option was specified (`--retry`). The option could also have been specified by `-r` instead because a short key was configured for the option.\n\nThis is a very simple example, illustrating the most basic of binding requirements: simple 1-to-1 mappings of program arguments to properties. \n\nThere's a lot more that can be done and configured, but hopefully you can at least see how simple and expressive defining commands through an object is. You can take a quick look at any command configuration and immediately know how it parses the program arguments.\n\nIf this has captured your interest, keep reading below for a deeper dive into all the features and areas of RunInfoBuilder.\n\n---\n\n## In-Depth Documentation\n\nTopics covered below:\n- [Command Processing Overview](#command-processing-overview)\n- [Custom Callbacks](#custom-callbacks)\n- [Commands and the Default Command](#commands-and-the-default-command)\n  - [Command Store](#command-store)\n  - [Command](#command)\n  - [Default Command](#default-command)\n- [Arguments](#arguments)\n  - [Property Argument](#property-argument)\n  - [Set Argument](#set-argument)\n  - [Custom Argument](#custom-argument)\n  - [Sequence Argument](#sequence-argument)\n- [Options](#options)\n- [Parser](#parser)\n- [Hooks](#hooks)\n- [Help Menu](#help-menu)\n- [Version](#version)\n- [Gotchas and Limitations](#gotchas-and-limitations)\n\n\n---\n\n### Command Processing Overview\n\nBefore diving into `Command` configuration, we need to understand the order in which commands, and their child items like subcommands and options are processed.\n\nIt doesn't matter if you have just a single `Command` or one with many levels of `SubCommands` nested within it. When the builder begins processing a `Command` or `SubCommand`, it will always go through the same steps in order as depicted below:\n\n![alt text](/Documentation/Images/command_flow_diagram.png)\n\n#### 1. Arguments\n\nArguments are processed first, and in the same order they're defined in the `Command` object added to the store.\n\nThey are also all required, so an exception will be thrown if no more program arguments are found.\n\n#### 2. Options\n\nAny `Options` are processed immediately after the command's `Arguments` are, and can appear in any order. They are optional and any number of them can be specified.\n\n#### 3. SubCommands\n\nA `Command` can contain nested `SubCommands` in a list, which are processed after `Options`. If any are configured, it is required that one is matched by the next program argument.\n\nFor example, if a command called `search` has two subCommands configured, `outside` and `inside`, then the two valid methods of calling the `search` command would be\n\n`search outside` or `search inside` (we're ignoring any arguments or options for brevity here)\n\nHere's a few examples of the `search` command being called incorrectly:\n\n`search` - an exception will be thrown because subCommands have been configured but one wasn't specified.\n\n`search everywhere` - an exception will be thrown because `everywhere` doesn't match a valid subCommand.\n\n`search outside inside` - this simply makes no sense. It's not possible to call more than one subCommand from the list. One, and only one, must be matched.\n\nA `SubCommand` is essentially the same type as a `Command` (just without the `GlobalOptions` property, more on that later). This results in a `Command` definition being a recursive tree structure, which can be nested arbitrarily deep:\n\n![alt text](/Documentation/Images/command_tree_diagram.png)\n\nObserving the command tree diagram above, when you create a `Command`, a valid processing will always start at the root node and traverse downwards (think DFS-like) until it hits and finishes processing the last `SubCommand` (a leaf node).\n\nAlthough it's technically possible to create a `Command` with an arbitrary number of layers, it's probably best to limit it. Else, you risk the program having a confusing API.\n\n_To recap: All `Arguments` and `Options` for a given `Command` are processed first, in that order. After which, _if_ any `SubCommands` exist, the matching one will be processed in the same manner. And so on and so forth._\n\n---\n\n### Custom Callbacks\n\nWhen configuring `Commands`, there are several places where you can provide custom callbacks. Most of these are `Func`s that must return a `ProcessStageResult`. The type that is returned will determine whether the builder continues processing or stops early.\n\nA static helper class exists with some members that makes returning the correct type easier.\n\nTo continue: `return ProcessResult.Continue`.\n\nTo end early: `return ProcessResult.End`.\n\n---\n\n### Commands and the Default Command\n\n#### Command Store\n\nAll `Commands` are configured on the builder's `CommandStore` object. The store provides two methods with the following interface:\n\n```\nCommandStore Add(Command\u003cTRunInfo\u003e command, Action\u003cTRunInfo\u003e postBuildCallback = null);\nCommandStore AddDefault(DefaultCommand\u003cTRunInfo\u003e defaultCommand), Action\u003cTRunInfo\u003e postBuildCallback = null;\n```\n\nIf the optional `postBuildCallback` action is set, it will be called after the program arguments are done processing, receiving the resolved `RunInfo` object as its single argument:\n\n```\nbuilder.Commands.Add(command, runInfo =\u003e\n{\n    // do something with the resolved runInfo\n});\n```\n\n#### Command\n\nType: `Command\u003cTRunInfo\u003e`\n- `TRunInfo`parameter is the `RunInfo` class the command is associated to.\n\nThe `Command` is the core entity, as everything else is nested within it.\n\nProperties:\n- Key - `string` - A unique keyword that represents the `Command`. This only needs to be unique within a given `Command`. For example, both a `Command` and one of its nested `SubCommands` can have the same key.\n- Description - `string` - Text that's displayed in the help menu.\n- Arguments - `List\u003cArgumentBase\u003cTRunInfo\u003e\u003e` - A list of `Arguments` required by the `Command`. Details of the different `Argument` types are discussed later.\n- Options - `List\u003cOptionBase\u003cTRunInfo\u003e\u003e` - A list of `Options` associated to the `Command`.\n- SubCommands - `List\u003cSubCommand\u003cTRunInfo\u003e\u003e` - A list of `SubCommands` associated to the `Command`. \n- GlobalOptions - `List\u003cOptionBase\u003cTRunInfo\u003e\u003e` - A list of `Options` that are available to the `Command` and any of its nested `SubCommands`.\n- OnMatched - `Func\u003cTRunInfo, ProcessStageResult\u003e` - An optional callback that's invoked immediately after the command is matched and begins processing (happens before anything else like arguments, options, etc).\n\nDescriptions and example configurations for `Arguments` and `Options` can be found later in their respective sections.\n\nThe `SubCommand\u003cTRunInfo\u003e` type has the same properties as `Command\u003cTRunInfo\u003e`, with the exception of `GlobalOptions` which is only available on the root `Command` object. Once a global option is configured, it's made available to the `Command` and any of its `SubCommands`, whereas normal options are scoped to the `Command` or `SubCommand` it's configured in. \n\nThe global option keys must also be unique, relative to any of the options configured in the `Command` or `SubCommands`. \n\nAn arbitrary number of `Commands` can be added to the store:\n\n```\nbuilder.Commands.Add(new Command\u003cTRunInfo\u003e\n{\n    Key = \"command_key\",\n    Description = \"command description\",\n    Arguments =\n    {\n        // arguments for this command\n    },\n    Options =\n    {\n        // options scoped specifically to this command\n    },\n    SubCommands =\n    {\n        new SubCommand\u003cTRunInfo\u003e\n        {\n            Key = \"subcommand\",\n            Arguments =\n            {\n                // arguments for this subcommand\n            },\n            Options = \n            {\n                // options scoped specifically to this subCommand\n            },\n            SubCommands = \n            {\n                // need another level of subCommands? Sure, add em here!\n            },\n            OnMatched = runInfo =\u003e\n            {\n                // immediately fires if this subCommand is matched\n            }\n        },\n        // add as many SubCommands as needed\n    },\n    GlobalOptions =\n    {\n        // options scoped to be accessible from this command or any subcommand in the tree\n    },\n    OnMatched = runInfo =\u003e \n    {\n        // do something with runInfo\n        return ProcessResult.Continue;\n    }\n});\n```\n\n#### Default Command\n\nType: `DefaultCommand\u003cTRunInfo\u003e`\n- `TRunInfo` is the `RunInfo` class the default command is associated to.\n\nYou can optionally include a single `DefaultCommand`. This behaves exactly like a normal `Command`, except that it doesn't include the `Key`, `SubCommands`, or `GlobalOptions` properties. It's a simple single-level command that can only process `Arguments`, `Options`, and the `OnMatched` callback.\n\nThis can be useful if your program doesn't require more than a single command. Or even if it does have a list of commands, it could be useful to provide some default behavior if it doesn't fit within your program's definition of a command.\n\nOnly a single `DefaultCommand` can be configured:\n\n```\nbuilder.Commands.AddDefault(new DefaultCommand\u003cTRunInfo\u003e\n{\n    Description = \"default command description\",\n    Arguments =\n    {\n        // ... arguments ...\n    },\n    Options =\n    {\n        // ... options ...\n    },\n    OnMatched = runInfo =\u003e \n    {\n        // do something with runInfo\n        return ProcessResult.Continue;\n    }\n});\n```\n\n---\n\n### Arguments\n\nAll `Arguments` are required (matching program arguments must be found). The order in which they're configured is significant: program arguments must also appear in the same order to correctly match an `Argument`.\n\n#### Property Argument\n\nType: `PropertyArgument\u003cTRunInfo, TProperty\u003e`\n- `TRunInfo` is the `RunInfo` class the property is associated to.\n- `TProperty` represents the type of the mapped `RunInfo` property.\n\nProperty argument's take the next single program argument, then attempts to parse and bind it to the configured `RunInfo` property\n\n_An exception is thrown if the program argument cannot be parsed into a `TProperty` type._\n\nProperties:\n- HelpToken - `string` - The text that appears in the help menu representing this `PropertyArgument`. It should be short and succinct. For example, a `HelpToken` could be `\"\u003cstring\u003e\"`, indicating to the user that this `PropertyArgument` binds to a string property.\n- Property - `Expression\u003cFunc\u003cTRunInfo, TProperty\u003e\u003e` - An expression representing the `RunInfo` property the parsed value will be bound to.\n- OnParsed - `Func\u003cTProperty, ProcessStageResult\u003e` - An optional custom callback that is invoked after a valid value has been parsed. The callback will be invoked with that value as its single argument, and return a `ProcessStageResult`. If the callback returns `ProcessResult.End`, processing will stop __before__ the parsed value is bound to the property.\n- OnParseErrorUseMessage - `Func\u003cstring, string\u003e` - An optional function used to generate the error message on parsing error. The single argument is the program argument that failed to parse.\n\n_Example Configuration:_\n\n```\nArguments =\n{\n    new PropertyArgument\u003cSendRequestRunInfo, string\u003e\n    {\n        HelpToken = \"\u003cmsg\u003e\",\n        Property = ri =\u003e ri.Message,\n        OnParsed = value =\u003e\n        {\n            if (value == \"dont send\")\n            {\n                throw new Exception(\"Shouldn't send!\");\n            }\n            return ProcessResult.Continue;\n        },\n        OnParseErrorUseMessage = arg =\u003e $\"Failed to parse program argument '{arg}' because ...\"\n    }\n}\n```\n\n#### Set Argument\n\nType: `SetArgument\u003cTRunInfo, TProperty\u003e`\n- `TRunInfo` is the `RunInfo` class the property is associated to.\n- `TProperty` represents the type of the mapped `RunInfo` property.\n\nSet arguments provide a list of tuples in the form `(key, boundValue)`. If the program argument matches one of the keys, its paired value will be bound to the `RunInfo` property.\n\n_An exception is thrown if the program argument doesn't match a key._\n\nProperties:\n- HelpToken - `string` - The text that appears in the help menu representing this `SetArgument`. It should be short and succinct. For example, a `HelpToken` could be `\"(a|b|c)\"`, indicating that the acceptable values are \"a\", \"b\", and \"c\".\n- Property - `Expression\u003cFunc\u003cTRunInfo, TProperty\u003e\u003e` - An expression representing the `RunInfo` property the paired value will be bound to.\n- Values - `List\u003c(string, TProperty)\u003e` - List of tuples representing the key and value pairings.\n\n_Example Configuration:_\n\n```\nArguments =\n{\n    new SetArgument\u003cSendRequestRunInfo, int\u003e\n    {\n        HelpToken = \"(now|one|five)\",\n        Property = ri =\u003e ri.DelayMinutes,\n        Values =\n        {\n            (\"now\", 0), (\"one\", 1), (\"five\", 5)\n        }\n    }\n}\n```\n\nIn the example above, a value of 0, 1, or 5 will be bound to the `DelayMinutes` property, depending on which key the program argument matched.\n\n#### Custom Argument\n\nType: `CustomArgument\u003cTRunInfo\u003e`\n- `TRunInfo` is the `RunInfo` class the property is associated to.\n\nCustom arguments handle a configurable number of consecutive program arguments through a callback that you provide.\n\nProperties:\n- HelpToken - `string` - The token that appears in the help menu representing this custom argument. Example: `\"\u003cfirst\u003e \u003csecond\u003e \u003cthird\u003e\"` \n- Count - `int` - The number of program arguments the callback will handle.\n- Handler - `Func\u003cCustomHandlerContext\u003cTRunInfo\u003e, ProcessStageResult\u003e` - The custom callback that will handle the program arguments.\n\nThe callback provides a `CustomHandlerContext` with the following properties:\n- RunInfo - `TRunInfo` - The RunInfo instance so you can modify it yourself.\n- ProgramArguments - `List\u003cstring\u003e` - A list containing the program arguments to be handled (as set by the `Count` property).\n- Parser - `ArgumentParser` - The same Parser that's configured on the builder.\n\n_Example Configuration:_\n\n```\nArguments =\n{\n    new CustomArgument\u003cSendRequestRunInfo\u003e\n    {\n        HelpToken = \"\u003cgreeting\u003e \u003cname\u003e\",\n        Count = 2,\n        Handler = context =\u003e\n        {\n            string greeting = context.ProgramArguments[0];\n            string name = context.ProgramArguments[1];\n            context.RunInfo.Message = $\"{greeting} {name}!\";\n            \n            return ProcessResult.Continue;\n        }\n    }\n}\n```\n\nIn this example, the custom argument will handle two program arguments: the first representing a greeting (eg \"hello\"), and the second representing the name of the recipient (eg \"bob\"). The callback simply concatenates the two values to use as the message (`\"hello bob!\"`).\n\n#### Sequence Argument\n\nType: `SequenceArgument\u003cTRunInfo, TListProperty\u003e`\n- `TRunInfo` is the `RunInfo` class the property is associated to.\n- `TListProperty` represents the type of the `List\u003cT\u003e` the parsed values will be added to.\n\nSequence arguments take consecutive program arguments, parsing and adding them to the configured list.\n\n_Note: the builder will continue to consider program arguments as long as they aren't an `Option` or `SubCommand`. Don't configure other `Arguments` after a `SequenceArgument` - sequences should either be the last or only `Argument` in a command._\n\n_An exception is thrown if any of the considered program arguments fail to parse into a `TListProperty`._\n\nProperties:\n- `HelpToken` (`string`) - The token that appears in the help menu representing this `SequenceArgument`. Example: `\"\u003c...int\u003e\"` .\n- `ListProperty` (`Expression\u003cFunc\u003cTRunInfo, List\u003cTListProperty\u003e\u003e\u003e`) - An expression representing the `RunInfo` list property the values will be added to.\n- `OnParsed` (`Func\u003cTListProperty, ProcessStageResult\u003e`) - An optional custom callback that is invoked for every value after they are parsed. The callback will be invoked with that value as its single argument, and return a `ProcessStageResult`. If the callback returns `ProcessResult.End`, processing will stop __before__ the parsed value is added to the property. \n- `OnParseErrorUseMessage` (`Func\u003cstring, string\u003e`) - An optional function used to generate the error message on parsing error. The single argument is the program argument that failed to parse.\n\n_Example Configuration:_\n\n```\nArguments =\n{\n    new SequenceArgument\u003cRunInfo, int\u003e\n    {\n        HelpToken = \"\u003c...int\u003e\",\n        ListProperty = ri =\u003e ri.ListOfNumbers,\n        OnParsed = value =\u003e\n        {\n            if (value \u003c 10) \n            {\n                return ProcessResult.End;\n            }\n            return ProcessResult.Continue;\n        },\n        OnParseErrorUseMessage = arg =\u003e $\"Failed to parse program argument '{arg}' because ...\"\n    }\n}\n```\n\nIn the example above, the builder will continue to parse and add program arguments as ints. However, if the parsed int value is less than 10, it will stop further processing.\n\n---\n\n### Options\n\nOptions allow you to setup optional 1-to-1 bindings to a property on the `RunInfo`. The user specifies an option using the standard `--option` (full) or `-o` (short) syntax. \n\n_`Options` can appear in any order in the `Command` configuration, unlike `Arguments` where order matters (because they're all required)._\n\n`Options` are bound to a property on the `RunInfo`, and its value is determined in one of two ways:\n\n- By parsing the right side of the `=` character in an option program argument: For example, if the program argument is `\"--option=value\"`, then the string `\"value\"` will be parsed into the expected type and bound to the property.\n- By parsing the next program argument: If an option was declared without the `=`, the builder will simply assume the next program argument is its intended value, and will parse and bind that to the property.\n\nType: `Option\u003cTRunInfo, TProperty\u003e`\n- `TRunInfo` is the `RunInfo` class the option is associated to.\n- `TProperty` represents the type of the property the parsed option value will be bound to.\n\nProperties:\n- `Key` (`string`) - A string representing the option key. For example, if it's set as `\"hide\"`, it would be called as `\"--hide\"` in a program argument. You can optionally set a short key by delimiting the string with a `|` character. If the key is set to `\"hide | h\"`, then you could use this option with either `\"--hide\"` or `\"-h\"`.\n- `Property` (`Expression\u003cFunc\u003cTRunInfo, TProperty\u003e\u003e`) - An expression representing the `RunInfo` property the parsed value will be bound to.\n- `HelpToken` (`string`) - The token that appears in the help menu representing this option. Example: `\"[--hide|-h]\"`.\n- `OnParseErrorUseMessage` (`Func\u003cstring, string\u003e`) - An optional function used to generate the error message on parsing error. The single argument is the program argument (representing option's value) that failed to parse.\n\n_Stacking `bool` options_\n\nMultiple `bool` options can be _stacked_ using the short syntax by combining their single character short keys. Re-emphasis on the `bool` constraint: stacking short options are not allowed on any other types.\n\nGlobal options can also be stacked with other options from the `Command` or any of its `SubCommands`. \n\n```\nOptions =\n{\n    new Option\u003cRunInfo, int\u003e\n    {\n        Key = \"minutes | m\"\n        HelpToken = \"[--minutes|-m]\",\n        Property = ri =\u003e ri.DelayMinutes,\n        OnParsed = value =\u003e\n        {\n            if (value \u003c 10) \n            {\n                return ProcessResult.End;\n            }\n            return ProcessResult.Continue;\n        },\n        OnParseErrorUseMessage = arg =\u003e $\"Failed to parse program argument '{arg}' because ...\"\n    }\n}\n```\n\nIn the example above, the user can set an `int` value for the `DelayMinutes` property on the `RunInfo` object through these program argument(s):\n- `\"--minutes=5\"`\n- `\"--minutes\", \"5\"`\n- `\"-m=5\"`\n- `\"-m\", \"5\"`\n\nBoth _full_ and _short_ keys must be unique within a given `Command`. This means that a command and its subcommand can have options that share the same option keys.\n\n---\n\n### Parser\n\nThe parsing of program arguments into the configured `Types` is handled by the `ArgumentParser`, available as a property on the `RunInfoBuilder` object and in some callback contexts.\n\nThe following types are automatically handled out-of-the-box, using the standard `TYPE.TryParse`  methods:\n- string\n- bool\n- byte\n- char\n- DateTime\n- decimal\n- double\n- int\n\nConfiguring the parser is easy, the following methods are available:\n\n__`ArgumentParser EnumParsingIgnoresCase()`__\n\nThe parser automatically handles enum types. By default, it does a _case-sensitive_ comparison of the program argument to the enum values. Calling this method will set all future comparisons to be _case-insensitive_.\n\n__`ArgumentParser SetPredicateForType\u003cT\u003e(Func\u003cstring, (bool isValid, T parsed)\u003e predicateFunc)`__\n\nThis method allows you to extend the parser to handle additional types (or re-configure how an already-handled `Type` should be parsed).\n\nYou provide a `Func` that takes in the program argument as its single argument, and it returns a `ValueTuple` where the first item represents whether the parsing was successful, and the second item being the parsed object. The custom predicates set using this method are used internally, and the second item (value) in the tuple is _always_ ignored if the parsing failed.\n\n__`bool TryParseAs(Type type, string value, out object parsed)`__\n\nAttempts to parse the value as the specified type. The method returns a `bool` indicating a successful parse, with the parsed object being returned as an `out` parameter.\n\n__`bool TryParseAs\u003cT\u003e(string value, out T parsed)`__\n\nThe same as above, but the `Type` is specified generically.\n\n__`bool HandlesType(Type type)`__\n\nReturns a `bool`, indicating whether the parser handles the given `Type`.\n\n__`HandlesType\u003cT\u003e()`__\n\nThe same as above, but the `Type` is specified generically.\n\n---\n\n### Hooks\n\nThe builder provides some hooks to invoke custom functionality at different phases of the build process. \n\nSetting these hooks is done on the `BuildHooks` object, found as the property `Hooks` on the `RunInfoBuilder` class.\n\nThe following methods are available:\n\n__`BuildHooks OnStartBuild(Action\u003cstring[]\u003e callback)`__\n\nSet a callback that receives the program arguments as it's single argument. This is invoked as the very first thing, immediately after the builder's `Build(args)` method is called.\n\n__`BuildHooks ArgsNullOrEmptyReturns\u003cTReturn\u003e(Func\u003cTReturn\u003e callback)`__\n\nIf you need to define what kind of resulting object is returned when the program arguments is `null` or empty, use this hook by defining a `Func\u003cTReturn\u003e` callback. Running the build will return whatever the custom callback returns.\n\n---\n\n### Help Menu\n\nProviding a help menu is essential to any program with a CLI, so this library provides some nice default behavior in that area.\n\nThe help menu is configured on the `HelpManager` object, found as the property `Help` on the `RunInfoBuilder` class. \n\nHere's an example of a help menu that's displayed by default (taken from the _GitCloneSample_ project):\n\n```\nadd - Add file contents to the index.\n  Usage: git add [--dry-run|-n] [--verbose|-v] [--ignore-errors] [--refresh]\n\nbranch - List, create, or delete branches.\n  Usage: git branch \u003cbranchname\u003e [--delete|-d] [--force|-f] [--ignore-case|-i]\n\ncheckout - Switch branches or restore working tree files.\n  Usage: git checkout \u003cbranch\u003e [--quiet|-q] [--force|-f] [--track|-t]\n\ncommit - Stores the current contents of the index in a new commit along with a log message from the user describing the changes.\n  Usage: git commit [--all|-a] [--patch|-p] [--branch]\n\ndiff - Switch branches or restore working tree files.\n  Usage: git diff [first-branch]...[second-branch] [--no-patch|-s] [--raw] [--minimal]\n\ninit - Create an empty Git repository or reinitialize an existing one.\n  Usage: git init [--quiet|-q] [--bare]\n```\n\nThe following methods are available:\n\n__`HelpManager SetProgramName(string name)`__\n\nSets the name of your program. This will most likely be the name of your program's executable file. For example, if the file is `git.exe`, you'd want to set your program name as `git`. If set, the name is displayed in the help menu.\n\n__`HelpManager SetTriggers(params string[] triggers)`__\n\nSets the triggers that will display the help menu. \n\nThe help menu displays if the very first argument matches one of the triggers. By default, the following triggers are set:\n\n`\"--help\", \"-h\", \"/help\"`\n\nYou can replace the default triggers by passing in a comma-separated list of strings:\n\n```\nbuilder.Help.SetTriggers(\"--?\", \"/h\");\n```\n\n__`HelpManager InvokeOnBuildFail(bool suppressException)`__\n\nBy default, the help menu is only displayed if explicitly called by the user's program argument. This method will configure the builder to automatically display the help on _any_ exceptions thrown.\n\nThe `suppressException` parameter allows you to configure whether exceptions thrown during the build process are suppressed. If true, will only display help text while suppressing the exception from bubbling to the client.\n\nAn example of when you'd select one over the other would be when you're creating a program/tool for internal use versus one for public release.\n\nIf the tool is being used internally, then it probably makes sense not to suppress any exceptions such that your app code can handle and deal with it.\n\nHowever, if it's something being released publically, it's generally not good practice to have exceptions and details such as stack traces shown to the clients. You can switch things up during development anyways, then suppress them for releases.\n\n__`HelpManager OnTrigger(Action customCallback)`__\n\nIf you need something other than the default help menu, you can set your own callback that is invoked instead. The callback is simple an `Action`, so you'll need to handle everything including the displaying of help text.\n\n```\nbuilder.Help.OnTrigger(() =\u003e {\n    Console.WriteLine(\"my custom help menu.\");\n});\n```\n\nThe help manager's `ToString` method has been overridden to return the help text (especially useful during development if you're building out your own custom help text).\n\n---\n\n### Version\n\nUsers should be able to quickly note the version of the program. The setup is similar to that of the help menu, and default behavior is provided as well.\n\nThe version is configured on the `VersionManager` object, found as the property `Version` on the `RunInfoBuilder` class. \n\nThe following methods are available:\n\n__`VersionManager Set(string version)`__\n\nSet the version string that is displayed. The default is `\"1.0.0\"`.\n\n__`VersionManager SetTriggers(params string[] triggers)`__\n\nSets the triggers that will display the version. \n\nThe version displays if the very first argument matches one of the triggers. By default, the following triggers are set:\n\n`\"--version\", \"-v\", \"/version\"`\n\nYou can replace the default triggers by passing in a comma-separated list of strings:\n\n```\nbuilder.Version.SetTriggers(\"--v\", \"/v\");\n```\n\n---\n\n### Gotchas and Limitations\n\nThis section will go over any known limitations or gotchas that you should be aware of when using this library.\n\n#### A Limitation of the Processing Flow (when dealing with strings)\n\nLets imagine a `Command` that expects a single `Argument` mapped to the `string` property `Message`. You also define some `Options`:\n\n```\nArguments =\n{\n    new PropertyArgument\u003cSendRequestRunInfo, string\u003e\n    {\n        Property = ri =\u003e ri.Message\n    }\n},\nOptions =\n{\n    // .. some options defined here ..\n}\n```\n\nWhat happens when a user forgets to include a value for the `Argument` and passes these program arguments:\n\n```\n[ \"sendhttp\", \"--some-option\" ]\n```\n\nWell, the builder has no way to know whether a given program argument is valid for the expected string `Argument`. So it would take the `\"--some-option\"` value and bind it to the `RunInfo`s `Message` property.\n\nWhat if the `Argument` was instead mapped to an `int` property? In this case, the build would throw an exception because the string `\"--some-option\"` is not parseable into an `int` type.\n\nThe workaround or solution to this issue is to simply be aware of this scenario and design your `Command` to avoid it.","funding_links":[],"categories":["CLI"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frushfive%2FRunInfoBuilder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frushfive%2FRunInfoBuilder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frushfive%2FRunInfoBuilder/lists"}