Coder Social home page Coder Social logo

clipr's People

Contributors

algorithmsarecool avatar genethomas avatar hirschmann avatar nemec avatar ppioli-cl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clipr's Issues

Integrate ParserConfig into IParseValidator

When building error messages during validation it may be helpful to refer to arguments by name in order to explain why parsing failed. Example:

Cannot use both -d/--delete and --move at the same time.

We already have the ability to refer to a strongly-typed name and value (e.g. nameof(opt.Delete) and opt.Delete), but the flags must be hardcoded. If we could pull them from the config, whenever it changes the validation message would automatically match.

Some notes:

  • Need the ability to access sub-config of verbs
  • Need to retrieve both the short and long names of a NamedArgument
  • Need to retrieve the meta name of a PositionalArgument
  • We might want to provide a facade over the complexity of IParserConfig and the IArguments and let users retrieve by name/namespace (e.g. config.GetArgumentFlags<Options>(opt => opt.Delete) == "-d/--delete"

Prompt for input when value missing from parameter

Provide the ability to prompt for input when a named parameter that expects a value doesn't see one. Specifically, this can be used to allow a password to be input into the application without it appearing in the console history. Additionally, allow the input to be "masked" so it doesn't show on the screen either.

$ myapp.exe --user nemec --password SomePass12
Logged in!

$ myapp.exe --user nemec --password
Please type a password:
Logged in!

List values should support more delimiters

List values, used with the ParseAction.Append, should support more delimiters. Right now, only whitespace works. I'd like to suggest that commas and semicolons are valid delimiters as well.

--listOfArguments a b c d
--listOfArguments a,b,c,d
--listOfArguments a;b;c;d
--listOfArguments a;b c,d

All four examples should add "a", "b", "c" and "d" to the backing list-property.

Note: If the argument is surrounded with quotation marks, it should not be treated as a delimited value.

--listOfArguments a;"b;c",d // Should be "a", "b;c" and "d".
--listOfArguments "a b c d" // Should be a single value.
--listOfArguments a,'b c';d // Should be "a", "b c" and "d".

It would be good if both single- and double quotes are supported, but I think it is a reasonable requirement to not allow mixed quotation marks, e.g. "a b'.

Ability to reverse a boolean option.

I have a program that I have hardcoded some options into, including a couple of Boolean ones. I would like to be able to prompt the user for additional options, including the ability for them to undo options that were set by default. I had hoped to be able to just append user input to the string I hardcoded prior to calling CLIPR. To do this, I would need them to be able to specify options such as:

--whatIf false
or

--whatIf-

...in order to negate the "--whatIf" I had already included in the string. Is there a way to do this? Is this something that has come up before?

Help generation via Resources

As the title says, allow help data to be configured by resx files. It doesn't make sense to configure option names themselves from the resx files though.

Permissions system

In multitenant systems (like a chatroom) it's possible you could build one command line system and want to restrict access to certain parameters/verbs based on users or roles. With the work in 2.x to make each parser resuable, it may be possible to add a permissions system around each call to parse.

Improve type conversion message

Presently when a --arg value can not be converted, e.g. "2x" can not be converted to an int the message does not show the user which switch the error related to, e.g:

Value "2x" cannot be converted to the required type System.Int32. 

It would be better for users to be shown which of the values they typed in is wrong, e.g:

Argument --size value "2x" cannot be converted to the required type System.Int32.

Determine order of NamedArguments

Unless I misunderstood the differences between PositionalArguments and NamedArguments, you can currently not have a NamedArgument and determine its position in the list of command line arguments.

This would be useful as you could allow different commands to be executed in a certain order without being restricted by the Verbs system (e.g. prog.exe --load-config default.cfg --open-file test.txt).
Similar use case is shown in FFMPEG's argument system, where you refer to input media files by their order in the argument list, or certain flags only apply to the previous input.

Although untested, this may be worked around by implementing setters of the argument's properties which then increment a counter for determining the order. In that case, the parsing order will have to be clearly defined.

Thanks for your hard work,
RedMser

Scale width of help screen to the width of its terminal

Right now the line width is configured to 80 characters, but users may grow or shrink their terminal as needed. clipr should automatically adapt to that setting, falling back to 80 characters only if a terminal is not available.

When parsing fails and user added a verb (that exists), display help for that verb rather than the general help.

Additionally, allow devs to tell whether or not a verb was invoked if they wish to display the error message manually. Example:

var parser = new CliParser<Options>();
try
{
    parser.Parse(args, opt)
        .GetValueOrThrow();
}
catch (AggregateException e)
{
    var help = new clipr.Usage.AutomaticHelpGenerator<Options>();  // Would like to use Options or Verb here
    Console.Error.WriteLine(help.GetHelp(parser.BuildConfig()));
    return;
}

help generation example sorely missing

You talk about help generation all over the place but you never mention any related code. Not in the readme, and not in the sample.
Please include an actual example of a Main method that contains all the features - parsing and help output and error output.

License

Thanks for building this library. It's very well done and has been really helpful for me in building some command line tools as part of our product. The one issue that I have is related to the license. While it appears to be a very simple, permissive license, management types tend to be more comfortable when a library or tool like this uses a standard, well-known license (MIT, Apache, etc.). :-)

Is this license copied from one of the more well-known OSS licenses, and if so can it be changed to reflect that?

AutomaticHelpGenerator fails with NullPointer when [NamedArgument] has no Description

class NoDescription
{
    [NamedArgument("name")]
    public string Name = null;
}

class Help: AutomaticHelpGenerator<NoDescription>
{
    public Help()
    {
        ShortName = null;
        LongName = "help";
    }
}

    [TestMethod]
    [ExpectedException(typeof(ParserExit))]
    public void Help_WithNoDescription_NoNullPointer()
    {

        var args = "--help".Split();
        var opt = new NoDescription();
        var parser = new CliParser<NoDescription>(
            opt,
            ParserOptions.None,
            new Help());
        parser.Parse(args);
    }

pull request to follow...

Support terminal auto-complete

Add support for auto-complete in the major terminal emulators (Powershell, Bash). May have to be a separate tool integrated into the build process since the completion scripts are often separate files.

https://blog.jcoglan.com/2013/02/12/tab-completion-for-your-command-line-apps/
http://stackoverflow.com/questions/25823910/pscmdlet-dynamic-auto-complete-a-parameter-like-get-process
http://stackoverflow.com/questions/33132028/powershell-binary-module-dynamic-tab-completion-for-cmdlet-parameter-values

Multiple positional arguments not working for negative numbers

The current first example:

        var opt = CliParser.Parse<Options>("-vvv output.txt 1 2 -1 7".Split());

fails with the message: "Additional information: Extra positional arguments found: -1 7"

    [PositionalArgument(1, MetaVar = "N",
        NumArgs = 1,
        Constraint = NumArgsConstraint.AtLeast,
        Description = "Numbers to sum.")]
    public List<int> Numbers { get; set; }

"-1" is treated as a new argument. I'm not inputting negative numbers as positionals, so I'll move on. But I thought I would point out this interesting problem.

David

Partial / Sub-Parser

I'd like to build a command line application where all the arguments aren't necessarily known by the main parser. The idea being to allow for extensions which may define their own arguments. Something akin to argparse's subparser functionality.

One approach would be a parser option to ignore unknown arguments. Parse everything per usual, but when the destination object doesn't have an argument defined just ignore it instead of throwing an error. Then I just pass args to the extension and it can do the same to pull it's own arguments.

Optional Variable Argument Count?

Hello Dan,

I've just tried your library and I like it, simple and elegant.

I have a special situation:
I have some executable with various options. I save a shortcut on desktop pointing to the executable and add the desired named arguments at the end of the target field in the shortcut definition. Now if I double click the shortcut it will properly execute with the specified named arguments.

I want to provide the user's the ability to drag and drop files on the shortcut. It works with a positional argument, but the NumArgs must be at least 1, so it's not 'optional'. I tried setting NumArgs=0, but understandably it throws an exception. What would you suggest to work around this?

 [PositionalArgument(1, Constraint = NumArgsConstraint.AtLeast, NumArgs = 1)]
    public List<string> Files { get; set; }

Thanks!

TB

Warn the end user when they enter potentially dangerous option values

There may be some scenarios where you want to warn the end user if they enter a dangerous value that they may not have indended, the canonical example being rm -rf / bin/someprog and putting an extra space in there.

Today the rm program uses an extra parameter (--no-preserve-root) to allow that value, but it also terminates the program immediately with an error. This feature will offer devs the option to choose "warn", "error", or custom behavior. To get the behavior used by rm today, you would have to implement your own post-parse validation.

Allow argument value for boolean flags.

Allow -c true or -c false to set the flag as true or false, respectively.
It also integrates well with scripts, who can simply feed a boolean value to the argument rather than conditionally include the flag.

Unintuitive mututally exclusive groups

The way the mutually exclusive groups are made is rather unintuitive and, I must say, inconvenient.

If I have mutually exclusive groups of arguments, the amount of groups I would need to create would grow exponentially.

Let's say, I have my app.exe, which either loads its config from a supplied file path or gets all its arguments from command line. As I understand, currently I would need to write it like this:

[ApplicationInfo(Name = "app")]
class CLIOptions
{
    [MutuallyExclusiveGroup("file1")]
    [NamedArgument('a', "ServerAddress", Required = true)]
    public string ServerAddress { get; set; }

    [MutuallyExclusiveGroup("file2")]
    [NamedArgument('u', "Username", Required = true)]
    public string Username { get; set; }

    [MutuallyExclusiveGroup("file3")]
    [PromptIfValueMissing(MaskInput = true)]
    [NamedArgument('p', "Password", Required = true)]
    public string Password { get; set; }

    [MutuallyExclusiveGroup("file1")]
    [MutuallyExclusiveGroup("file2")]
    [MutuallyExclusiveGroup("file3")]
    [NamedArgument('f', "File", Constraint = NumArgsConstraint.Optional, Const = "settings.json", Required = true)]
    public string SettingsFile { get; set; }
}

Actually, even this wouldn't work, I still didn't figure out how to do it properly.

A much more sensible way would be to have mutually exclusive groups of arguments (and some way of letting the parser know which groups are mutually exclusive):

[ApplicationInfo(Name = "app")]
class CLIOptions
{
    [MutuallyExclusiveGroup("cli")]
    [NamedArgument('a', "ServerAddress", Required = true)]
    public string ServerAddress { get; set; }

    [MutuallyExclusiveGroup("cli")]
    [NamedArgument('u', "Username", Required = true)]
    public string Username { get; set; }

    [MutuallyExclusiveGroup("cli")]
    [PromptIfValueMissing(MaskInput = true)]
    [NamedArgument('p', "Password", Required = true)]
    public string Password { get; set; }

    [MutuallyExclusiveGroup("file")]
    [NamedArgument('f', "File", Constraint = NumArgsConstraint.Optional, Const = "settings.json", Required = true)]
    public string SettingsFile { get; set; }
}

Parser would require that either options from group cli or from group file would be present.

StaticEnumeration does not combine with list values

Using Static Enumerations work fine for distinct values, but they don't work with list values.

Argument -c value "Red" cannot be converted to the required type System.Collections.Generic.IList1[StaticEnumerationList.ColorEnum].`

I would like to be able to do something like this:

internal class Program
{
	private static void Main(string[] args)
	{
		var options = CliParser.StrictParse<Options>(args);
		foreach (var color in options.Colors)
			Console.WriteLine("Color: {0}", color);

		Console.ReadLine();
	}
}


internal class Options
{
	[NamedArgument('c', "colors", Action = ParseAction.Append, Constraint = NumArgsConstraint.AtLeast, Const = 1)]
	public IList<ColorEnum> Colors { get; set; }
}


public enum Color
{
	Red,
	Green,
	Blue
}


[StaticEnumeration]
internal abstract class ColorEnum
{
	public static readonly ColorEnum Red = new EnumValue(Color.Red);


	public class EnumValue : ColorEnum
	{
		public EnumValue(Color value)
		{
			Value = value;
		}

		public Color Value { get; private set; }
	}
}

"-- file --another" fails with [PositionalArgument(1, NumArgs = 1, Constraint = NumArgsConstraint.AtLeast)]

When there is a [PositionalArgument] with NumArgsConstraint.AtLeast -- is ignored when an argument that looks like a switch is encountered.

Test case:

    internal class PositionalMultipleValuesDelimiter
    {
        [PositionalArgument(1, NumArgs = 1, Constraint = NumArgsConstraint.AtLeast)]
        public List<string> Names { get; set; }
    }

    [TestMethod]
    public void PositionalArguments_WithPositionalDelimiter_ParsesCorrectly()
    {
        var expectedNames = new List<string> {"Nancy", "--Rick", "Tim"};
        var opt = CliParser.Parse<PositionalMultipleValuesDelimiter>(
            "-- Nancy --Rick Tim".Split());
        CollectionAssert.AreEqual(expectedNames, opt.Names);
    }

pull to follow...

Throws IndexOutOfRangeException on " " (one space) arg

internal class NullUsageAndVersion
{
    [PositionalArgument(0)]
    public string Value { get; set; }
}

[TestMethod]
public void ParseArgument_Space()
{
    var args = CliParser.Parse<NullUsageAndVersion>(new[] { " " });
    Assert.AreEqual(" ", args.Value);
}

Solution: remove ParsingContext.cs, line 81: arg = arg.Trim();
Why trim arguments? Maybe user want argument to start or end with spaces.

Support optional value when named argument is provided.

When a property has a reference or nullable type, it should be possible for the user to specify that the value is optional (zero or one arguments) without turning that property into a list.

This way you could have an option that enables a server on a given port:

class Options
{
    [NamedArgument('s', "server", Constraint=NumArgsConstraint.Optional)]
    public int Server { get; set; }
}

--server  // default port
--server 1234  // set port

With a constraint like that, we should verify that NumArgs wasn't explicitly set too.

Localize the AutomaticHelpGenerator UI into _your_ language

The UI can be localized into additional languages, but I need help in translating! If you're fluent in another language, please consider adding a translation. Translations are provided by the resx format and to add a new language, simply make a copy of Resources.resx and rename it according to the language's two-letter ISO 639-1 code.

For example, a Spanish translation would be named Resources.es.resx. If your country or region has a special way of translating a piece of text, add the ISO two-letter country code (in uppercase) as well: Resources.es-MX.resx

Visual Studio provides a convenient UI for editing resource files, but the XML itself can be modified on any platform.

Thank you!

Note: currently translated into three non-English languages, however as this project evolves there may be more English UI items added, which makes the translations incomplete/out of date. If you see anything missing, even in a language that has already been added, please feel free to make some updates.

  • Spanish (es): #42
  • German (de): #44
  • Portuguese (pt-PT): #47

Setting the Required attribute to true removes space between long option and meta var

Consider the following:

using System;
using clipr;
using clipr.Core;

internal class Options
{
    [NamedArgument('a', "optiona", Required = true, Description = "The A option.")]
    public string Optiona { get; set; }

    [NamedArgument('b', "optionb", Required = false, Description = "The B option.")]
    public string Optionb { get; set; }
}

internal class Program
{
    private static void Main(string[] args)
    {
        Options options = new Options();
        CliParser<Options> cliParser = new CliParser<Options>(options, ParserOptions.CaseInsensitive);

        try
        {
            cliParser.Parse(args);
        }
        catch (ParserExit)
        {
            return;
        }
        catch (ParseException e)
        {
            Console.WriteLine(e.Message);

            return;
        }
    }
}

Produces the following help output:

Usage: CliprRepro [ -h|--help ] [ --version ] -a|--optionaA [ -b|--optionb B ]
Required Arguments:
 -a, --optiona  The A option.

Optional Arguments:
 -b, --optionb  The B option.
 -h, --help     Display this help document.
 --version      Displays the version of the current executable.

Notice -a|--optionaA versus -b|--optionb B.

Handling of multiple optional named arguments

Hi,
I have two named arguments with attributes defined as

     [NamedArgument('a', "ArgumentA",
            Constraint = NumArgsConstraint.Optional,
            Action = ParseAction.Store,            
            Const = "MyConstValueA",
            Description = "Argument A")]
        public string A{ get; set; }

        [NamedArgument('b', "ArgumentB",
            Constraint = NumArgsConstraint.Optional,
            Action = ParseAction.Store,
            Const = "MyConstValueB
            Description = "Argument B")]
       public string B{ get; set; }

Command line is: MyApplication.exe -a -b

When leaving both arguments empty -a is reading -b as its value.
After Parse: A = -b and B = MyConstValueB

Expected: A = MyConstValueA and B = MyConstValueB

Is this functionality intended or how can it be defined to work as expected

Thanks,

Uwe

Start page sample

The 'quick but powerful example of the objects this library enables you to build' on the project start page quits with a ParseException stating that 'extra positional arguments are found'...

That's using NuGet package. Didn't try building from code.

Add fluent interface for constructing a parser configuration.

Instead of configuring the parser via attributes, it would be useful to be able to programmatically build the parser with a fluent interface. This will allow options to be added and removed dynamically and provide a code (instead of configuration) based interface for parsing.

There is some work already started on this (see clipr.Fluent namespace) but it's still in alpha stage.

Make CliParser class reusable

I've done work on the backend to separate parsing into two discrete steps: building a configuration and the act of parsing. There are some situations (such as a chat bot) where it would make sense to parse multiple inputs with the same config, but currently the 'object' is passed in the CliParser constructor, meaning each parser can only write to one object!

MissingManifestResourceException is thrown in .NET Core POC project

I am building a demo project on Windows and get a runtime exception that I am not sure how to fix. Could you please take a look and guide me out?

Source

using System;
using clipr;

namespace CliprTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var parsed = CliParser.StrictParse<Options>(args);

            Console.WriteLine("Ran without params first");
        }
    }

    [ApplicationInfo(Description = "Test")]
    public class Options
    {
    }
}

Ouput

soloy@DESKTOP-MRND0KT MINGW64 /c/GitHub/CliprTest/src/CliprTest
$ dotnet build && dotnet run bin/Debug/netcoreapp1.0/CliprTest.dll
Project CliprTest (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling CliprTest for .NETCoreApp,Version=v1.0

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:02.2712506


Project CliprTest (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.

Unhandled Exception: System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure "clipr.Properties.Resources.resources" was correctly embedded or linked into assembly "clipr.NetCore" at compile time, or that all the satellite assemblies required are loadable and fully signed.
   at System.Resources.ManifestBasedResourceGroveler.HandleResourceStreamMissing(String fileName)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(CultureInfo culture, Dictionary`2 localResourceSets, Boolean tryParents, Boolean createIfNotExists, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
   at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)
   at clipr.Usage.AutomaticHelpGenerator`1.get_UsageTitle()
   at clipr.Usage.AutomaticHelpGenerator`1.GetUsage(IParserConfig config)
   at clipr.CliParser`1.StrictParse(String[] args)
   at clipr.CliParser.StrictParse[TS](String[] args)
   at CliprTest.Program.Main(String[] args)

Support arguments that start with a dash

There are cases where an argument may need to begin with a dash, such as passing arguments which will be passed on to another command. Ex:

MyProgram.exe --forwardArgs "--flagToForward --otherFlag --etc"

Since adding the check for "ParamIsArgument()" to clipr the string of arguments to forward on will be assumed to be another option flag and not be recognized as an argument to the --forwardArgs option.

Support fields

Fields are faster to type than properties and may be initialised with default values in the same place.

 class Options
 {
            [NamedArgument('n', "name")]
            public string Name = "default name";

            [NamedArgument('v', "verbose", Action = ParseAction.StoreTrue, Description = "Verbose output.")]
            public bool Verbose = false;
 }

 static void Main(string[] args)
 {
            var opt = new Options();
            var parser = new CliParser<Options>(opt);
            parser.StrictParse(args);
 }

Enable help messages for Verbs

At the moment, Verb data is not included in the default help generator nor can "help" be invoked for an individual verb.

Required arguments are listed as optional in help text

Arguments that are required are listed under the "Optional Arguments" listing in the help text.

[ApplicationInfo(Description = "This is a set of options.")]
public class Options
{
	[NamedArgument('c', "confirm", Action = ParseAction.StoreTrue, Required = true,
		 Description = "Confirms that the action is intended.")]
	public bool Confirmed { get; set; }
}

Executing without arguments:

MyApplication.exe
Usage: MyApplication [ -h|--help ] [ --version ] [ -c|--confirm ]

Required named argument(s) "Confirmed" were not provided.

Executing with -h flag:

MyApplication.exe -h                                                                          
Usage: MyApplication [ -h|--help ] [ --version ] [ -c|--confirm ]


 This is a set of options.

Optional Arguments:                                                                                                     
 -c, --confirm  Confirms that the action is intended.                                                                   
 -h, --help     Display this help document.                                                                             
 --version      Displays the version of the current executable.                                                         

Required arguments ("confirm" in this example) should be listed under a "Required Arguments" heading, and also preferably indicated in the "usage"-line.

Make variable length arguments more intuitive.

Currently, variable length args (e.g. List<string>) will attempt to store the argument value as a string (instead of an enumerable) which fails to convert unless you also explicitly set the Constraint attribute property.

When clipr detects a variable length type (say, an IEnumerable<>) it should automatically default the Constraint to AtLeast, the expected default value.

Evaluate Async Support

I need to evaluate whether the new handlers can support async methods/async Main. It would be nice to also check whether it can support async methods for verb parsing, post-parse handlers, and other places where users/devs can hook in to clipr.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.