Cysharp / ConsoleAppFramework

Zero Dependency, Zero Overhead, Zero Reflection, Zero Allocation, AOT Safe CLI Framework powered by C# Source Generator.
MIT License
1.54k stars 88 forks source link

Support for parameter groups (eg. like in 'CommandLineParser') #132

Open Sergio0694 opened 1 week ago

Sergio0694 commented 1 week ago

Hey there! I'm trying to migrate this CLI tool from CommandLineParser to this one (so I can also get proper AOT support), and I stumbled upon a small issue: I can't see a way to define parameter groups with this library. The idea of a parameter group is to have an arbitrary set of parameters for a command (which can be optional), but with parsing requiring at least one of them being set. For instance, I use this to allow users to pass either the source code or a path to a file (containing source code) when invoking my CLI tool to execute that code for them. I think it would be nice to also support this functionality here.

General idea

I could see this implemented in two possible ways.

Option 1

Extend [Argument] to also have a way to specify a group:

 namespace ConsoleAppFramework;

 [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
 internal sealed class ArgumentAttribute : Attribute
 {
+    public string? Group { get; init; }
 }

Option 2

Generate a new [Group] attribute:

namespace ConsoleAppFramework;

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class GroupAttribute(string name) : Attribute
{
    public string Name { get; } = name;
}

I think the first approach seems simpler and likely better integrated.

Use case example

static void Run(
    [Argument(Group = "source")] string? source = null,
    [Argument(Group = "source")] string? sourceFilePath = null,
    ...)
{
    // At least one between 'source' and 'sourceFilePath' is guaranteed to be set here
}

Congrats again for this library, looks pretty cool and I love the idea! 😄

neuecc commented 1 week ago

Thank you for trying it out!

I understand the functionality you're requesting. I can see the usefulness when thinking of it as a Discriminated Union for arguments.

Since Argument are a feature related to order, it seems like overlapping with Group would break the meaning. So, considering the proposal, I think Option 2 is reasonable. However, just calling it "Group" doesn't seem to express well whether it's one or the other, or at least one. In the example, sourceOrSourceFilePath seems appropriate, and it doesn't seem good to be able to set both. In CommandLineParser, it was expressed as being able to set prefix or suffix or both. I don't think calling Help would usually be well understood, and it seems difficult to adopt the current specification as is.

The current workarounds are to have them create two separate commands, or to make both optional and check for errors yourself at runtime.