Added support for non-int enums

This commit is contained in:
RogueException
2016-07-20 23:16:06 -03:00
parent c101c44c04
commit b407c71567
2 changed files with 66 additions and 43 deletions

View File

@@ -18,7 +18,7 @@ namespace Discord.Commands
public string Text { get; } public string Text { get; }
public Module Module { get; } public Module Module { get; }
public IReadOnlyList<CommandParameter> Parameters { get; } public IReadOnlyList<CommandParameter> Parameters { get; }
internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix) internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix)
{ {
Module = module; Module = module;
@@ -76,24 +76,17 @@ namespace Discord.Commands
} }
var typeInfo = type.GetTypeInfo(); var typeInfo = type.GetTypeInfo();
if (typeInfo.IsEnum)
Module.Service.AddTypeReader(type, new EnumTypeReader(type));
var reader = Module.Service.GetTypeReader(type); var reader = Module.Service.GetTypeReader(type);
if (reader == null) if (reader == null && typeInfo.IsEnum)
{ {
if (typeInfo.IsEnum) reader = EnumTypeReader.GetReader(type);
{ Module.Service.AddTypeReader(type, reader);
type = Enum.GetUnderlyingType(type);
reader = Module.Service.GetTypeReader(type);
}
if (reader == null)
throw new InvalidOperationException($"{type.FullName} is not supported as a command parameter, are you missing a TypeReader?");
} }
if (reader == null)
throw new InvalidOperationException($"{type.FullName} is not supported as a command parameter, are you missing a TypeReader?");
bool isUnparsed = parameter.GetCustomAttribute<UnparsedAttribute>() != null; bool isUnparsed = parameter.GetCustomAttribute<UnparsedAttribute>() != null;
if (isUnparsed) if (isUnparsed)
{ {

View File

@@ -1,54 +1,84 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Discord.Commands namespace Discord.Commands
{ {
internal class EnumTypeReader : TypeReader delegate bool TryParseDelegate<T>(string str, out T value);
internal static class EnumTypeReader
{
private static readonly IReadOnlyDictionary<Type, object> _parsers;
static EnumTypeReader()
{
var parserBuilder = ImmutableDictionary.CreateBuilder<Type, object>();
parserBuilder[typeof(sbyte)] = (TryParseDelegate<sbyte>)sbyte.TryParse;
parserBuilder[typeof(byte)] = (TryParseDelegate<byte>)byte.TryParse;
parserBuilder[typeof(short)] = (TryParseDelegate<short>)short.TryParse;
parserBuilder[typeof(ushort)] = (TryParseDelegate<ushort>)ushort.TryParse;
parserBuilder[typeof(int)] = (TryParseDelegate<int>)int.TryParse;
parserBuilder[typeof(uint)] = (TryParseDelegate<uint>)uint.TryParse;
parserBuilder[typeof(long)] = (TryParseDelegate<long>)long.TryParse;
parserBuilder[typeof(ulong)] = (TryParseDelegate<ulong>)ulong.TryParse;
_parsers = parserBuilder.ToImmutable();
}
public static TypeReader GetReader(Type type)
{
Type baseType = Enum.GetUnderlyingType(type);
var constructor = typeof(EnumTypeReader<>).MakeGenericType(baseType).GetTypeInfo().DeclaredConstructors.First();
return (TypeReader)constructor.Invoke(new object[] { type, _parsers[baseType] });
}
}
internal class EnumTypeReader<T> : TypeReader
{ {
private readonly Dictionary<string, object> stringValues; private readonly IReadOnlyDictionary<string, object> _enumsByName;
private readonly Dictionary<int, object> intValues; private readonly IReadOnlyDictionary<T, object> _enumsByValue;
private readonly Type enumType; private readonly Type _enumType;
private readonly TryParseDelegate<T> _tryParse;
public EnumTypeReader(Type type, TryParseDelegate<T> parser)
{
_enumType = type;
_tryParse = parser;
var byNameBuilder = ImmutableDictionary.CreateBuilder<string, object>();
var byValueBuilder = ImmutableDictionary.CreateBuilder<T, object>();
foreach (var v in Enum.GetValues(_enumType))
{
byNameBuilder.Add(v.ToString().ToLower(), v);
byValueBuilder.Add((T)v, v);
}
_enumsByName = byNameBuilder.ToImmutable();
_enumsByValue = byValueBuilder.ToImmutable();
}
public override Task<TypeReaderResult> Read(IMessage context, string input) public override Task<TypeReaderResult> Read(IMessage context, string input)
{ {
int inputAsInt; T intValue;
object enumValue; object enumValue;
if (int.TryParse(input, out inputAsInt)) if (_tryParse(input, out intValue))
{ {
if (intValues.TryGetValue(inputAsInt, out enumValue)) if (_enumsByValue.TryGetValue(intValue, out enumValue))
return Task.FromResult(TypeReaderResult.FromSuccess(enumValue)); return Task.FromResult(TypeReaderResult.FromSuccess(enumValue));
else else
return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {enumType.Name}")); return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {_enumType.Name}"));
} }
else else
{ {
if (stringValues.TryGetValue(input.ToLower(), out enumValue)) if (_enumsByName.TryGetValue(input.ToLower(), out enumValue))
return Task.FromResult(TypeReaderResult.FromSuccess(enumValue)); return Task.FromResult(TypeReaderResult.FromSuccess(enumValue));
else else
return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {enumType.Name}")); return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {_enumType.Name}"));
} }
} }
public EnumTypeReader(Type type)
{
enumType = type;
var stringValuesBuilder = new Dictionary<string, object>();
var intValuesBuilder = new Dictionary<int, object>();
var values = Enum.GetValues(enumType);
foreach (var v in values)
{
stringValuesBuilder.Add(v.ToString().ToLower(), v);
intValuesBuilder.Add((int)v, v);
}
stringValues = stringValuesBuilder;
intValues = intValuesBuilder;
}
} }
} }