Skip to content

Commit 8d89898

Browse files
authored
Merge branch 'master' into net40-net45
2 parents 4295f74 + 030eb8d commit 8d89898

12 files changed

+245
-128
lines changed

Diff for: .editorconfig

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#top-most EditorConfig for project
2+
root = true
3+
4+
[*]
5+
end_of_line = crlf
6+
insert_final_newline = true
7+
8+
[*.cs]
9+
indent_style = space
10+
indent_size = 4
11+
12+
[*.{xml,csproj,config}]
13+
indent_style = tab
14+
indent_size = 4
15+
16+
[*.json]
17+
indent_style = space
18+
indent_size = 2
19+
20+
[*.js]
21+
indent_style = space
22+
indent_size = 2
23+
24+
[*.yml]
25+
indent_style = space
26+
indent_size = 2

Diff for: CommandLine.sln

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
44
VisualStudioVersion = 15.0.27703.2042
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLine", "src\CommandLine\CommandLine.csproj", "{E1BD3C65-49C3-49E7-BABA-C60980CB3F20}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLine", "src\CommandLine\CommandLine.csproj", "{E1BD3C65-49C3-49E7-BABA-C60980CB3F20}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLine.Tests", "tests\CommandLine.Tests\CommandLine.Tests.csproj", "{0A15C4D2-B3E9-43AB-8155-1B39F7AC8A5E}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLine.Tests", "tests\CommandLine.Tests\CommandLine.Tests.csproj", "{0A15C4D2-B3E9-43AB-8155-1B39F7AC8A5E}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1361E8B1-D0E1-493E-B8C1-7380A7B7C472}"
911
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,6 +27,9 @@ Global
2527
GlobalSection(SolutionProperties) = preSolution
2628
HideSolutionNode = FALSE
2729
EndGlobalSection
30+
GlobalSection(NestedProjects) = preSolution
31+
{0A15C4D2-B3E9-43AB-8155-1B39F7AC8A5E} = {1361E8B1-D0E1-493E-B8C1-7380A7B7C472}
32+
EndGlobalSection
2833
GlobalSection(ExtensibilityGlobals) = postSolution
2934
SolutionGuid = {5B5A476C-82FB-49FB-B592-5224D9005186}
3035
EndGlobalSection

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ __This library provides _hassle free_ command line parsing with a constantly upd
2727
- Supports `--help`, `--version`, `version` and `help [verb]` by default.
2828
- Map to sequences (via `IEnumerable<T>` and similar) and scalar types, including Enums and `Nullable<T>`.
2929
- You can also map to every type with a constructor that accepts a string (like `System.Uri`).
30-
- Define [verb commands](https://github.com/commandlineparser/commandline/wiki#verbs) similar to `git commit -a`.
30+
- Define [verb commands](https://github.com/commandlineparser/commandline/wiki/Verbs) similar to `git commit -a`.
3131
- Unparsing support: `CommandLine.Parser.Default.FormatCommandLine<T>(T options)`.
3232
- CommandLineParser.FSharp package is F#-friendly with support for `option<'a>`, see [demo](https://github.com/commandlineparser/commandline/blob/master/demo/fsharp-demo.fsx). _NOTE: This is a separate NuGet package._
3333
- Most of features applies with a [CoC](http://en.wikipedia.org/wiki/Convention_over_configuration) philosophy.

Diff for: appveyor.yml

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#version should be only changed with RELEASE eminent, see RELEASE.md
2-
version: 2.3.{build}
2+
version: 2.4.{build}
3+
4+
image: Visual Studio 2017
35

46
clone_depth: 1
57
pull_requests:
@@ -31,18 +33,26 @@ after_test:
3133

3234
artifacts:
3335
- path: 'src/CommandLine/bin/Release/*.nupkg'
34-
name: NugetPackages
36+
name: NuGetPackages
3537

3638
on_failure:
3739
- cmd: |
3840
tree /f /a >files.lst
3941
appveyor PushArtifact .\files.lst -DeploymentName "Failed Build File Listing"
4042
4143
deploy:
44+
- provider: GitHub
45+
auth_token:
46+
secure: hVyVwHl0JiVq0VxXB4VMRWbUtrGclIzadfnWFcWCQBLvbgMLahLBnWlwGglT63pZ
47+
artifact: 'NuGetPackages'
48+
prerelease: false
49+
force_update: true #fsharp package runs as separate build job, so have to force_update to add fsharp.nuget added
50+
on:
51+
APPVEYOR_REPO_TAG: true
52+
4253
- provider: NuGet
4354
api_key:
44-
secure: +Zxb8M5W+UJV1yd9n8seu3PvH/hGNPEmgriGBnsSmtxjKPQAJ4+iL7tKAmfPHAuG
55+
secure: Ab4T/48EyIJhVrqkfKdUxmHUtseEVuXuyrGACxZ0KN35rb/BzABlBM2YjZojicvT
4556
artifact: 'NuGetPackages'
4657
on:
4758
APPVEYOR_REPO_TAG: true
48-

Diff for: src/CommandLine/CommandLine.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
<Authors>gsscoder;nemec;ericnewton76</Authors>
1515
<Title>Command Line Parser Library</Title>
1616
<Version Condition="'$(VersionSuffix)' != ''">$(VersionSuffix)</Version>
17-
<Version Condition="'$(VersionSuffix)' == ''">2.5.0-Dev</Version>
17+
<Version Condition="'$(VersionSuffix)' == ''">0.0.0</Version>
1818
<Description Condition="'$(BuildTarget)' != 'fsharp'">Terse syntax C# command line parser for .NET. For FSharp support see CommandLineParser.FSharp. The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks.</Description>
1919
<Description Condition="'$(BuildTarget)' == 'fsharp'">Terse syntax C# command line parser for .NET with F# support. The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks.</Description>
2020
<Copyright>Copyright (c) 2005 - 2018 Giacomo Stelluti Scala &amp; Contributors</Copyright>
2121
<PackageLicenseUrl>https://raw.githubusercontent.com/gsscoder/commandline/master/doc/LICENSE</PackageLicenseUrl>
22-
<PackageProjectUrl>https://github.com/gsscoder/commandline</PackageProjectUrl>
22+
<PackageProjectUrl>https://github.com/commandlineparser/commandline</PackageProjectUrl>
2323
<PackageIconUrl>https://raw.githubusercontent.com/commandlineparser/commandline/master/art/CommandLine20.png</PackageIconUrl>
2424
<PackageTags>command line;commandline;argument;option;parser;parsing;library;syntax;shell</PackageTags>
2525
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>

Diff for: src/CommandLine/Core/InstanceBuilder.cs

+73-36
Original file line numberDiff line numberDiff line change
@@ -82,44 +82,19 @@ public static ParserResult<T> Build<T>(
8282
var specPropsWithValue =
8383
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memorize();
8484

85-
var setPropertyErrors = new List<Error>();
85+
var setPropertyErrors = new List<Error>();
8686

87-
Func <T> buildMutable = () =>
87+
//build the instance, determining if the type is mutable or not.
88+
T instance;
89+
if(typeInfo.IsMutable() == true)
8890
{
89-
var mutable = factory.MapValueOrDefault(f => f(), Activator.CreateInstance<T>());
90-
setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail()));
91-
setPropertyErrors.AddRange(mutable.SetProperties(
92-
specPropsWithValue,
93-
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
94-
sp => sp.Specification.DefaultValue.FromJustOrFail()));
95-
setPropertyErrors.AddRange(mutable.SetProperties(
96-
specPropsWithValue,
97-
sp =>
98-
sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence
99-
&& sp.Specification.DefaultValue.MatchNothing(),
100-
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray()));
101-
return mutable;
102-
};
103-
104-
Func<T> buildImmutable = () =>
91+
instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors);
92+
}
93+
else
10594
{
106-
var ctor = typeInfo.GetTypeInfo().GetConstructor((from sp in specProps select sp.Property.PropertyType).ToArray());
107-
var values = (from prms in ctor.GetParameters()
108-
join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower() into spv
109-
from sp in spv.DefaultIfEmpty()
110-
select
111-
sp == null
112-
? specProps.First(s => String.Equals(s.Property.Name, prms.Name, StringComparison.CurrentCultureIgnoreCase))
113-
.Property.PropertyType.GetDefaultValue()
114-
: sp.Value.GetValueOrDefault(
115-
sp.Specification.DefaultValue.GetValueOrDefault(
116-
sp.Specification.ConversionType.CreateDefaultForImmutable()))).ToArray();
117-
var immutable = (T)ctor.Invoke(values);
118-
return immutable;
119-
};
120-
121-
var instance = typeInfo.IsMutable() ? buildMutable() : buildImmutable();
122-
95+
instance = BuildImmutable(typeInfo, factory, specProps, specPropsWithValue, setPropertyErrors);
96+
}
97+
12398
var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens));
12499

