From b7a5ee6542477d3e3b1a73443ba55b1f033e7aea Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 19 Nov 2016 15:12:04 +0000 Subject: [PATCH] Parameter preconditions and typereader overriding --- .../Attributes/OverrideTypeReaderAttribute.cs | 22 ++++++++++ .../ParameterPreconditionAttribute.cs | 11 +++++ .../Builders/ModuleClassBuilder.cs | 4 ++ .../Builders/ParameterBuilder.cs | 18 +++++++- src/Discord.Net.Commands/Info/CommandInfo.cs | 19 ++++++++- .../Info/ParameterInfo.cs | 42 +++++++++++++++---- 6 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs create mode 100644 src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs new file mode 100644 index 000000000..e14be063f --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs new file mode 100644 index 000000000..f2ef78c05 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs @@ -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 CheckPermissions(CommandContext context, ParameterInfo parameter, object value); + } +} \ No newline at end of file diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index aaec43161..1775cc1fe 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -182,6 +182,10 @@ namespace Discord.Commands // TODO: C#7 type switch if (attribute is SummaryAttribute) 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) { builder.IsMultiple = true; diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs index 801a10080..6b941a1c7 100644 --- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs @@ -1,10 +1,14 @@ using System; using System.Reflection; +using System.Collections.Generic; + namespace Discord.Commands.Builders { public class ParameterBuilder { + private readonly List _preconditions; + public CommandBuilder Command { get; } public string Name { get; internal set; } public Type ParameterType { get; internal set; } @@ -16,16 +20,20 @@ namespace Discord.Commands.Builders public object DefaultValue { get; set; } public string Summary { get; set; } + public IReadOnlyList Preconditions => _preconditions; + //Automatic internal ParameterBuilder(CommandBuilder command) { + _preconditions = new List(); + Command = command; } //User-defined internal ParameterBuilder(CommandBuilder command, string name, Type type) : this(command) { - Preconditions.NotNull(name, nameof(name)); + Discord.Preconditions.NotNull(name, nameof(name)); Name = name; SetType(type); @@ -49,7 +57,7 @@ namespace Discord.Commands.Builders } public ParameterBuilder WithDefault(object defaultValue) { - DefaultValue = defaultValue; + DefaultValue = defaultValue; return this; } public ParameterBuilder WithIsOptional(bool isOptional) @@ -68,6 +76,12 @@ namespace Discord.Commands.Builders return this; } + public ParameterBuilder AddPrecondition(ParameterPreconditionAttribute precondition) + { + _preconditions.Add(precondition); + return this; + } + internal ParameterInfo Build(CommandInfo info) { if (TypeReader == null) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 571c47e13..a91a1f9f4 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -135,9 +135,26 @@ namespace Discord.Commands if (map == null) 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 { - var args = GenerateArgs(argList, paramList); switch (RunMode) { case RunMode.Sync: //Always sync diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index 18c5e653c..2ef4b89c4 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading.Tasks; using Discord.Commands.Builders; @@ -10,6 +12,17 @@ namespace Discord.Commands { 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 Preconditions { get; } + internal ParameterInfo(ParameterBuilder builder, CommandInfo command, CommandService service) { Command = command; @@ -23,17 +36,30 @@ namespace Discord.Commands Type = builder.ParameterType; DefaultValue = builder.DefaultValue; + Preconditions = builder.Preconditions.ToImmutableArray(); + _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 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 Parse(CommandContext context, string input) {