diff --git a/src/CommandLine/CommandLine.csproj b/src/CommandLine/CommandLine.csproj index 8f829c9f..b445db7c 100644 --- a/src/CommandLine/CommandLine.csproj +++ b/src/CommandLine/CommandLine.csproj @@ -169,8 +169,16 @@ - - + + + + ..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll + True + True + + + + ..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll diff --git a/src/CommandLine/Core/InstanceBuilder.cs b/src/CommandLine/Core/InstanceBuilder.cs index 0c24fddd..fd9ac4d7 100644 --- a/src/CommandLine/Core/InstanceBuilder.cs +++ b/src/CommandLine/Core/InstanceBuilder.cs @@ -80,22 +80,23 @@ public static ParserResult Build( var specPropsWithValue = optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()); - Func buildMutable = () => + var setPropertyErrors = new List(); + + Func buildMutable = () => { var mutable = factory.MapValueOrDefault(f => f(), Activator.CreateInstance()); - 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 buildImmutable = () => @@ -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; diff --git a/src/CommandLine/Core/ReflectionExtensions.cs b/src/CommandLine/Core/ReflectionExtensions.cs index 068b3034..0e8811ca 100644 --- a/src/CommandLine/Core/ReflectionExtensions.cs +++ b/src/CommandLine/Core/ReflectionExtensions.cs @@ -80,51 +80,30 @@ public static TargetType ToTargetType(this Type type) : TargetType.Scalar; } - public static T SetProperties( + public static IEnumerable SetProperties( this T instance, IEnumerable specProps, Func predicate, Func selector) { - return specProps.Where(predicate).Aggregate( - instance, - (current, specProp) => - { - specProp.Property.SetValue(current, selector(specProp)); - return instance; - }); - } - - private static T SetValue(this PropertyInfo property, T instance, object value) - { - Action 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 SetValue(this PropertyInfo property, T instance, object value) + { + try + { + property.SetValue(instance, value, null); + return Enumerable.Empty(); + } + catch (TargetInvocationException e) + { + return new[] { new SetValueExceptionError(e.InnerException) }; + } + catch (Exception e) + { + return new[] { new SetValueExceptionError(e) }; + } } public static object CreateEmptyArray(this Type type) diff --git a/src/CommandLine/Error.cs b/src/CommandLine/Error.cs index 475ac8e3..0cbf4938 100644 --- a/src/CommandLine/Error.cs +++ b/src/CommandLine/Error.cs @@ -60,7 +60,11 @@ public enum ErrorType /// /// Value of type. /// - VersionRequestedError + VersionRequestedError, + /// + /// Value of type. + /// + SetValueExceptionError } /// @@ -471,4 +475,26 @@ internal VersionRequestedError() { } } + + /// + /// Models as error generated when exception is thrown at Property.SetValue + /// + public sealed class SetValueExceptionError : Error + { + private readonly Exception exception; + + internal SetValueExceptionError(Exception exception) + : base(ErrorType.SetValueExceptionError) + { + this.exception = exception; + } + + /// + /// The expection thrown from Property.SetValue + /// + public Exception Exception + { + get { return exception; } + } + } } \ No newline at end of file diff --git a/src/CommandLine/Text/SentenceBuilder.cs b/src/CommandLine/Text/SentenceBuilder.cs index 1c28e959..44ca5b8f 100644 --- a/src/CommandLine/Text/SentenceBuilder.cs +++ b/src/CommandLine/Text/SentenceBuilder.cs @@ -137,6 +137,8 @@ public override Func 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(); };