125100
var allErrors =
@@ -150,5 +125,67 @@ from sp in spv.DefaultIfEmpty()
150125

151126
return result;
152127
}
128+
129+
private static T BuildMutable<T>(Maybe<Func<T>> factory, IEnumerable<SpecificationProperty> specPropsWithValue, List<Error> setPropertyErrors )
130+
{
131+
var mutable = factory.MapValueOrDefault(f => f(), Activator.CreateInstance<T>());
132+
133+
setPropertyErrors.AddRange(
134+
mutable.SetProperties(
135+
specPropsWithValue,
136+
sp => sp.Value.IsJust(),
137+
sp => sp.Value.FromJustOrFail()
138+
)
139+
);
140+
141+
setPropertyErrors.AddRange(
142+
mutable.SetProperties(
143+
specPropsWithValue,
144+
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
145+
sp => sp.Specification.DefaultValue.FromJustOrFail()
146+
)
147+
);
148+
149+
setPropertyErrors.AddRange(
150+
mutable.SetProperties(
151+
specPropsWithValue,
152+
sp => sp.Value.IsNothing()
153+
&& sp.Specification.TargetType == TargetType.Sequence
154+
&& sp.Specification.DefaultValue.MatchNothing(),
155+
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray()
156+
)
157+
);
158+
159+
return mutable;
160+
}
161+
162+
private static T BuildImmutable<T>(Type typeInfo, Maybe<Func<T>> factory, IEnumerable<SpecificationProperty> specProps, IEnumerable<SpecificationProperty> specPropsWithValue, List<Error> setPropertyErrors)
163+
{
164+
var ctor = typeInfo.GetTypeInfo().GetConstructor(
165+
specProps.Select(sp => sp.Property.PropertyType).ToArray()
166+
);
167+
168+
if(ctor == null)
169+
{
170+
throw new InvalidOperationException($"Type appears to be immutable, but no constructor found for type {typeInfo.FullName} to accept values.");
171+
}
172+
173+
var values =
174+
(from prms in ctor.GetParameters()
175+
join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower() into spv
176+
from sp in spv.DefaultIfEmpty()
177+
select
178+
sp == null
179+
? specProps.First(s => String.Equals(s.Property.Name, prms.Name, StringComparison.CurrentCultureIgnoreCase))
180+
.Property.PropertyType.GetDefaultValue()
181+
: sp.Value.GetValueOrDefault(
182+
sp.Specification.DefaultValue.GetValueOrDefault(
183+
sp.Specification.ConversionType.CreateDefaultForImmutable()))).ToArray();
184+
185+
var immutable = (T)ctor.Invoke(values);
186+
187+
return immutable;
188+
}
189+
153190
}
154-
}
191+
}

