Added support for non-int enums
This commit is contained in:
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user