Allow commands to return a Task<RuntimeResult> (#466)

* Allow commands to return a Task<RuntimeResult>

This allows bot developers to centralize command result logic by
using result data whether the command as successful or not.

Example usage:
```csharp
var _result = await Commands.ExecuteAsync(context, argPos);
if (_result is RuntimeResult result)
{
    await message.Channel.SendMessageAsync(result.Reason);
}
else if (!_result.IsSuccess)
{
    // Previous error handling
}
```

The RuntimeResult class can be subclassed too, for example:
```csharp
var _result = await Commands.ExecuteAsync(context, argPos);
if (_result is MySubclassedResult result)
{
    var builder = new EmbedBuilder();
    for (var pair in result.Data)
    {
        builder.AddField(pair.Key, pair.Value, true);
    }
    await message.Channel.SendMessageAsync("", embed: builder);
}
else if (_result is RuntimeResult result)
{
    await message.Channel.SendMessageAsync(result.Reason);
}
else if (!_result.IsSuccess)
{
    // Previous error handling
}
```

* Make RuntimeResult's ctor protected

* Make RuntimeResult abstract

It never really made sense to have it instantiable in the first place,
frankly.
This commit is contained in:
Finite Reality
2017-06-29 23:21:05 +01:00
committed by RogueException
parent b96748f9c3
commit 74f6a4b392
5 changed files with 86 additions and 26 deletions

View File

@@ -197,22 +197,34 @@ namespace Discord.Commands
var createInstance = ReflectionUtils.CreateBuilder<IModuleBase>(typeInfo, service);
builder.Callback = async (ctx, args, map, cmd) =>
async Task<IResult> ExecuteCallback(ICommandContext context, object[] args, IServiceProvider services, CommandInfo cmd)
{
var instance = createInstance(map);
instance.SetContext(ctx);
var instance = createInstance(services);
instance.SetContext(context);
try
{
instance.BeforeExecute(cmd);
var task = method.Invoke(instance, args) as Task ?? Task.Delay(0);
await task.ConfigureAwait(false);
if (task is Task<RuntimeResult> resultTask)
{
return await resultTask.ConfigureAwait(false);
}
else
{
await task.ConfigureAwait(false);
return ExecuteResult.FromSuccess();
}
}
finally
{
instance.AfterExecute(cmd);
(instance as IDisposable)?.Dispose();
}
};
}
builder.Callback = ExecuteCallback;
}
private static void BuildParameter(ParameterBuilder builder, System.Reflection.ParameterInfo paramInfo, int position, int count, CommandService service)
@@ -293,7 +305,7 @@ namespace Discord.Commands
private static bool IsValidCommandDefinition(MethodInfo methodInfo)
{
return methodInfo.IsDefined(typeof(CommandAttribute)) &&
(methodInfo.ReturnType == typeof(Task) || methodInfo.ReturnType == typeof(void)) &&
(methodInfo.ReturnType == typeof(Task) || methodInfo.ReturnType == typeof(Task<RuntimeResult>)) &&
!methodInfo.IsStatic &&
!methodInfo.IsGenericMethod;
}