Skip to content

Commit

Permalink
Issue commandlineparser#283: report exceptions in property.SetValue a…
Browse files Browse the repository at this point in the history
…s parsing errors

ToDo: add tests and nameinfo
  • Loading branch information
e673 committed May 5, 2018
1 parent 67f77e1 commit 2377f59
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 57 deletions.
12 changes: 10 additions & 2 deletions src/CommandLine/CommandLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,16 @@
</Reference>
</ItemGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3' Or $(TargetFrameworkVersion) == 'v4.6' Or $(TargetFrameworkVersion) == 'v4.6.1' Or $(TargetFrameworkVersion) == 'v4.6.2' Or $(TargetFrameworkVersion) == 'v4.6.3' Or $(TargetFrameworkVersion) == 'v4.7' Or $(TargetFrameworkVersion) == 'v4.7.1')" />
<When Condition="($(TargetFrameworkIdentifier) == '.NETCore') Or ($(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v2.2' Or $(TargetFrameworkVersion) == 'v2.3' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.1' Or $(TargetFrameworkVersion) == 'v4.2' Or $(TargetFrameworkVersion) == 'v4.3' Or $(TargetFrameworkVersion) == 'v4.4' Or $(TargetFrameworkVersion) == 'v5.0' Or $(TargetFrameworkVersion) == 'v5.1' Or $(TargetFrameworkVersion) == 'v6.0' Or $(TargetFrameworkVersion) == 'v7.0' Or $(TargetFrameworkVersion) == 'v7.1' Or $(TargetFrameworkVersion) == 'v8.0')) Or ($(TargetFrameworkIdentifier) == 'MonoTouch') Or ($(TargetFrameworkIdentifier) == '.NETCoreApp' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkIdentifier) == '.NETStandard' And ($(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v1.2' Or $(TargetFrameworkVersion) == 'v1.3' Or $(TargetFrameworkVersion) == 'v1.4' Or $(TargetFrameworkVersion) == 'v1.5' Or $(TargetFrameworkVersion) == 'v1.6' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkProfile) == 'Profile7') Or ($(TargetFrameworkProfile) == 'Profile44') Or ($(TargetFrameworkIdentifier) == 'Xamarin.iOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.Mac') Or ($(TargetFrameworkIdentifier) == 'Xamarin.tvOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.watchOS')">
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3' Or $(TargetFrameworkVersion) == 'v4.6' Or $(TargetFrameworkVersion) == 'v4.6.1' Or $(TargetFrameworkVersion) == 'v4.6.2' Or $(TargetFrameworkVersion) == 'v4.6.3' Or $(TargetFrameworkVersion) == 'v4.7' Or $(TargetFrameworkVersion) == 'v4.7.1')">
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
<When Condition="($(TargetFrameworkIdentifier) == '.NETCore') Or ($(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v2.2' Or $(TargetFrameworkVersion) == 'v2.3' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.1' Or $(TargetFrameworkVersion) == 'v4.2' Or $(TargetFrameworkVersion) == 'v4.3' Or $(TargetFrameworkVersion) == 'v4.4' Or $(TargetFrameworkVersion) == 'v5.0' Or $(TargetFrameworkVersion) == 'v5.1' Or $(TargetFrameworkVersion) == 'v6.0' Or $(TargetFrameworkVersion) == 'v7.0' Or $(TargetFrameworkVersion) == 'v7.1' Or $(TargetFrameworkVersion) == 'v8.0' Or $(TargetFrameworkVersion) == 'v8.1')) Or ($(TargetFrameworkIdentifier) == 'MonoTouch') Or ($(TargetFrameworkIdentifier) == '.NETCoreApp' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v2.0' Or $(TargetFrameworkVersion) == 'v2.1')) Or ($(TargetFrameworkIdentifier) == '.NETStandard' And ($(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v1.2' Or $(TargetFrameworkVersion) == 'v1.3' Or $(TargetFrameworkVersion) == 'v1.4' Or $(TargetFrameworkVersion) == 'v1.5' Or $(TargetFrameworkVersion) == 'v1.6' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkProfile) == 'Profile7') Or ($(TargetFrameworkProfile) == 'Profile44') Or ($(TargetFrameworkIdentifier) == 'Xamarin.iOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.Mac') Or ($(TargetFrameworkIdentifier) == 'Xamarin.tvOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.watchOS')">
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll</HintPath>
Expand Down
30 changes: 16 additions & 14 deletions src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,23 @@ public static ParserResult<T> Build<T>(
var specPropsWithValue =
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith());
Func<T> buildMutable = () =>
var setPropertyErrors = new List<Error>();
Func <T> buildMutable = () =>
{
var mutable = factory.MapValueOrDefault(f => f(), Activator.CreateInstance<T>());
mutable =
mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail())
.SetProperties(
specPropsWithValue,
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
sp => sp.Specification.DefaultValue.FromJustOrFail())
.SetProperties(
specPropsWithValue,
sp =>
sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence
&& sp.Specification.DefaultValue.MatchNothing(),
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray());
return mutable;
setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail()));
setPropertyErrors.AddRange(mutable.SetProperties(
specPropsWithValue,
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
sp => sp.Specification.DefaultValue.FromJustOrFail()));
setPropertyErrors.AddRange(mutable.SetProperties(
specPropsWithValue,
sp =>
sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence
&& sp.Specification.DefaultValue.MatchNothing(),
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray()));
return mutable;
};
Func<T> buildImmutable = () =>
Expand All @@ -121,6 +122,7 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
.Concat(optionSpecPropsResult.SuccessfulMessages())
.Concat(valueSpecPropsResult.SuccessfulMessages())
.Concat(validationErrors)
.Concat(setPropertyErrors)
.Memorize();
var warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e;
Expand Down
59 changes: 19 additions & 40 deletions src/CommandLine/Core/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,51 +80,30 @@ public static TargetType ToTargetType(this Type type)
: TargetType.Scalar;
}

