They keep coming... Let's have a look at PowerArgs.
With its NuGet package and sane, useful documentation both in the README and in the WIKI.
Conventional Attribution
PowerArgs relies heavily on attributes. Whereas some other frameworks use a single attribute with multiple properties to drive behavior, PowerArgs uses multiple attributes. It is true that some of those attributes have multiple properties themselves, so it is not just waste of keystrokes.
I have to admit that this multiple argument path and the usage of Arg- prefixes is not much of my liking; but, in the end, it is just a matter of taste, as the framework works very well and offers a lot of power and unique features.
The "conventional" part of the title comes from the way different commands are implemented: modelling them as methods that take an Arg-decorated object as argument.
Implementing our example
Implementing our example was pretty simple:
Some clarifications:
- decorate the argument object with
[ArgExceptionBehavior(ArgExceptionPolicy.StandardExceptionHandling)]
for the framework handling parsing exceptions such as missing mandatory arguments or type problems so that friendly-er messages are printed instead of daunting exceptions. - the
Action
property, mandatory and positioned as first argument is a requirement of the ActionFramework for multiple commands to be dispatched to the correct method. - the
Help
property, decorated withHelpHook
allows getting global and command-specific help messages.
Dispatching is also simple: translate one argument object into another (redundant if we embraced the framework), instantiate and call the method.
Being attribute-driven, we would face the same challenges for localization of messages.
The Challenges
Mandatory Arguments
Mandatory arguments are not the default, but decorating a property with [ArgRequired]
enables easy reporting of missing mandatory arguments:
Extra, unmapped arguments will make an error appear, but that behavior is customizable.
Non-Textual arguments
Declaring the right type for the property works fine including flags (boolean properties).
Failures to do so will reported back to the user:
Default values are provided as objects, instead of strings.
Furthermore, there is support for custom argument types via custom revivers that turn on string/s into objects. Very nice to have.
Multi-arguments
Collection arguments are supported by declaring the type as such with no extra decoration required.
Again, there is no help hint in regards of what that separator character might be when displaying help to the user (as it happens with default values).
We could use [ArgExample]
on the top- level argument object to help the user out with collection semantics, but an out-of-the-box solution might be desirable.
Showing Help
Running the program with the -help argument provides a very nice and colorful auto-generated list of supported actions and arguments:
And using the -help argument with a action, drills down to the action-level documentation, equally colorful:
Furthermore, there seems to be support for generating usage documentation, not only for the command line user, but also in HTML. Nice, indeed, but not so well-documented... yet.
Command Dispatching
As mentioned previously, commands are executed (in its simplest form) by providing a method that takes the argument object and invoking Args.InvokeAction
.
We can further detach actions from the argument definition by using [ArgActionType]
.
There seems to be a way to tap into the action dispatching method, by using [ArgActionResolver]
, but I am not sure how to use it and could not find much documentation to help me. This could be a place to use an IOC container if we had the use for it.
Conclusion
PowerArgs surprised me. And it is a very positive surprise. I was put off at first by the usage of multiple, prefixed attributes, but as I worked with it, I found out that there is a lot of power in this framework. It can do everything to be done in the sample application and way more.
One thing I am really thrilled about is the support for tab completion, interactive console applications and secure parameters. I goes way beyond my sample, but it is a feature that can save a lot of time if the requirement arises. Heres is a simple image on how tab completion works with the simple addition of a [TabCompletion]
attribute on our argument object.
I will certainly keep an eye on this one in case I do not feel like going out the PS way of things.
Last Console Series
- The beginning
- GoCommando
- CommandLineParser
- PowerArgs (this)
- LBi.Cli.Arguments
- Command Line Utils
- CLAP
- Wrap-up