@@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
@@ -14,6 +15,7 @@ namespace Discord.Commands
private readonly SemaphoreSlim _moduleLock;
private readonly ConcurrentDictionary<object, Module> _modules;
private readonly ConcurrentDictionary<string, List<Command>> _map;
private readonly Dictionary<Type, TypeReader> _typeReaders;
public IEnumerable<Module> Modules => _modules.Select(x => x.Value);
public IEnumerable<Command> Commands => _modules.SelectMany(x => x.Value.Commands);
@@ -23,6 +25,113 @@ namespace Discord.Commands
_moduleLock = new SemaphoreSlim(1, 1);
_modules = new ConcurrentDictionary<object, Module>();
_map = new ConcurrentDictionary<string, List<Command>>();
_typeReaders = new Dictionary<Type, TypeReader>
{
[typeof(string)] = new GenericTypeReader((m, s) => Task.FromResult(TypeReaderResult.FromSuccess(s))),
[typeof(byte)] = new GenericTypeReader((m, s) =>
{
byte value;
if (byte.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Byte"));
}),
[typeof(sbyte)] = new GenericTypeReader((m, s) =>
{
sbyte value;
if (sbyte.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse SByte"));
}),
[typeof(ushort)] = new GenericTypeReader((m, s) =>
{
ushort value;
if (ushort.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt16"));
}),
[typeof(short)] = new GenericTypeReader((m, s) =>
{
short value;
if (short.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int16"));
}),
[typeof(uint)] = new GenericTypeReader((m, s) =>
{
uint value;
if (uint.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt32"));
}),
[typeof(int)] = new GenericTypeReader((m, s) =>
{
int value;
if (int.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int32"));
}),
[typeof(ulong)] = new GenericTypeReader((m, s) =>
{
ulong value;
if (ulong.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt64"));
}),
[typeof(long)] = new GenericTypeReader((m, s) =>
{
long value;
if (long.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int64"));
}),
[typeof(float)] = new GenericTypeReader((m, s) =>
{
float value;
if (float.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Single"));
}),
[typeof(double)] = new GenericTypeReader((m, s) =>
{
double value;
if (double.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Double"));
}),
[typeof(decimal)] = new GenericTypeReader((m, s) =>
{
decimal value;
if (decimal.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Decimal"));
}),
[typeof(DateTime)] = new GenericTypeReader((m, s) =>
{
DateTime value;
if (DateTime.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse DateTime"));
}),
[typeof(DateTimeOffset)] = new GenericTypeReader((m, s) =>
{
DateTimeOffset value;
if (DateTimeOffset.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value));
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse DateTimeOffset"));
}),
[typeof(IMessage)] = new MessageTypeReader(),
[typeof(IChannel)] = new ChannelTypeReader<IChannel>(),
[typeof(IGuildChannel)] = new ChannelTypeReader<IGuildChannel>(),
[typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(),
[typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(),
[typeof(IRole)] = new RoleTypeReader(),
[typeof(IUser)] = new UserTypeReader<IUser>(),
[typeof(IGuildUser)] = new UserTypeReader<IGuildUser>()
};
}
public void AddTypeReader<T>(TypeReader reader)
{
_typeReaders[typeof(T)] = reader;
}
public void AddTypeReader(Type type, TypeReader reader)
{
_typeReaders[type] = reader;
}
internal TypeReader GetTypeReader(Type type)
{
TypeReader reader;
if (_typeReaders.TryGetValue(type, out reader))
return reader;
return null;
}
public async Task<Module> Load(object module)
@@ -46,7 +155,7 @@ namespace Discord.Commands
}
private Module LoadInternal(object module, TypeInfo typeInfo)
{
var loadedModule = new Module(module, typeInfo);
var loadedModule = new Module(this, module, typeInfo);
_modules[module] = loadedModule;
foreach (var cmd in loadedModule.Commands)
@@ -114,7 +223,7 @@ namespace Discord.Commands
}
//TODO: C#7 Candidate for tuple
public SearchResults Search(string input)
public SearchResult Search(string input)
{
string lowerInput = input.ToLowerInvariant();
@@ -125,21 +234,25 @@ namespace Discord.Commands
{
endPos = input.IndexOf(' ', startPos);
string cmdText = endPos == -1 ? input.Substring(startPos) : input.Substring(startPos, endPos - startPos);
startPos = endPos + 1;
if (!_map.TryGetValue(cmdText, out group))
break;
bestGroup = group;
if (endPos == -1)
{
startPos = input.Length;
break;
}
else
startPos = endPos + 1;
}
ImmutableArray<Command> cmds;
if (bestGroup != null)
{
lock (bestGroup)
cmds = bestGroup.ToImmutableArray();
return SearchResult.FromSuccess(bestGroup.ToImmutableArray(), input.Substring(startPos) );
}
else
cmds = ImmutableArray.Create<Command>();
return new SearchResults(cmds, startPos);
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command.");
}
}
}