@@ -0,0 +1,22 @@ | |||||
using System; | |||||
using System.Reflection; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Parameter)] | |||||
public class OverrideTypeReaderAttribute : Attribute | |||||
{ | |||||
private readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); | |||||
public Type TypeReader { get; } | |||||
public OverrideTypeReaderAttribute(Type overridenType) | |||||
{ | |||||
if (!_typeReaderTypeInfo.IsAssignableFrom(overridenType.GetTypeInfo())) | |||||
throw new ArgumentException($"{nameof(overridenType)} must inherit from {nameof(TypeReader)}"); | |||||
TypeReader = overridenType; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] | |||||
public abstract class ParameterPreconditionAttribute : Attribute | |||||
{ | |||||
public abstract Task<PreconditionResult> CheckPermissions(CommandContext context, ParameterInfo parameter, object value); | |||||
} | |||||
} |
@@ -182,6 +182,10 @@ namespace Discord.Commands | |||||
// TODO: C#7 type switch | // TODO: C#7 type switch | ||||
if (attribute is SummaryAttribute) | if (attribute is SummaryAttribute) | ||||
builder.Summary = (attribute as SummaryAttribute).Text; | builder.Summary = (attribute as SummaryAttribute).Text; | ||||
else if (attribute is OverrideTypeReaderAttribute) | |||||
builder.TypeReader = service.GetTypeReader((attribute as OverrideTypeReaderAttribute).TypeReader); | |||||
else if (attribute is ParameterPreconditionAttribute) | |||||
builder.AddPrecondition(attribute as ParameterPreconditionAttribute); | |||||
else if (attribute is ParamArrayAttribute) | else if (attribute is ParamArrayAttribute) | ||||
{ | { | ||||
builder.IsMultiple = true; | builder.IsMultiple = true; | ||||
@@ -1,10 +1,14 @@ | |||||
using System; | using System; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Collections.Generic; | |||||
namespace Discord.Commands.Builders | namespace Discord.Commands.Builders | ||||
{ | { | ||||
public class ParameterBuilder | public class ParameterBuilder | ||||
{ | { | ||||
private readonly List<ParameterPreconditionAttribute> _preconditions; | |||||
public CommandBuilder Command { get; } | public CommandBuilder Command { get; } | ||||
public string Name { get; internal set; } | public string Name { get; internal set; } | ||||
public Type ParameterType { get; internal set; } | public Type ParameterType { get; internal set; } | ||||
@@ -16,16 +20,20 @@ namespace Discord.Commands.Builders | |||||
public object DefaultValue { get; set; } | public object DefaultValue { get; set; } | ||||
public string Summary { get; set; } | public string Summary { get; set; } | ||||
public IReadOnlyList<ParameterPreconditionAttribute> Preconditions => _preconditions; | |||||
//Automatic | //Automatic | ||||
internal ParameterBuilder(CommandBuilder command) | internal ParameterBuilder(CommandBuilder command) | ||||
{ | { | ||||
_preconditions = new List<ParameterPreconditionAttribute>(); | |||||
Command = command; | Command = command; | ||||
} | } | ||||
//User-defined | //User-defined | ||||
internal ParameterBuilder(CommandBuilder command, string name, Type type) | internal ParameterBuilder(CommandBuilder command, string name, Type type) | ||||
: this(command) | : this(command) | ||||
{ | { | ||||
Preconditions.NotNull(name, nameof(name)); | |||||
Discord.Preconditions.NotNull(name, nameof(name)); | |||||
Name = name; | Name = name; | ||||
SetType(type); | SetType(type); | ||||
@@ -49,7 +57,7 @@ namespace Discord.Commands.Builders | |||||
} | } | ||||
public ParameterBuilder WithDefault(object defaultValue) | public ParameterBuilder WithDefault(object defaultValue) | ||||
{ | { | ||||
DefaultValue = defaultValue; | |||||
DefaultValue = defaultValue; | |||||
return this; | return this; | ||||
} | } | ||||
public ParameterBuilder WithIsOptional(bool isOptional) | public ParameterBuilder WithIsOptional(bool isOptional) | ||||
@@ -68,6 +76,12 @@ namespace Discord.Commands.Builders | |||||
return this; | return this; | ||||
} | } | ||||
public ParameterBuilder AddPrecondition(ParameterPreconditionAttribute precondition) | |||||
{ | |||||
_preconditions.Add(precondition); | |||||
return this; | |||||
} | |||||
internal ParameterInfo Build(CommandInfo info) | internal ParameterInfo Build(CommandInfo info) | ||||
{ | { | ||||
if (TypeReader == null) | if (TypeReader == null) | ||||
@@ -135,9 +135,26 @@ namespace Discord.Commands | |||||
if (map == null) | if (map == null) | ||||
map = DependencyMap.Empty; | map = DependencyMap.Empty; | ||||
object[] args = null; | |||||
try | |||||
{ | |||||
args = GenerateArgs(argList, paramList); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
return ExecuteResult.FromError(ex); | |||||
} | |||||
foreach (var parameter in Parameters) | |||||
{ | |||||
var result = await parameter.CheckPreconditionsAsync(context, args, map).ConfigureAwait(false); | |||||
if (!result.IsSuccess) | |||||
return ExecuteResult.FromError(result); | |||||
} | |||||
try | try | ||||
{ | { | ||||
var args = GenerateArgs(argList, paramList); | |||||
switch (RunMode) | switch (RunMode) | ||||
{ | { | ||||
case RunMode.Sync: //Always sync | case RunMode.Sync: //Always sync | ||||
@@ -1,5 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Collections.Generic; | |||||
using System.Collections.Immutable; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Discord.Commands.Builders; | using Discord.Commands.Builders; | ||||
@@ -10,6 +12,17 @@ namespace Discord.Commands | |||||
{ | { | ||||
private readonly TypeReader _reader; | private readonly TypeReader _reader; | ||||
public CommandInfo Command { get; } | |||||
public string Name { get; } | |||||
public string Summary { get; } | |||||
public bool IsOptional { get; } | |||||
public bool IsRemainder { get; } | |||||
public bool IsMultiple { get; } | |||||
public Type Type { get; } | |||||
public object DefaultValue { get; } | |||||
public IReadOnlyList<ParameterPreconditionAttribute> Preconditions { get; } | |||||
internal ParameterInfo(ParameterBuilder builder, CommandInfo command, CommandService service) | internal ParameterInfo(ParameterBuilder builder, CommandInfo command, CommandService service) | ||||
{ | { | ||||
Command = command; | Command = command; | ||||
@@ -23,17 +36,30 @@ namespace Discord.Commands | |||||
Type = builder.ParameterType; | Type = builder.ParameterType; | ||||
DefaultValue = builder.DefaultValue; | DefaultValue = builder.DefaultValue; | ||||
Preconditions = builder.Preconditions.ToImmutableArray(); | |||||
_reader = builder.TypeReader; | _reader = builder.TypeReader; | ||||
} | } | ||||
public CommandInfo Command { get; } | |||||
public string Name { get; } | |||||
public string Summary { get; } | |||||
public bool IsOptional { get; } | |||||
public bool IsRemainder { get; } | |||||
public bool IsMultiple { get; } | |||||
public Type Type { get; } | |||||
public object DefaultValue { get; } | |||||
public async Task<PreconditionResult> CheckPreconditionsAsync(CommandContext context, object[] args, IDependencyMap map = null) | |||||
{ | |||||
if (map == null) | |||||
map = DependencyMap.Empty; | |||||
int position = 0; | |||||
for(position = 0; position < Command.Parameters.Count; position++) | |||||
if (Command.Parameters[position] == this) | |||||
break; | |||||
foreach (var precondition in Preconditions) | |||||
{ | |||||
var result = await precondition.CheckPermissions(context, this, args[position]).ConfigureAwait(false); | |||||
if (!result.IsSuccess) | |||||
return result; | |||||
} | |||||
return PreconditionResult.FromSuccess(); | |||||
} | |||||
public async Task<TypeReaderResult> Parse(CommandContext context, string input) | public async Task<TypeReaderResult> Parse(CommandContext context, string input) | ||||
{ | { | ||||