Spectre.Cli has been moved into the Spectre.Console library.
For information about how to migrate, see the migration guide.
An extremely opinionated command-line parser.
Home Page: https://spectresystems.github.io/spectre.cli/
Spectre.Cli has been moved into the Spectre.Console library.
For information about how to migrate, see the migration guide.
How can commands and arguments add programmatically to the CommandApp?
Now that we can change the behavior of a command at runtime by either passing data to it or doing something different based on the command name, being able to change the description of a command at runtime would be helpful too.
I propose a new WithDescription(string description)
fluent method to set a description override when registering commands. Anything provided through the method will override an existing attribute-based description on the command class.
If you’re okay with the change I’ll submit a PR.
One of the evil things I was able to do with the new command data passing is implement delegate-based commands using a single DelegateCommand<TSettings>
class and passing the delegate in as data. Is this something that would be cool in-the-box?
Usage would look like this:
public class QuoteCommandSettings : CommandSettings
{
public string Quote { get; set; }
}
public class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.AddCommand<QuoteCommandSettings>(
(context, settings) => Console.WriteLine(
$”Abe Lincoln once said {settings.Quote}”));
});
return app.Run(args);
}
}
Any interest in me pulling this in?
If you don't feel this is a good feature, feel free to close, but I just ran into this problem in an app and thought I'd suggest it.
Having an overload for AddBranch
or AddCommand
that supports specifying the settings type as a parameter would make it possible to add commands without access to the type parameter (e.g. dynamically added commands).
I'm not sure if this is possible given there'd be no way to know what settings type to pass to the IConfigurator
but maybe you can think of something I can't!
For example:
void AddBranch(string name, Type settingsType, Action<IConfigurator<>> action);
--
This is similar to what's available in the Microsoft.Extensions packages:
services.AddSingleton<ConfigLoader>();
// is equivalent to
services.AddSingleton(typeof(ConfigLoader), objectInstance);
What I want to do is register multiple commands using the same command type but pass different data to it when defining the commands. In other words, I want to be able to create a "dynamic" command at run time using the same base command.
The easiest way I can think to do this is add something like an object Data { get; }
to the CommandContext
and pass it through.
I imagine this might look something like:
public class FooCommandSettings : CommandSettings
{
public string Bar { get; set; }
}
public class FooCommand : AsyncCommand<FooCommandSettings>
{
public sealed override async Task<int> ExecuteAsync(
CommandContext context, FooCommandSettings settings)
{
Console.WriteLine(context.Data + settings.Bar);
}
}
public class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
// Set CommandContext.Data to "FIZZ"
config.AddCommand<FooCommand >("fizz", "FIZZ");
// Set CommandContext.Data to "FUZZ"
config.AddCommand<FooCommand >("fuzz", "FUZZ");
});
return app.Run(args);
}
}
Thoughts? Would you accept a PR?
Would be nice to be able to tell the command line parsing engine that an argument is dependent on another options existence (or non-existence).
public sealed class AddCustomerSettings
{
[CommandOption("--email <EMAIL>")]
public string Email { get; set; }
[CommandOption("--wantemail")]
[MustBeProvided(nameof(Email), "No email has been provided.")]
public bool WantsEmail { get; set; }
[CommandOption("--noemail")]
[MustNotBeProvided(nameof(Email), "Customer do not want email. Please remove email argument.")]
public string DoNotWantEmail { get; set; }
}
Currently arguments are sorted from implementation to base class, which imo is wrong.
Top level
USAGE:
myapp [OPTIONS] <COMMAND>
EXAMPLES:
myapp --help
myapp dog
OPTIONS:
-h, --help Prints help information
COMMANDS:
dog The dog command
cat The cat command
horse The horse command
giraffe The giraffe command
Command
USAGE:
myapp cat [LEGS] lion <TEETH> [OPTIONS]
EXAMPLES:
myapp cat 3 lion 32 -c 3
myapp cat lion 31 -c 1
ARGUMENTS:
<TEETH> The number of teeth the lion has
OPTIONS:
-h, --help Prints help information
-c <CHILDREN> The number of children the lion has
In the constructor for CommandAppBase
, the Resolver
property is set using the value from CommandAppSettings
parameter's Resolver
property.
However, this means that updating the CommandAppBase.Settings.Resolver
property (a perfectly valid syntactic operation) is ignored, since the value of the CommandAppBase.Resolver
property is only set in the constructor (and has no accessible set
).
Note that the same applies for the
CommandAppBase.Settings.Streams
property
Is this expected behaviour?
It would be convenient if the command line application could give suggestions to commands in case you spell it wrong.
Given the following configuration:
app.Configure(config =>
{
config.AddCommand<GitSettings>("git", animal =>
{
animal.AddCommand<PushCommand>("push");
animal.AddCommand<CloneCommand>("clone");
});
});
Would give the following output:
> git pusg
Unknown command 'pusg'.
Did you mean 'push'?
Using a setup like this sample, I would expect that settings would be handled as follows:
In the above example, that would mean that the Name argument would be included in the help and parsing for both MyCommand
/child
and MyOtherCommand
/other
Let me know if that's not clear.
Running a Spectre-enabled app without any arguments shows the help text and available commands. Likewise, running an individual command with the help switches set in SetHelpOption
shows the help for the command.
However, I would expect that using the same option (from SetHelpOption
) with the top-level executable should also display available commands etc, same as running without any arguments does.
That is, I would say that running:
MyConsoleApp
and
MyConsoleApp --help
...should give the same output.
Sample repo included here
Hi Patrik,
I was wondering if it would be possible to repeat a param that one can enter.
e.g. myapp.exe myaction --myparam = string1 --myparam = string2
Thanks,
Maurice
Would be nice if we could validate parameters.
public sealed class MyOption
{
[CommandOption("-n|--name")]
[Validator(typeof(NotEmptyValidator))]
public string Name { get; set; }
}
public sealed class NotEmptyValidator : IValidator<string>
{
public ValidationResult Validate(string name, string value)
{
if(string.IsNullOrWhiteSpace(value))
{
ValidationResult.Fail($"Parameter {name} can not be empty!");
}
return ValidationResult.Success();
}
}
A runtime check to ensure that the validator type is the same as the provided parameter type must be done.
Added new test case here: https://github.com/spectresystems/spectre.commandline/wiki/Test-cases#test-case-4
See repro at https://github.com/agc93/spectre-cli-sample/tree/upgrade/0.7.0
Introduce a "relaxed" mode where options that are unknown are ignored and added to the list of remaining arguments. The --
delimiter is not required in this mode for remaining arguments.
This is needed by Cake.
To make releasing new versions easier, let's remove the develop branch and only work on the master branch. We will then release to NuGet by tagging the master branch. We won't generate release notes anymore, but that's ok.
There is currently a lot of different kinds of configuration exceptions, and while we cover a lot of cases, they could be friendlier to the poor developer trying to get things to work.
For example, the following setting:
public sealed class InvalidSettings : CommandSettings
{
[CommandOption("-f|--g <BAR>")]
public string Foo { get; set; }
}
Currently gives the following exception:
Long option names must consist of more than one character.
It would be much nicer if we could pinpoint the problem a little more.
An error occured when parsing template for Program.InvalidSettings.Foo.
-f|--g <BAR>
^^^ Long option names must consist of more than one character.
I could write more examples, but hopefully this captures the gist of what I want.
It would also be nice if we extracted all exception logic to it's own helper class.
Should be possible to send --foo true
Will probably make the API clearer to call this AddBranch
instead.
We should support a default command which would be invoked if no command is provided by the user. This way the user can provide custom options on the application level.
using System.Threading.Tasks;
using Spectre.CommandLine;
namespace MyApp
{
public static class Program
{
public static async Task<int> Main(string[] args)
{
var app = new CommandApp<RootCommand>();
app.Configure(config =>
{
config.SetApplicationName("myapp");
config.AddCommand<RunCommand>("run");
});
return await app.RunAsync(args);
}
}
}
> ./foo.exe --magic
RootCommand: Magic!
From what I can tell, this code means that any argument represented as [arg-name]
will be required while any arg in the format <arg-name>
will be accepted but made optional.
This is the exact opposite of the usual convention for Unix-like environments. Is this something that would be changeable, or configurable?
I currently have some options with optional values. For example, turning on a web server: --serve
(with a default port) and turning on a server with optional port: --serve 1234
.
This can currently be represented by using two options (a flag + a value) like --serve --port 1234
. Doing so requires a little extra validation to make sure that they're provided as a pair (I.e., --port
is never specified without --serve
). Being able to combine them as a single option is a little cleaner.
One implementation idea is to use a new special value type named something like OptionalValue<T>
to indicate the option has an optional value and to convey whether it was unspecified, specified without a value, or specified with a value (and what the value was).
Add support for ObsoleteAttribute
when it comes to parameters for a command (options and arguments).
Arguments shown in the help view (--help
) should be sorted by position.
Just opening for discussion - any thoughts about renaming the AsyncCommand<T>.Execute(...)
virtual method to AsyncCommand<T>.ExecuteAsync(...)
? I know the Async
suffix is generally debatable and people have strong feelings so it's not a hill I'd die on. That said, I personally prefer the suffix - reasoning is summarized pretty well in this thread: https://github.com/dotnet/corefx/issues/31275#issuecomment-407181532
Although it works great for simple things, there are too many things that are not implemented or not supported in a good way.
Right now, we accept all arguments not specified in settings, and provide them in a ILookup<string, string>
collection. We should only allow unspecified arguments after a trailing --
.
Remaining arguments should be represented as ICollection<string>
instead of ILookup<string, string>
and making sense of remaining arguments is up to the command. This is because it might be desirable to pass the arguments to some other application.
There's so many problems related to consuming a netstandard2.0 library that uses the "new" value tuple from a net461 project, so let's remove it for now. If this problem is fixed in the future we might add it back again since it removes some boiler plate code.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.