public static T SetProperties<T>(
public static IEnumerable<Error> SetProperties<T>(
this T instance,
IEnumerable<SpecificationProperty> specProps,
Func<SpecificationProperty, bool> predicate,
Func<SpecificationProperty, object> selector)
{
return specProps.Where(predicate).Aggregate(
instance,
(current, specProp) =>
{
specProp.Property.SetValue(current, selector(specProp));
return instance;
});
}

private static T SetValue<T>(this PropertyInfo property, T instance, object value)
{
Action<Exception> fail = inner => {
throw new InvalidOperationException("Cannot set value to target instance.", inner);
};

try
{
property.SetValue(instance, value, null);
}
#if !PLATFORM_DOTNET
catch (TargetException e)
{
fail(e);
}
#endif
catch (TargetParameterCountException e)
{
fail(e);
}
catch (MethodAccessException e)
{
fail(e);
}
catch (TargetInvocationException e)
{
fail(e);
}

return instance;
return specProps.Where(predicate).SelectMany(specProp => specProp.Property.SetValue(instance, selector(specProp)));
}

private static IEnumerable<Error> SetValue<T>(this PropertyInfo property, T instance, object value)
{
try
{
property.SetValue(instance, value, null);
return Enumerable.Empty<Error>();
}
catch (TargetInvocationException e)
{
return new[] { new SetValueExceptionError(e.InnerException) };
}
catch (Exception e)
{
return new[] { new SetValueExceptionError(e) };
}
}

public static object CreateEmptyArray(this Type type)
Expand Down
28 changes: 27 additions & 1 deletion src/CommandLine/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ public enum ErrorType
/// <summary>
/// Value of <see cref="CommandLine.VersionRequestedError"/> type.
/// </summary>
VersionRequestedError
VersionRequestedError,
/// <summary>
/// Value of <see cref="CommandLine.SetValueExceptionError"/> type.
/// </summary>
SetValueExceptionError
}

/// <summary>
Expand Down Expand Up @@ -471,4 +475,26 @@ internal VersionRequestedError()
{
}
}

/// <summary>
/// Models as error generated when exception is thrown at Property.SetValue
/// </summary>
public sealed class SetValueExceptionError : Error
{
private readonly Exception exception;

internal SetValueExceptionError(Exception exception)
: base(ErrorType.SetValueExceptionError)
{
this.exception = exception;
}

/// <summary>
/// The expection thrown from Property.SetValue
/// </summary>
public Exception Exception
{
get { return exception; }
}
}
}
2 changes: 2 additions & 0 deletions src/CommandLine/Text/SentenceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public override Func<Error, string> FormatError
case ErrorType.RepeatedOptionError:
return "Option '".JoinTo(((RepeatedOptionError)error).NameInfo.NameText,
"' is defined multiple times.");
case ErrorType.SetValueExceptionError:
return "Error setting option value: ".JoinTo(((SetValueExceptionError)error).Exception.Message);
}
throw new InvalidOperationException();
};
Expand Down

0 comments on commit 2377f59

Please sign in to comment.