Skip to content

Commit 872b241

Browse files
Merge pull request #11 from pamidur/feature/enum-parsing
Made type-converter enum aware
2 parents f73b08e + f8dfaa3 commit 872b241

File tree

5 files changed

+123
-23
lines changed

5 files changed

+123
-23
lines changed

.vscode/launch.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": ".NET Core Attach",
6+
"type": "coreclr",
7+
"request": "attach",
8+
"processId": "${command:pickProcess}"
9+
}
10+
]
11+
}

.vscode/settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"files.exclude": {
3+
"**/bin": true,
4+
"**/obj": true
5+
},
6+
"search.exclude": {
7+
"**/obj": true
8+
}
9+
}

src/QRest.Core/Compilation/TypeConverters/DefaultTypeConverter.cs

+10-8
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ public bool TryConvert(Expression expression, Type target, out Expression result
2626
result = null;
2727
var key = (expression.Type, target);
2828

29-
if (_converters.TryGetValue(key, out var converter))
30-
result = converter(expression, _format);
31-
else if(expression.Type == target)
32-
result = expression;
33-
else if (target.IsAssignableFrom(expression.Type))
34-
result = Expression.Convert(expression, target);
29+
if (_converters.TryGetValue(key, out var converter))
30+
result = converter(expression, _format);
31+
else if (expression.Type == target)
32+
result = expression;
33+
else if (target.IsAssignableFrom(expression.Type))
34+
result = Expression.Convert(expression, target);
35+
else if (target.IsEnum && target.GetEnumUnderlyingType().IsAssignableFrom(expression.Type))
36+
result = Expression.Convert(expression, target);
3537
else if (expression.Type == typeof(string) && _parseStrings)
3638
{
3739
var parser = StringParser.GetParser(target);
@@ -40,8 +42,8 @@ public bool TryConvert(Expression expression, Type target, out Expression result
4042
_converters[key] = parser;
4143
result = parser(expression, _format);
4244
}
43-
}
44-
45+
}
46+
4547
return result != null;
4648
}
4749

Original file line numberDiff line numberDiff line change
@@ -1,41 +1,52 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq.Expressions;
4+
using System.Reflection;
45

56
namespace QRest.Core.Compilation.TypeConverters
67
{
78
public class StringParser
89
{
9-
private static readonly string _parseMethodName = "Parse";
10+
private static readonly string _parseMethodName = "Parse";
1011
private static readonly Type[] _parseWithFormatSignature = new[] { typeof(string), typeof(IFormatProvider) };
1112
private static readonly Type[] _parseSignature = new[] { typeof(string) };
13+
private static readonly MethodInfo _enumParser = typeof(Enum).GetMethod(nameof(Enum.Parse), new[] { typeof(Type), typeof(string) });
1214

1315
private static readonly Dictionary<Type, Func<Expression, IFormatProvider, Expression>> _parsers = new Dictionary<Type, Func<Expression, IFormatProvider, Expression>>();
1416

1517
public static Func<Expression, IFormatProvider, Expression> GetParser(Type type)
1618
{
1719
if (!_parsers.ContainsKey(type))
1820
{
19-
var source = Expression.Parameter(typeof(string));
20-
21-
Func<Expression, IFormatProvider, Expression[]> parameters = null;
22-
23-
var method = type.GetMethod(_parseMethodName, _parseWithFormatSignature);
24-
if (method != null) parameters = (e, c) => new Expression[] { e, Expression.Constant(c, typeof(IFormatProvider)) };
25-
else
21+
if (type.IsEnum)
2622
{
27-
method = type.GetMethod(_parseMethodName, _parseSignature);
28-
if (method != null)
29-
parameters = (e, c) => new Expression[] { e };
23+
_parsers[type] = ParseEnum(type);
3024
}
31-
32-
if (method != null && parameters != null)
33-
_parsers[type] = (e, c) => Expression.Call(method, parameters(e, c));
3425
else
35-
_parsers[type] = null;
26+
{
27+
_parsers[type] = ParseOtherTypes(type) ?? throw new CompilationException($"Cannot find parser for type {type}");
28+
}
3629
}
3730

3831
return _parsers[type];
3932
}
33+
34+
private static Func<Expression, IFormatProvider, Expression> ParseOtherTypes(Type type)
35+
{
36+
var method = type.GetMethod(_parseMethodName, _parseWithFormatSignature);
37+
if (method != null)
38+
return (e, c) => Expression.Call(method, new[] { e, Expression.Constant(c, typeof(IFormatProvider)) });
39+
40+
method = type.GetMethod(_parseMethodName, _parseSignature);
41+
if (method != null)
42+
return (e, c) => Expression.Call(method, new[] { e });
43+
44+
return null;
45+
}
46+
47+
private static Func<Expression, IFormatProvider, Expression> ParseEnum(Type type)
48+
{
49+
return (e, c) => Expression.Convert(Expression.Call(_enumParser, new[] { Expression.Constant(type), e }), type);
50+
}
4051
}
4152
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using QRest.Core.Compilation.TypeConverters;
2+
using System;
3+
using System.Globalization;
4+
using System.Linq.Expressions;
5+
using Xunit;
6+
7+
namespace QRest.Compiler.Standard.Tests
8+
{
9+
public enum SimpleEnum
10+
{
11+
First,
12+
Second
13+
}
14+
15+
public enum IntEnum : int
16+
{
17+
First = 10,
18+
Second = 20
19+
}
20+
21+
22+
public class StringParserTests
23+
{
24+
private readonly DefaultTypeConverter _converter;
25+
26+
public StringParserTests()
27+
{
28+
_converter = new DefaultTypeConverter(CultureInfo.InvariantCulture, true);
29+
}
30+
31+
[Fact(DisplayName = "Can parse string enum")]
32+
public void Can_Parse_String_Enum()
33+
{
34+
var param = Expression.Parameter(typeof(string));
35+
Assert.True(_converter.TryConvert(param, typeof(SimpleEnum), out var parser));
36+
37+
var lambda = Expression.Lambda<Func<string, SimpleEnum>>(parser, param);
38+
var result = lambda.Compile()(SimpleEnum.Second.ToString());
39+
40+
Assert.Equal(SimpleEnum.Second, result);
41+
}
42+
43+
[Fact(DisplayName = "Can parse int enum")]
44+
public void Can_Parse_Int_Enum()
45+
{
46+
var param = Expression.Parameter(typeof(int));
47+
Assert.True(_converter.TryConvert(param, typeof(IntEnum), out var parser));
48+
49+
var lambda = Expression.Lambda<Func<int, IntEnum>>(parser, param);
50+
var result = lambda.Compile()((int)IntEnum.Second);
51+
52+
Assert.Equal(IntEnum.Second, result);
53+
}
54+
55+
[Fact(DisplayName = "Can parse stringly-int enum")]
56+
public void Can_Parse_Stringly_Int_Enum()
57+
{
58+
var param = Expression.Parameter(typeof(string));
59+
Assert.True(_converter.TryConvert(param, typeof(IntEnum), out var parser));
60+
61+
var lambda = Expression.Lambda<Func<string, IntEnum>>(parser, param);
62+
var result = lambda.Compile()(((int)IntEnum.Second).ToString());
63+
64+
Assert.Equal(IntEnum.Second, result);
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)