forked from commandlineparser/commandline
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTypeConverter.cs
More file actions
158 lines (139 loc) · 6.61 KB
/
TypeConverter.cs
File metadata and controls
158 lines (139 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using CommandLine.Infrastructure;
using CSharpx;
using RailwaySharp.ErrorHandling;
using System.Reflection;
namespace CommandLine.Core
{
static class TypeConverter
{
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, bool isFlag, CultureInfo conversionCulture, bool ignoreValueCase)
{
return isFlag
? ChangeTypeFlagCounter(values, conversionType, conversionCulture, ignoreValueCase)
: scalar
? ChangeTypeScalar(values.Last(), conversionType, conversionCulture, ignoreValueCase)
: ChangeTypeSequence(values, conversionType, conversionCulture, ignoreValueCase);
}
private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
{
var type =
conversionType.GetTypeInfo()
.GetGenericArguments()
.SingleOrDefault()
.ToMaybe()
.FromJustOrFail(
new InvalidOperationException("Non scalar properties should be sequence of type IEnumerable<T>.")
);
var converted = values.Select(value => ChangeTypeScalar(value, type, conversionCulture, ignoreValueCase));
return converted.Any(a => a.MatchNothing())
? Maybe.Nothing<object>()
: Maybe.Just(converted.Select(c => ((Just<object>)c).Value).ToUntypedArray(type));
}
private static Maybe<object> ChangeTypeScalar(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
{
var result = ChangeTypeScalarImpl(value, conversionType, conversionCulture, ignoreValueCase);
result.Match((_,__) => { }, e => e.First().RethrowWhenAbsentIn(
new[] { typeof(InvalidCastException), typeof(FormatException), typeof(OverflowException) }));
return result.ToMaybe();
}
private static Maybe<object> ChangeTypeFlagCounter(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
{
var converted = values.Select(value => ChangeTypeScalar(value, typeof(bool), conversionCulture, ignoreValueCase));
return converted.Any(maybe => maybe.MatchNothing())
? Maybe.Nothing<object>()
: Maybe.Just((object)converted.Count(value => value.IsJust()));
}
private static object ConvertString(string value, Type type, CultureInfo conversionCulture)
{
try
{
return Convert.ChangeType(value, type, conversionCulture);
}
catch (InvalidCastException)
{
// Required for converting from string to TimeSpan because Convert.ChangeType can't
return System.ComponentModel.TypeDescriptor.GetConverter(type).ConvertFrom(null, conversionCulture, value);
}
}
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
{
Func<object> changeType = () =>
{
Func<object> safeChangeType = () =>
{
var isFsOption = ReflectionHelper.IsFSharpOptionType(conversionType);
Func<Type> getUnderlyingType =
() =>
#if !SKIP_FSHARP
isFsOption
? FSharpOptionHelper.GetUnderlyingType(conversionType) :
#endif
Nullable.GetUnderlyingType(conversionType);
var type = getUnderlyingType() ?? conversionType;
Func<object> withValue =
() =>
#if !SKIP_FSHARP
isFsOption
? FSharpOptionHelper.Some(type, ConvertString(value, type, conversionCulture)) :
#endif
ConvertString(value, type, conversionCulture);
#if !SKIP_FSHARP
Func<object> empty = () => isFsOption ? FSharpOptionHelper.None(type) : null;
#else
Func<object> empty = () => null;
#endif
return (value == null) ? empty() : withValue();
};
return value.IsBooleanString() && conversionType == typeof(bool)
? value.ToBoolean() : conversionType.GetTypeInfo().IsEnum
? value.ToEnum(conversionType, ignoreValueCase) : safeChangeType();
};
Func<object> makeType = () =>
{
try
{
var ctor = conversionType.GetTypeInfo().GetConstructor(new[] { typeof(string) });
return ctor.Invoke(new object[] { value });
}
catch (Exception)
{
throw new FormatException("Destination conversion type must have a constructor that accepts a string.");
}
};
if (conversionType.IsCustomStruct()) return Result.Try(makeType);
return Result.Try(
conversionType.IsPrimitiveEx() || ReflectionHelper.IsFSharpOptionType(conversionType)
? changeType
: makeType);
}
private static object ToEnum(this string value, Type conversionType, bool ignoreValueCase)
{
object parsedValue;
try
{
parsedValue = Enum.Parse(conversionType, value, ignoreValueCase);
}
catch (ArgumentException)
{
throw new FormatException();
}
if (IsDefinedEx(parsedValue))
{
return parsedValue;
}
throw new FormatException();
}
private static bool IsDefinedEx(object enumValue)
{
char firstChar = enumValue.ToString()[0];
if (Char.IsDigit(firstChar) || firstChar == '-')
return false;
return true;
}
}
}