little-sharps / args Goto Github PK
View Code? Open in Web Editor NEWCommand line argument parser for .NET
Command line argument parser for .NET
Hi, consider the following code:
[ArgsModel(SwitchDelimiter = "-")]
public class CommandOption
{
public Uri OutputFilePath { get; set; }
public string PatientId { get; set; }
public bool IsDatabase { get; set; }
public bool IsXml { get; set; }
}
and the following command line arguments:
-o C:\Temp\someFile.htm -p 1 -d
now -d
represents "IsDatabase" while -x
(not used here) represents "IsXml". Is there an annotation that I can use to say that
-d = IsDatabase
-x = IsXml
I cannot change the fact that my switches are -d
and -x
.
TIA.
Resolved with commit 3dd3525. See unit tests for details.
I can't kick off my command line utility from bash because '/D' is expanded to a directory by the shell. I'd like to be able to use either slash or dash to mark a switch so that I can use the utility from a broader set of environments.
For large numbers of arguments, the current help get a bit noisy:
<command> [/Id] [/PersonId] [/CustomId] [/FirstName] [/LastName] [/MiddleName]
[/MobilePhone] [/OfficeExtension] [/HomePhone] [/FaxPhone]
[/ContactEmail] [/BusinessPhone] [/Suffix] [/Title] [/Prefix]
[/Password]
[/Id|/I] A GUID used to identify this person.
[/PersonId|/Pe] [/CustomId|/Cu] [/FirstName|/Fi] [/LastName|/L] [/MiddleName|/Mi] [/MobilePhone|/Mo] [/Of
ficeExtension|/O] [/HomePhone|/H] [/FaxPhone|/Fa] [/ContactEmail|/Co] [/BusinessPhone|/B] [/Suffix|/S] [/Title|
/T] [/Prefix|/Pr] [/Password|/Pa]
All the params are listed multiple times. This would be a tighter representation:
<command> [/Id /I] [/PersonId /Pe] [/CustomId /Cu] [/FirstName /Fi] [/LastName /L] [/MiddleName /Mi]
[/MobilePhone /Mo] [/OfficeExtension /O] [/HomePhone /H] [/FaxPhone /Fa]
[/ContactEmail /Co] [/BusinessPhone /B] [/Suffix /S] [/Title /T] [/Prefix /Pr]
[/Password /Pa]
[/Id|/I] A GUID used to identify this person.
[/Title /T] Some description for Title.
public void ActionMethod(Options opt) {
...
}
public class Options
{
private string Thing { get; set; } //And its the only property
}
Thing
is private and is the only property. Args gets pretty upset when asked to generate help:
Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
at System.Linq.Enumerable.Max(IEnumerable`1 source)
at Args.Help.Formatters.ConsoleHelpFormatter.WriteJustifiedOutput(IDictionary`2 lines, Int32 padding)
at Args.Help.Formatters.ConsoleHelpFormatter.WriteArgumentDescriptions(ModelHelp modelHelp)
at Args.Help.Formatters.ConsoleHelpFormatter.WriteHelp(ModelHelp modelHelp, TextWriter writer)
But really, I'd like a nice error when my arguments type has no public properties.
if you have a switch (say /f for "force") and that switch is the only switch provided or is the last switch (and /f doesn't also exist prior to its usage), then the switch is ignored.
I believe this has to do with the extra .Take(1)
at the end of ModelBindingDefinition.BindModel(...)
's
//find a suitable switch, then skip 1 (to skip the switch itself)
var argument = switchedArguments.SkipWhile(a => a.Length <= SwitchDelimiter.Length || member.Value.CanHandleSwitch(a.Substring(SwitchDelimiter.Length)) == false).Skip(1);
I think, even in situations where you want the switch to be /s value
, the Skip(1)
is not needed as the followup call to argument.TakeWhile(...)
will skip anything with a delimiter anyhow.
I looked through your unit tests to see if you ever tested a solo "/f"-style switch, and I didn't see a test for that case.
Handling Array or IEnumerable Model Properties would be a great addition.
Currently, I'm using a typeconverter to convert comma seperated values to an array of strings (file paths), but if the user drags many files onto the executable, each file path is a separate arg, so this doesn't work.
Is there a way to handle this?
You should use a string format here:
args/Args/ModelBindingDefinition.cs
Line 94 in 42da229
Trying to run a silent install of java through Bootstrapper which uses this package:
bootstrapper.exe /get role-setup/jre-7u9-windows-x64.exe /lr $lr(tempInstallationDir) /sc $config(ConnectionString) /run $lr(tempInstallationDir)\jre-7u9-windows-x64.exe /args /s /L "%RoleRoot%\approot\java-install.log" /block
http://java.com/en/download/help/silent_install.xml
But can't seem to escape the args value of (/s /L "%RoleRoot%\approot\java-install.log")
Any suggestions?
From System.ComponentModel.DataAnnotations
If a property is marked [Required]
then the help should reflect that.
Also a nice exception would be cool...
Read help for command object and its properties/fields from XML document produced based on the XML code comments.
The ArgsTypeResolver uses
return Activator.CreateInstance(serviceType);
to create a new instance. This fails on non-public parameterless constructors. I would propose to also include non-public constructors (as the DataContractSerializer and other de-serializing libs do).
So the call would change into:
Activator.CreateInstance(serviceType, true)
Thoughts?
When parsing a configuration with
public class GenerateConfiguration {
public string Method { get; set; }
public string File { get; set; }
}
GenerateConfiguration command = Args.Configuration.Configure().CreateAndBind(args);
the properties will not be set if the class is in a different namespace than the caller.
Can you add more examples how to use your API. E.g. Help section?
Calling CreateAndBind simply throws exceptions on wrong params.
In the ArgsTypeConverter
, the ConvertFrom()
method makes a recursive call instead of calling Convert()
on the IArgsTypeConverter
instance it has. There is no "out" path for the recursion so it eventually errors at with a StackOverflowException
being thrown by the runtime.
When the type of the arguments class has a superclass this happens:
Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationExceptio
n: memberInfo must be from type TYPENAME
at Args.MemberBindingDefinition`1..ctor(MemberInfo memberInfo, ModelBindingDefinition`1 parent)
at Args.ModelBindingDefinition`1.GetOrCreateMemberBindingDefinition(MemberInfo member)
at Args.ConventionBasedModelDefinitionInitializer.Initialize[TModel](IModelBindingDefinition`1 init)
at Args.Configuration.ConfigureInternal[TModel](IModelBindingDefinitionInitializer initializer)
at Args.Configuration.Args.IArgsConfiguration.Configure[TModel]()
at Args.Configuration.Configure[TModel]()
It should be possible to use a type with a superclass.
I've been able to identify an issue, but it's boundaries still elude me a bit. Essentially the value of an arg can affect another arg. Here's a passing and a failing test:
public class SimpleModelForWeirdnessTest {
public Guid? Id { get; set; }
public string Name { get; set; }
}
[Test]
public void TestForParsingWierdnessPasses()
{
var nameValue = "foo";
var idValue = "c304cffd-f331-49a1-a4ba-0706f869de11";
var modelBindingDefinition = Configuration.Configure<SimpleModelForWeirdnessTest>();
var model = modelBindingDefinition.CreateAndBind(new string[] {"/N", nameValue, "/I", idValue});
model.Name.Should().Be(nameValue);
model.Id.ToString().Should().Be(idValue);
}
[Test]
public void TestForParsingWierdnessFails() {
var nameValue = "vi";
var idValue = "c304cffd-f331-49a1-a4ba-0706f869de11";
var modelBindingDefinition = Configuration.Configure<SimpleModelForWeirdnessTest>();
var model = modelBindingDefinition.CreateAndBind(new string[] { "/N", nameValue, "/I", idValue });
model.Name.Should().Be(nameValue);
model.Id.ToString().Should().Be(idValue);
}
As you can see only the name value changes between the examples. At first I thought it was the presence of an 'I' that was problematic, but I have counterexamples. Then I thought it was the length, which also didn't pan out.
I will look into this further, but I thought @cbrianball might have some insight.
I have encountered this:
Cannot create instance of type Company1Test.Domain.ArgumentOption; no public default constructor found. To override this behavior, impelment System.IServiceProvider and set the ArgsTypeResolver.Current static property.
and I've created my own implementation of IServiceProvider
. Where do I now set the "ArgsTypeResolver.Current" property? How can I even set it because there are no setters on the "Current" property?
Could I ask that you please publish a package that rather targets .NET Standard rather than a specific framework. That way we can use it in both .NET Core and Full .NET
If we depend on you package in .NET Core you get the following error:
Package 'Args 1.1.2' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.2'. This package may not be fully compatible with your project.
HI, I've looked at the code and I can't quite figure out an easy way to change the Switch delimiter character. In the documentation, we're told that ArgsModelAttribute will do the trick but how will
var command = Configuration.Configure<CommandObject>().CreateAndBind(args)
take that in?
I've also seen another issue raised to that effect but even with that I was unable to figure out how to change the Switch Delimiter.
When generating the short forms of parameters: [/FirstName /F]
it'd be nice if, when multiple letters are needed, to take into account the PascalCased values.
FirstName : FN instead of FI
(Also, if more than 2 characters are needed, perhaps it should just give up on the short form altogether and just use the long form.)
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.