From b407c71567c4894cfee70c358dfe89898cc80f99 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 20 Jul 2016 23:16:06 -0300 Subject: [PATCH] Added support for non-int enums --- src/Discord.Net.Commands/Command.cs | 21 ++---- src/Discord.Net.Commands/Readers/EnumTypeReader.cs | 88 +++++++++++++++------- 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/Discord.Net.Commands/Command.cs b/src/Discord.Net.Commands/Command.cs index 433a63073..909765e93 100644 --- a/src/Discord.Net.Commands/Command.cs +++ b/src/Discord.Net.Commands/Command.cs @@ -18,7 +18,7 @@ namespace Discord.Commands public string Text { get; } public Module Module { get; } public IReadOnlyList Parameters { get; } - + internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix) { Module = module; @@ -76,24 +76,17 @@ namespace Discord.Commands } var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsEnum) - Module.Service.AddTypeReader(type, new EnumTypeReader(type)); - var reader = Module.Service.GetTypeReader(type); - if (reader == null) + if (reader == null && typeInfo.IsEnum) { - if (typeInfo.IsEnum) - { - 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?"); + reader = EnumTypeReader.GetReader(type); + Module.Service.AddTypeReader(type, reader); } + if (reader == null) + throw new InvalidOperationException($"{type.FullName} is not supported as a command parameter, are you missing a TypeReader?"); + bool isUnparsed = parameter.GetCustomAttribute() != null; if (isUnparsed) { diff --git a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs index fc75432bf..b9d31ab32 100644 --- a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs @@ -1,54 +1,84 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Reflection; using System.Threading.Tasks; namespace Discord.Commands { - internal class EnumTypeReader : TypeReader + delegate bool TryParseDelegate(string str, out T value); + + internal static class EnumTypeReader + { + private static readonly IReadOnlyDictionary _parsers; + + static EnumTypeReader() + { + var parserBuilder = ImmutableDictionary.CreateBuilder(); + parserBuilder[typeof(sbyte)] = (TryParseDelegate)sbyte.TryParse; + parserBuilder[typeof(byte)] = (TryParseDelegate)byte.TryParse; + parserBuilder[typeof(short)] = (TryParseDelegate)short.TryParse; + parserBuilder[typeof(ushort)] = (TryParseDelegate)ushort.TryParse; + parserBuilder[typeof(int)] = (TryParseDelegate)int.TryParse; + parserBuilder[typeof(uint)] = (TryParseDelegate)uint.TryParse; + parserBuilder[typeof(long)] = (TryParseDelegate)long.TryParse; + parserBuilder[typeof(ulong)] = (TryParseDelegate)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 : TypeReader { - private readonly Dictionary stringValues; - private readonly Dictionary intValues; - private readonly Type enumType; + private readonly IReadOnlyDictionary _enumsByName; + private readonly IReadOnlyDictionary _enumsByValue; + private readonly Type _enumType; + private readonly TryParseDelegate _tryParse; + + public EnumTypeReader(Type type, TryParseDelegate parser) + { + _enumType = type; + _tryParse = parser; + + var byNameBuilder = ImmutableDictionary.CreateBuilder(); + var byValueBuilder = ImmutableDictionary.CreateBuilder(); + + 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 Read(IMessage context, string input) { - int inputAsInt; + T intValue; 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)); 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 { - if (stringValues.TryGetValue(input.ToLower(), out enumValue)) + if (_enumsByName.TryGetValue(input.ToLower(), out enumValue)) return Task.FromResult(TypeReaderResult.FromSuccess(enumValue)); else - return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {enumType.Name}")); - } - } - - public EnumTypeReader(Type type) - { - enumType = type; - - var stringValuesBuilder = new Dictionary(); - var intValuesBuilder = new Dictionary(); - - var values = Enum.GetValues(enumType); - - foreach (var v in values) - { - stringValuesBuilder.Add(v.ToString().ToLower(), v); - intValuesBuilder.Add((int)v, v); + return Task.FromResult(TypeReaderResult.FromError(CommandError.CastFailed, $"Value is not a {_enumType.Name}")); } - - stringValues = stringValuesBuilder; - intValues = intValuesBuilder; } } }