dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.37k stars 378 forks source link

Question/Discuss Builder API #824

Open BenjaminHolland opened 4 years ago

BenjaminHolland commented 4 years ago

I have some questions here:

jonsequitur commented 4 years ago

The entire API surface is subject to change if we get feedback that helps us improve it, so the builder APIs aren't final.

The builders are intended to be used directly but aren't required unless you want to alter the default behaviors. So, for example, the following are roughly equivalent:

new RootCommand().Invoke(args);

// versus

new CommandLineBuilder()
    .UseDefault()
    .Build()
    .Invoke(args);

UseDefaults is currently implemented as:

public static CommandLineBuilder UseDefaults(this CommandLineBuilder builder)
{
    return builder
           .UseVersionOption()
           .UseHelp()
           .UseParseDirective()
           .UseDebugDirective()
           .UseSuggestDirective()
           .RegisterWithDotnetSuggest()
           .UseTypoCorrections()
           .UseParseErrorReporting()
           .UseExceptionHandler()
           .CancelOnProcessTermination();
}

So for example if you're happy with those defaults, you don't need the builder.

The reason for this approach is because of the complexity of the CommandLineConfiguration class, which is effectively immutable, so its constructor is complicated:

     public CommandLineConfiguration(
            IReadOnlyCollection<Symbol> symbols,
            IReadOnlyCollection<char> argumentDelimiters = null,
            bool enablePosixBundling = true,
            bool enableDirectives = true,
            ValidationMessages validationMessages = null,
            ResponseFileHandling responseFileHandling = ResponseFileHandling.ParseArgsAsLineSeparated,
            IReadOnlyCollection<InvocationMiddleware> middlewarePipeline = null,
            Func<BindingContext, IHelpBuilder> helpBuilderFactory = null)
        {

We could do away with the immutability here by making all of these properties publicly settable, but this would add other complexities.

Regarding a fluent API, the entire API used to be both fluent and immutable, but many people found it harder to work with. It was particularly hard to work with when building custom app models (e.g. DragonFriut) on top of System.CommandLine. But there's been interest in a fluent API (#540) and one of our main goals is to make sure we're building a solid foundation for diverse approaches.