Implemented command type readers, parser and service.
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user