@@ -257,8 +257,10 @@ namespace Discord.Interactions.Builders | |||||
var parameters = methodInfo.GetParameters(); | var parameters = methodInfo.GetParameters(); | ||||
var wildCardCount = Regex.Matches(Regex.Escape(builder.Name), Regex.Escape(commandService._wildCardExp)).Count; | |||||
foreach (var parameter in parameters) | foreach (var parameter in parameters) | ||||
builder.AddParameter(x => BuildParameter(x, parameter)); | |||||
builder.AddParameter(x => BuildComponentParameter(x, parameter, parameter.Position >= wildCardCount)); | |||||
builder.Callback = CreateCallback(createInstance, methodInfo, commandService); | builder.Callback = CreateCallback(createInstance, methodInfo, commandService); | ||||
} | } | ||||
@@ -442,10 +444,11 @@ namespace Discord.Interactions.Builders | |||||
builder.Name = Regex.Replace(builder.Name, "(?<=[a-z])(?=[A-Z])", "-").ToLower(); | builder.Name = Regex.Replace(builder.Name, "(?<=[a-z])(?=[A-Z])", "-").ToLower(); | ||||
} | } | ||||
private static void BuildComponentParameter(ComponentCommandParameterBuilder builder, ParameterInfo paramInfo) | |||||
private static void BuildComponentParameter(ComponentCommandParameterBuilder builder, ParameterInfo paramInfo, bool isComponentParam) | |||||
{ | { | ||||
BuildParameter(builder, paramInfo); | |||||
builder.SetAsRouteSegment(!isComponentParam); | |||||
BuildParameter(builder, paramInfo); | |||||
} | } | ||||
private static void BuildParameter<TInfo, TBuilder> (ParameterBuilder<TInfo, TBuilder> builder, ParameterInfo paramInfo) | private static void BuildParameter<TInfo, TBuilder> (ParameterBuilder<TInfo, TBuilder> builder, ParameterInfo paramInfo) | ||||
@@ -5,6 +5,8 @@ namespace Discord.Interactions.Builders | |||||
public class ComponentCommandParameterBuilder : ParameterBuilder<ComponentCommandParameterInfo, ComponentCommandParameterBuilder> | public class ComponentCommandParameterBuilder : ParameterBuilder<ComponentCommandParameterInfo, ComponentCommandParameterBuilder> | ||||
{ | { | ||||
public ComponentTypeConverter TypeConverter { get; private set; } | public ComponentTypeConverter TypeConverter { get; private set; } | ||||
public TypeReader TypeReader { get; private set; } | |||||
public bool IsRouteSegmentParameter { get; private set; } | |||||
protected override ComponentCommandParameterBuilder Instance => this; | protected override ComponentCommandParameterBuilder Instance => this; | ||||
public ComponentCommandParameterBuilder(ICommandBuilder command) : base(command) { } | public ComponentCommandParameterBuilder(ICommandBuilder command) : base(command) { } | ||||
@@ -25,7 +27,18 @@ namespace Discord.Interactions.Builders | |||||
public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services = null) | public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services = null) | ||||
{ | { | ||||
base.SetParameterType(type); | base.SetParameterType(type); | ||||
TypeConverter = Command.Module.InteractionService.GetComponentTypeConverter(ParameterType, services); | |||||
if (IsRouteSegmentParameter) | |||||
TypeReader = Command.Module.InteractionService.GetTypeReader(type); | |||||
else | |||||
TypeConverter = Command.Module.InteractionService.GetComponentTypeConverter(ParameterType, services); | |||||
return this; | |||||
} | |||||
public ComponentCommandParameterBuilder SetAsRouteSegment(bool isRouteSegment) | |||||
{ | |||||
IsRouteSegmentParameter = isRouteSegment; | |||||
return this; | return this; | ||||
} | } | ||||
@@ -62,19 +62,47 @@ namespace Discord.Interactions | |||||
{ | { | ||||
var parameter = Parameters.ElementAt(i); | var parameter = Parameters.ElementAt(i); | ||||
if (i <= captureCount) | |||||
args[i] = wildcardCaptures.ElementAt(i); | |||||
if (i < captureCount) | |||||
{ | |||||
if(parameter.IsRouteSegmentParameter) | |||||
{ | |||||
var readResult = await parameter.TypeReader.ReadAsync(context, wildcardCaptures.ElementAt(i), services).ConfigureAwait(false); | |||||
if(!readResult.IsSuccess) | |||||
{ | |||||
await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
return readResult; | |||||
} | |||||
args[i] = readResult.Value; | |||||
} | |||||
else | |||||
{ | |||||
var result = ExecuteResult.FromError(InteractionCommandError.BadArgs, $"Expected Wild Card Capture, got component value"); | |||||
await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
return result; | |||||
} | |||||
} | |||||
else | else | ||||
{ | { | ||||
var readResult = await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false); | |||||
if(!parameter.IsRouteSegmentParameter) | |||||
{ | |||||
var readResult = await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false); | |||||
if (!readResult.IsSuccess) | |||||
{ | |||||
await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
return readResult; | |||||
} | |||||
if(!readResult.IsSuccess) | |||||
args[i] = readResult.Value; | |||||
} | |||||
else | |||||
{ | { | ||||
await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
return readResult; | |||||
var result = ExecuteResult.FromError(InteractionCommandError.BadArgs, $"Expected component value, got Wild Card capture."); | |||||
await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
return result; | |||||
} | } | ||||
args[i] = readResult.Value; | |||||
} | } | ||||
} | } | ||||
@@ -94,7 +122,7 @@ namespace Discord.Interactions | |||||
{ | { | ||||
var parameter = paramList.ElementAt(i); | var parameter = paramList.ElementAt(i); | ||||
if (argList?.ElementAt(i) == null) | |||||
if (argList?.ElementAt(i) is null) | |||||
{ | { | ||||
if (!parameter.IsRequired) | if (!parameter.IsRequired) | ||||
result[i] = parameter.DefaultValue; | result[i] = parameter.DefaultValue; | ||||
@@ -13,10 +13,14 @@ namespace Discord.Interactions | |||||
/// <see cref="CommandParameterInfo.ParameterType"/>. | /// <see cref="CommandParameterInfo.ParameterType"/>. | ||||
/// </summary> | /// </summary> | ||||
public ComponentTypeConverter TypeConverter { get; } | public ComponentTypeConverter TypeConverter { get; } | ||||
public TypeReader TypeReader { get; } | |||||
public bool IsRouteSegmentParameter { get; } | |||||
internal ComponentCommandParameterInfo(ComponentCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | internal ComponentCommandParameterInfo(ComponentCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | ||||
{ | { | ||||
TypeConverter = builder.TypeConverter; | TypeConverter = builder.TypeConverter; | ||||
TypeReader = builder.TypeReader; | |||||
IsRouteSegmentParameter = builder.IsRouteSegmentParameter; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -199,8 +199,7 @@ namespace Discord.Interactions | |||||
_compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new Dictionary<Type, ComponentTypeConverter>(), | _compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new Dictionary<Type, ComponentTypeConverter>(), | ||||
new Dictionary<Type, Type> | new Dictionary<Type, Type> | ||||
{ | { | ||||
[typeof(Array)] = typeof(DefaultArrayComponentConverter<>), | |||||
[typeof(string)] = typeof(DefaultValueConverter<>) | |||||
[typeof(Array)] = typeof(DefaultArrayComponentConverter<>) | |||||
}); | }); | ||||
_typeReaderMap = new TypeMap<TypeReader, string>(this, new Dictionary<Type, TypeReader>(), | _typeReaderMap = new TypeMap<TypeReader, string>(this, new Dictionary<Type, TypeReader>(), | ||||
@@ -31,7 +31,7 @@ namespace Discord.Interactions | |||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the string expression that will be treated as a wild card. | /// Gets or sets the string expression that will be treated as a wild card. | ||||
/// </summary> | /// </summary> | ||||
public string WildCardExpression { get; set; } | |||||
public string WildCardExpression { get; set; } = "*"; | |||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the option to use compiled lambda expressions to create module instances and execute commands. This method improves performance at the cost of memory. | /// Gets or sets the option to use compiled lambda expressions to create module instances and execute commands. This method improves performance at the cost of memory. | ||||
@@ -36,7 +36,12 @@ namespace Discord.Interactions | |||||
results.Add(result); | results.Add(result); | ||||
} | } | ||||
return TypeConverterResult.FromSuccess(results.Select(x => x.Value).ToArray()); | |||||
var destination = Array.CreateInstance(_underlyingType, results.Count); | |||||
for (var i = 0; i < results.Count; i++) | |||||
destination.SetValue(results[i].Value, i); | |||||
return TypeConverterResult.FromSuccess(destination); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,17 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Interactions.TypeConverters | |||||
{ | |||||
internal sealed class DefaultValueComponentConverter<T> : ComponentTypeConverter<T> | |||||
where T : class, IConvertible | |||||
{ | |||||
public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services) | |||||
{ | |||||
return Task.FromResult(TypeConverterResult.FromSuccess(Convert.ChangeType(option.Values, TypeCode.Object))); | |||||
} | |||||
} | |||||
} |
@@ -4,7 +4,7 @@ using System.Threading.Tasks; | |||||
namespace Discord.Interactions | namespace Discord.Interactions | ||||
{ | { | ||||
internal sealed class DefaultValueReader<T> : TypeReader<T> | internal sealed class DefaultValueReader<T> : TypeReader<T> | ||||
where T : class, IConvertible | |||||
where T : IConvertible | |||||
{ | { | ||||
public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services) | public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services) | ||||
{ | { | ||||