-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from pamidur/feature/enum-parsing
Made type-converter enum aware
- Loading branch information
Showing
5 changed files
with
123 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": ".NET Core Attach", | ||
"type": "coreclr", | ||
"request": "attach", | ||
"processId": "${command:pickProcess}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"files.exclude": { | ||
"**/bin": true, | ||
"**/obj": true | ||
}, | ||
"search.exclude": { | ||
"**/obj": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 26 additions & 15 deletions
41
src/QRest.Core/Compilation/TypeConverters/StringParser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,52 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
|
||
namespace QRest.Core.Compilation.TypeConverters | ||
{ | ||
public class StringParser | ||
{ | ||
private static readonly string _parseMethodName = "Parse"; | ||
private static readonly string _parseMethodName = "Parse"; | ||
private static readonly Type[] _parseWithFormatSignature = new[] { typeof(string), typeof(IFormatProvider) }; | ||
private static readonly Type[] _parseSignature = new[] { typeof(string) }; | ||
private static readonly MethodInfo _enumParser = typeof(Enum).GetMethod(nameof(Enum.Parse), new[] { typeof(Type), typeof(string) }); | ||
|
||
private static readonly Dictionary<Type, Func<Expression, IFormatProvider, Expression>> _parsers = new Dictionary<Type, Func<Expression, IFormatProvider, Expression>>(); | ||
|
||
public static Func<Expression, IFormatProvider, Expression> GetParser(Type type) | ||
{ | ||
if (!_parsers.ContainsKey(type)) | ||
{ | ||
var source = Expression.Parameter(typeof(string)); | ||
|
||
Func<Expression, IFormatProvider, Expression[]> parameters = null; | ||
|
||
var method = type.GetMethod(_parseMethodName, _parseWithFormatSignature); | ||
if (method != null) parameters = (e, c) => new Expression[] { e, Expression.Constant(c, typeof(IFormatProvider)) }; | ||
else | ||
if (type.IsEnum) | ||
{ | ||
method = type.GetMethod(_parseMethodName, _parseSignature); | ||
if (method != null) | ||
parameters = (e, c) => new Expression[] { e }; | ||
_parsers[type] = ParseEnum(type); | ||
} | ||
|
||
if (method != null && parameters != null) | ||
_parsers[type] = (e, c) => Expression.Call(method, parameters(e, c)); | ||
else | ||
_parsers[type] = null; | ||
{ | ||
_parsers[type] = ParseOtherTypes(type) ?? throw new CompilationException($"Cannot find parser for type {type}"); | ||
} | ||
} | ||
|
||
return _parsers[type]; | ||
} | ||
|
||
private static Func<Expression, IFormatProvider, Expression> ParseOtherTypes(Type type) | ||
{ | ||
var method = type.GetMethod(_parseMethodName, _parseWithFormatSignature); | ||
if (method != null) | ||
return (e, c) => Expression.Call(method, new[] { e, Expression.Constant(c, typeof(IFormatProvider)) }); | ||
|
||
method = type.GetMethod(_parseMethodName, _parseSignature); | ||
if (method != null) | ||
return (e, c) => Expression.Call(method, new[] { e }); | ||
|
||
return null; | ||
} | ||
|
||
private static Func<Expression, IFormatProvider, Expression> ParseEnum(Type type) | ||
{ | ||
return (e, c) => Expression.Convert(Expression.Call(_enumParser, new[] { Expression.Constant(type), e }), type); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using QRest.Core.Compilation.TypeConverters; | ||
using System; | ||
using System.Globalization; | ||
using System.Linq.Expressions; | ||
using Xunit; | ||
|
||
namespace QRest.Compiler.Standard.Tests | ||
{ | ||
public enum SimpleEnum | ||
{ | ||
First, | ||
Second | ||
} | ||
|
||
public enum IntEnum : int | ||
{ | ||
First = 10, | ||
Second = 20 | ||
} | ||
|
||
|
||
public class StringParserTests | ||
{ | ||
private readonly DefaultTypeConverter _converter; | ||
|
||
public StringParserTests() | ||
{ | ||
_converter = new DefaultTypeConverter(CultureInfo.InvariantCulture, true); | ||
} | ||
|
||
[Fact(DisplayName = "Can parse string enum")] | ||
public void Can_Parse_String_Enum() | ||
{ | ||
var param = Expression.Parameter(typeof(string)); | ||
Assert.True(_converter.TryConvert(param, typeof(SimpleEnum), out var parser)); | ||
|
||
var lambda = Expression.Lambda<Func<string, SimpleEnum>>(parser, param); | ||
var result = lambda.Compile()(SimpleEnum.Second.ToString()); | ||
|
||
Assert.Equal(SimpleEnum.Second, result); | ||
} | ||
|
||
[Fact(DisplayName = "Can parse int enum")] | ||
public void Can_Parse_Int_Enum() | ||
{ | ||
var param = Expression.Parameter(typeof(int)); | ||
Assert.True(_converter.TryConvert(param, typeof(IntEnum), out var parser)); | ||
|
||
var lambda = Expression.Lambda<Func<int, IntEnum>>(parser, param); | ||
var result = lambda.Compile()((int)IntEnum.Second); | ||
|
||
Assert.Equal(IntEnum.Second, result); | ||
} | ||
|
||
[Fact(DisplayName = "Can parse stringly-int enum")] | ||
public void Can_Parse_Stringly_Int_Enum() | ||
{ | ||
var param = Expression.Parameter(typeof(string)); | ||
Assert.True(_converter.TryConvert(param, typeof(IntEnum), out var parser)); | ||
|
||
var lambda = Expression.Lambda<Func<string, IntEnum>>(parser, param); | ||
var result = lambda.Compile()(((int)IntEnum.Second).ToString()); | ||
|
||
Assert.Equal(IntEnum.Second, result); | ||
} | ||
} | ||
} |