Diff for: src/CommandLine/Core/ReflectionExtensions.cs

+20-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace CommandLine.Core
1414
{
1515
static class ReflectionExtensions
1616
{
17+
public const string CannotSetValueToTargetInstance = "Cannot set value to target instance.";
18+
1719
public static IEnumerable<T> GetSpecifications<T>(this Type type, Func<PropertyInfo, T> selector)
1820
{
1921
return from pi in type.FlattenHierarchy().SelectMany(x => x.GetTypeInfo().GetProperties())
@@ -91,6 +93,10 @@ public static IEnumerable<Error> SetProperties<T>(
9193

9294
private static IEnumerable<Error> SetValue<T>(this SpecificationProperty specProp, T instance, object value)
9395
{
96+
Action<Exception> fail = inner => {
97+
throw new InvalidOperationException(CannotSetValueToTargetInstance, inner);
98+
};
99+
94100
try
95101
{
96102
specProp.Property.SetValue(instance, value, null);
@@ -104,6 +110,13 @@ private static IEnumerable<Error> SetValue<T>(this SpecificationProperty specPro
104110
{
105111
return new[] { new SetValueExceptionError(specProp.Specification.FromSpecification(), e, value) };
106112
}
113+
catch(ArgumentException e)
114+
{
115+
var argEx = new ArgumentException(InvalidAttributeConfigurationError.ErrorMessage, e);
116+
fail(argEx);
117+
}
118+
119+
return instance;
107120
}
108121

109122
public static object CreateEmptyArray(this Type type)
@@ -122,12 +135,13 @@ public static object GetDefaultValue(this Type type)
122135

123136
public static bool IsMutable(this Type type)
124137
{
125-
Func<bool> isMutable = () => {
126-
var props = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance).Any(p => p.CanWrite);
127-
var fields = type.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance).Any();
128-
return props || fields;
129-
};
130-
return type != typeof(object) ? isMutable() : true;
138+
if(type == typeof(object))
139+
return true;
140+
141+
var props = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance).Any(p => p.CanWrite);
142+
var fields = type.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance).Any();
143+
144+
return props || fields;
131145
}
132146

133147
public static object CreateDefaultForImmutable(this Type type)

Diff for: src/CommandLine/Error.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ public enum ErrorType
6464
/// <summary>
6565
/// Value of <see cref="CommandLine.SetValueExceptionError"/> type.
6666
/// </summary>
67-
SetValueExceptionError
67+
SetValueExceptionError,
68+
69+
VersionRequestedError,
70+
/// <summary>
71+
/// Value of <see cref="CommandLine.InvalidAttributeConfigurationError"/> type.
72+
/// </summary>
73+
InvalidAttributeConfigurationError
6874
}
6975

7076
/// <summary>

Diff for: src/CommandLine/Infrastructure/ReflectionHelper.cs

+2-5
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,8 @@ public static bool IsFSharpOptionType(Type type)
8484

8585
public static T CreateDefaultImmutableInstance<T>(Type[] constructorTypes)
8686
{
87-
var t = typeof(T);
88-
var ctor = t.GetTypeInfo().GetConstructor(constructorTypes);
89-
var values = (from prms in ctor.GetParameters()
90-
select prms.ParameterType.CreateDefaultForImmutable()).ToArray();
91-
return (T)ctor.Invoke(values);
87+
var t = typeof(T);
88+
return (T)CreateDefaultImmutableInstance(t, constructorTypes);
9289
}
9390

9491
public static object CreateDefaultImmutableInstance(Type type, Type[] constructorTypes)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+

2+
namespace CommandLine.Tests.Fakes
3+
{
4+
class Options_With_InvalidDefaults
5+
{
6+
// Default of string and integer type property will also throw.
7+
8+
[Option(Default = false)]
9+
public string FileName { get; set; }
10+
}
11+
}

Diff for: tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,37 @@ public void Parse_TimeSpan()
11331133
// Teardown
11341134
}
11351135

1136-
public static IEnumerable<object[]> RequiredValueStringData
1136+
[Fact]
1137+
public void Build_DefaultBoolTypeString_ThrowsInvalidOperationException()
1138+
{
1139+
// Exercize system
1140+
Action test = () => InvokeBuild<Options_With_InvalidDefaults>(
1141+
new string[] { });
1142+
1143+
// Verify outcome
1144+
test.ShouldThrow<InvalidOperationException>()
1145+
.WithMessage(ReflectionExtensions.CannotSetValueToTargetInstance)
1146+
.WithInnerException<ArgumentException>()
1147+
.WithInnerMessage(InvalidAttributeConfigurationError.ErrorMessage);
1148+
}
1149+
1150+
1151+
[Fact]
1152+
public void OptionClass_IsImmutable_HasNoCtor()
1153+
{
1154+
Action act = () => InvokeBuild<ValueWithNoSetterOptions>(new string[] { "Test" }, false, false);
1155+
1156+
act.Should().Throw<InvalidOperationException>();
1157+
}
1158+
1159+
private class ValueWithNoSetterOptions
1160+
{
1161+
[Value(0, MetaName = "Test", Default = 0)]
1162+
public int TestValue { get; }
1163+
}
1164+
1165+
1166+
public static IEnumerable<object> RequiredValueStringData
11371167
{
11381168
get
11391169
{

0 commit comments

Comments
 (0)