@@ -38,6 +38,9 @@ namespace Discord.Interactions.Builders | |||||
/// </summary> | /// </summary> | ||||
Type Type { get; } | Type Type { get; } | ||||
/// <summary> | |||||
/// Get the <see cref="ComponentTypeConverter"/> assigned to this input. | |||||
/// </summary> | |||||
ComponentTypeConverter TypeConverter { get; } | ComponentTypeConverter TypeConverter { get; } | ||||
/// <summary> | /// <summary> | ||||
@@ -33,6 +33,7 @@ namespace Discord.Interactions.Builders | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public Type Type { get; private set; } | public Type Type { get; private set; } | ||||
/// <inheritdoc/> | |||||
public ComponentTypeConverter TypeConverter { get; private set; } | public ComponentTypeConverter TypeConverter { get; private set; } | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
@@ -446,8 +446,7 @@ namespace Discord.Interactions.Builders | |||||
private static void BuildComponentParameter(ComponentCommandParameterBuilder builder, ParameterInfo paramInfo, bool isComponentParam) | private static void BuildComponentParameter(ComponentCommandParameterBuilder builder, ParameterInfo paramInfo, bool isComponentParam) | ||||
{ | { | ||||
builder.SetAsRouteSegment(!isComponentParam); | |||||
builder.SetIsRouteSegment(!isComponentParam); | |||||
BuildParameter(builder, paramInfo); | BuildParameter(builder, paramInfo); | ||||
} | } | ||||
@@ -4,13 +4,32 @@ namespace Discord.Interactions.Builders | |||||
{ | { | ||||
public class ComponentCommandParameterBuilder : ParameterBuilder<ComponentCommandParameterInfo, ComponentCommandParameterBuilder> | public class ComponentCommandParameterBuilder : ParameterBuilder<ComponentCommandParameterInfo, ComponentCommandParameterBuilder> | ||||
{ | { | ||||
/// <summary> | |||||
/// Get the <see cref="ComponentTypeConverter"/> assigned to this parameter, if <see cref="IsRouteSegmentParameter"/> is <see langword="false"/>. | |||||
/// </summary> | |||||
public ComponentTypeConverter TypeConverter { get; private set; } | public ComponentTypeConverter TypeConverter { get; private set; } | ||||
/// <summary> | |||||
/// Get the <see cref="Discord.Interactions.TypeReader"/> assigned to this parameter, if <see cref="IsRouteSegmentParameter"/> is <see langword="true"/>. | |||||
/// </summary> | |||||
public TypeReader TypeReader { get; private set; } | public TypeReader TypeReader { get; private set; } | ||||
/// <summary> | |||||
/// Gets whether this parameter is a CustomId segment or a Component value parameter. | |||||
/// </summary> | |||||
public bool IsRouteSegmentParameter { get; private set; } | public bool IsRouteSegmentParameter { get; private set; } | ||||
/// <inheritdoc/> | |||||
protected override ComponentCommandParameterBuilder Instance => this; | protected override ComponentCommandParameterBuilder Instance => this; | ||||
public ComponentCommandParameterBuilder(ICommandBuilder command) : base(command) { } | |||||
internal ComponentCommandParameterBuilder(ICommandBuilder command) : base(command) { } | |||||
/// <summary> | |||||
/// Initializes a new <see cref="ComponentCommandParameterBuilder"/>. | |||||
/// </summary> | |||||
/// <param name="command">Parent command of this parameter.</param> | |||||
/// <param name="name">Name of this command.</param> | |||||
/// <param name="type">Type of this parameter.</param> | |||||
public ComponentCommandParameterBuilder(ICommandBuilder command, string name, Type type) : base(command, name, type) { } | public ComponentCommandParameterBuilder(ICommandBuilder command, string name, Type type) : base(command, name, type) { } | ||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
@@ -36,7 +55,14 @@ namespace Discord.Interactions.Builders | |||||
return this; | return this; | ||||
} | } | ||||
public ComponentCommandParameterBuilder SetAsRouteSegment(bool isRouteSegment) | |||||
/// <summary> | |||||
/// Sets <see cref="IsRouteSegmentParameter"/>. | |||||
/// </summary> | |||||
/// <param name="isRouteSegment">New value of the <see cref="IsRouteSegmentParameter"/>.</param> | |||||
/// <returns> | |||||
/// The builder instance. | |||||
/// </returns> | |||||
public ComponentCommandParameterBuilder SetIsRouteSegment(bool isRouteSegment) | |||||
{ | { | ||||
IsRouteSegmentParameter = isRouteSegment; | IsRouteSegmentParameter = isRouteSegment; | ||||
return this; | return this; | ||||
@@ -52,15 +52,18 @@ namespace Discord.Interactions | |||||
if (additionalArgs is not null) | if (additionalArgs is not null) | ||||
args.AddRange(additionalArgs); | args.AddRange(additionalArgs); | ||||
var modalResult = await Modal.ParseModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | |||||
var modalResult = await Modal.CreateModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | |||||
if(!modalResult.IsSuccess || modalResult is not ParseResult parseResult) | |||||
if(!modalResult.IsSuccess) | |||||
{ | { | ||||
await InvokeModuleEvent(context, modalResult).ConfigureAwait(false); | await InvokeModuleEvent(context, modalResult).ConfigureAwait(false); | ||||
return modalResult; | return modalResult; | ||||
} | } | ||||
args.Add(parseResult.Value); | |||||
if(modalResult is ParseResult parseResult) | |||||
args.Add(parseResult.Value); | |||||
else | |||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."); | |||||
return await RunAsync(context, args.ToArray(), services); | return await RunAsync(context, args.ToArray(), services); | ||||
} | } | ||||
@@ -39,6 +39,9 @@ namespace Discord.Interactions | |||||
/// </summary> | /// </summary> | ||||
public Type Type { get; } | public Type Type { get; } | ||||
/// <summary> | |||||
/// Gets the <see cref="ComponentTypeConverter"/> assigned to this component. | |||||
/// </summary> | |||||
public ComponentTypeConverter TypeConverter { get; } | public ComponentTypeConverter TypeConverter { get; } | ||||
/// <summary> | /// <summary> | ||||
@@ -62,10 +62,11 @@ namespace Discord.Interactions | |||||
/// <summary> | /// <summary> | ||||
/// Creates an <see cref="IModal"/> and fills it with provided message components. | /// Creates an <see cref="IModal"/> and fills it with provided message components. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="components"><see cref="IModalInteraction"/> that will be injected into the modal.</param> | |||||
/// <param name="modalInteraction"><see cref="IModalInteraction"/> that will be injected into the modal.</param> | |||||
/// <returns> | /// <returns> | ||||
/// A <see cref="IModal"/> filled with the provided components. | /// A <see cref="IModal"/> filled with the provided components. | ||||
/// </returns> | /// </returns> | ||||
[Obsolete("This method is no longer supported with the introduction of Component TypeConverters, please use the CreateModalAsync method.")] | |||||
public IModal CreateModal(IModalInteraction modalInteraction, bool throwOnMissingField = false) | public IModal CreateModal(IModalInteraction modalInteraction, bool throwOnMissingField = false) | ||||
{ | { | ||||
var args = new object[Components.Count]; | var args = new object[Components.Count]; | ||||
@@ -90,10 +91,19 @@ namespace Discord.Interactions | |||||
return _initializer(args); | return _initializer(args); | ||||
} | } | ||||
internal async Task<IResult> ParseModalAsync(IInteractionContext context, IServiceProvider services = null, bool throwOnMissingField = false) | |||||
/// <summary> | |||||
/// Creates an <see cref="IModal"/> and fills it with provided message components. | |||||
/// </summary> | |||||
/// <param name="context">Context of the <see cref="IModalInteraction"/> that will be injected into the modal.</param> | |||||
/// <param name="services">Services to be passed onto the <see cref="ComponentTypeConverter"/>s of the modal fiels.</param> | |||||
/// <param name="throwOnMissingField">Wheter or not this method should exit on encountering a missing modal field.</param> | |||||
/// <returns> | |||||
/// A <see cref="TypeConverterResult"/> if a type conversion has failed, else a <see cref="ParseResult"/>. | |||||
/// </returns> | |||||
public async Task<IResult> CreateModalAsync(IInteractionContext context, IServiceProvider services = null, bool throwOnMissingField = false) | |||||
{ | { | ||||
if (context.Interaction is not IModalInteraction modalInteraction) | if (context.Interaction is not IModalInteraction modalInteraction) | ||||
throw new InvalidOperationException("Provided context doesn't belong to a Modal Interaction."); | |||||
return ParseResult.FromError(InteractionCommandError.Unsuccessful, "Provided context doesn't belong to a Modal Interaction."); | |||||
services ??= EmptyServiceProvider.Instance; | services ??= EmptyServiceProvider.Instance; | ||||
@@ -110,7 +120,7 @@ namespace Discord.Interactions | |||||
if (!throwOnMissingField) | if (!throwOnMissingField) | ||||
args[i] = input.DefaultValue; | args[i] = input.DefaultValue; | ||||
else | else | ||||
throw new InvalidOperationException($"Modal interaction is missing the required field: {input.CustomId}"); | |||||
return ParseResult.FromError(InteractionCommandError.BadArgs, $"Modal interaction is missing the required field: {input.CustomId}"); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.Interactions.Builders; | using Discord.Interactions.Builders; | ||||
using System; | |||||
namespace Discord.Interactions | namespace Discord.Interactions | ||||
{ | { | ||||
@@ -10,10 +9,19 @@ namespace Discord.Interactions | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Gets the <see cref="ComponentTypeConverter"/> that will be used to convert a message component value into | /// Gets the <see cref="ComponentTypeConverter"/> that will be used to convert a message component value into | ||||
/// <see cref="CommandParameterInfo.ParameterType"/>. | |||||
/// <see cref="CommandParameterInfo.ParameterType"/>, if <see cref="IsRouteSegmentParameter"/> is false. | |||||
/// </summary> | /// </summary> | ||||
public ComponentTypeConverter TypeConverter { get; } | public ComponentTypeConverter TypeConverter { get; } | ||||
/// <summary> | |||||
/// Gets the <see cref="TypeReader"/> that will be used to convert a CustomId segment value into | |||||
/// <see cref="CommandParameterInfo.ParameterType"/>, if <see cref="IsRouteSegmentParameter"/> is <see langword="true"/>. | |||||
/// </summary> | |||||
public TypeReader TypeReader { get; } | public TypeReader TypeReader { get; } | ||||
/// <summary> | |||||
/// Gets whether this parameter is a CustomId segment or a component value parameter. | |||||
/// </summary> | |||||
public bool IsRouteSegmentParameter { get; } | public bool IsRouteSegmentParameter { get; } | ||||
internal ComponentCommandParameterInfo(ComponentCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | internal ComponentCommandParameterInfo(ComponentCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | ||||
@@ -181,30 +181,30 @@ namespace Discord.Interactions | |||||
_autoServiceScopes = config.AutoServiceScopes; | _autoServiceScopes = config.AutoServiceScopes; | ||||
_restResponseCallback = config.RestResponseCallback; | _restResponseCallback = config.RestResponseCallback; | ||||
_typeConverterMap = new TypeMap<TypeConverter, IApplicationCommandInteractionDataOption>(this, new Dictionary<Type, TypeConverter> | |||||
{ | |||||
[typeof(TimeSpan)] = new TimeSpanConverter() | |||||
}, new Dictionary<Type, Type> | |||||
{ | |||||
[typeof(IChannel)] = typeof(DefaultChannelConverter<>), | |||||
[typeof(IRole)] = typeof(DefaultRoleConverter<>), | |||||
[typeof(IAttachment)] = typeof(DefaultAttachmentConverter<>), | |||||
[typeof(IUser)] = typeof(DefaultUserConverter<>), | |||||
[typeof(IMentionable)] = typeof(DefaultMentionableConverter<>), | |||||
[typeof(IConvertible)] = typeof(DefaultValueConverter<>), | |||||
[typeof(Enum)] = typeof(EnumConverter<>), | |||||
[typeof(Nullable<>)] = typeof(NullableConverter<>), | |||||
}); | |||||
_compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new Dictionary<Type, ComponentTypeConverter>(), | |||||
new Dictionary<Type, Type> | |||||
_typeConverterMap = new TypeMap<TypeConverter, IApplicationCommandInteractionDataOption>(this, new ConcurrentDictionary<Type, TypeConverter> | |||||
{ | |||||
[typeof(TimeSpan)] = new TimeSpanConverter() | |||||
}, new ConcurrentDictionary<Type, Type> | |||||
{ | |||||
[typeof(IChannel)] = typeof(DefaultChannelConverter<>), | |||||
[typeof(IRole)] = typeof(DefaultRoleConverter<>), | |||||
[typeof(IAttachment)] = typeof(DefaultAttachmentConverter<>), | |||||
[typeof(IUser)] = typeof(DefaultUserConverter<>), | |||||
[typeof(IMentionable)] = typeof(DefaultMentionableConverter<>), | |||||
[typeof(IConvertible)] = typeof(DefaultValueConverter<>), | |||||
[typeof(Enum)] = typeof(EnumConverter<>), | |||||
[typeof(Nullable<>)] = typeof(NullableConverter<>), | |||||
}); | |||||
_compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new ConcurrentDictionary<Type, ComponentTypeConverter>(), | |||||
new ConcurrentDictionary<Type, Type> | |||||
{ | { | ||||
[typeof(Array)] = typeof(DefaultArrayComponentConverter<>), | [typeof(Array)] = typeof(DefaultArrayComponentConverter<>), | ||||
[typeof(IConvertible)] = typeof(DefaultValueComponentConverter<>) | [typeof(IConvertible)] = typeof(DefaultValueComponentConverter<>) | ||||
}); | }); | ||||
_typeReaderMap = new TypeMap<TypeReader, string>(this, new Dictionary<Type, TypeReader>(), | |||||
new Dictionary<Type, Type> | |||||
_typeReaderMap = new TypeMap<TypeReader, string>(this, new ConcurrentDictionary<Type, TypeReader>(), | |||||
new ConcurrentDictionary<Type, Type> | |||||
{ | { | ||||
[typeof(IChannel)] = typeof(DefaultChannelReader<>), | [typeof(IChannel)] = typeof(DefaultChannelReader<>), | ||||
[typeof(IRole)] = typeof(DefaultRoleReader<>), | [typeof(IRole)] = typeof(DefaultRoleReader<>), | ||||
@@ -827,56 +827,84 @@ namespace Discord.Interactions | |||||
_compTypeConverterMap.Get(type, services); | _compTypeConverterMap.Get(type, services); | ||||
/// <summary> | /// <summary> | ||||
/// Add a concrete type <see cref="TypeReader"/>. | |||||
/// Add a concrete type <see cref="ComponentTypeConverter"/>. | |||||
/// </summary> | /// </summary> | ||||
/// <typeparam name="T">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</typeparam> | |||||
/// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
/// <typeparam name="T">Primary target <see cref="Type"/> of the <see cref="ComponentTypeConverter"/>.</typeparam> | |||||
/// <param name="converter">The <see cref="ComponentTypeConverter"/> instance.</param> | |||||
public void AddComponentTypeConverter<T>(ComponentTypeConverter converter) => | public void AddComponentTypeConverter<T>(ComponentTypeConverter converter) => | ||||
AddComponentTypeConverter(typeof(T), converter); | AddComponentTypeConverter(typeof(T), converter); | ||||
/// <summary> | /// <summary> | ||||
/// Add a concrete type <see cref="TypeReader"/>. | |||||
/// Add a concrete type <see cref="ComponentTypeConverter"/>. | |||||
/// </summary> | /// </summary> | ||||
/// <param name="type">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</param> | |||||
/// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
/// <param name="type">Primary target <see cref="Type"/> of the <see cref="ComponentTypeConverter"/>.</param> | |||||
/// <param name="converter">The <see cref="ComponentTypeConverter"/> instance.</param> | |||||
public void AddComponentTypeConverter(Type type, ComponentTypeConverter converter) => | public void AddComponentTypeConverter(Type type, ComponentTypeConverter converter) => | ||||
_compTypeConverterMap.AddConcrete(type, converter); | _compTypeConverterMap.AddConcrete(type, converter); | ||||
/// <summary> | /// <summary> | ||||
/// Add a generic type <see cref="CompTypeConverter{T}"/>. | |||||
/// Add a generic type <see cref="ComponentTypeConverter{T}"/>. | |||||
/// </summary> | /// </summary> | ||||
/// <typeparam name="T">Generic Type constraint of the <see cref="Type"/> of the <see cref="CompTypeConverter{T}"/>.</typeparam> | |||||
/// <param name="converterType">Type of the <see cref="CompTypeConverter{T}"/>.</param> | |||||
/// <typeparam name="T">Generic Type constraint of the <see cref="Type"/> of the <see cref="ComponentTypeConverter{T}"/>.</typeparam> | |||||
/// <param name="converterType">Type of the <see cref="ComponentTypeConverter{T}"/>.</param> | |||||
public void AddGenericComponentTypeConverter<T>(Type converterType) => | public void AddGenericComponentTypeConverter<T>(Type converterType) => | ||||
AddGenericComponentTypeConverter(typeof(T), converterType); | AddGenericComponentTypeConverter(typeof(T), converterType); | ||||
/// <summary> | /// <summary> | ||||
/// Add a generic type <see cref="CompTypeConverter{T}"/>. | |||||
/// Add a generic type <see cref="ComponentTypeConverter{T}"/>. | |||||
/// </summary> | /// </summary> | ||||
/// <param name="targetType">Generic Type constraint of the <see cref="Type"/> of the <see cref="CompTypeConverter{T}"/>.</param> | |||||
/// <param name="converterType">Type of the <see cref="CompTypeConverter{T}"/>.</param> | |||||
/// <param name="targetType">Generic Type constraint of the <see cref="Type"/> of the <see cref="ComponentTypeConverter{T}"/>.</param> | |||||
/// <param name="converterType">Type of the <see cref="ComponentTypeConverter{T}"/>.</param> | |||||
public void AddGenericComponentTypeConverter(Type targetType, Type converterType) => | public void AddGenericComponentTypeConverter(Type targetType, Type converterType) => | ||||
_compTypeConverterMap.AddGeneric(targetType, converterType); | _compTypeConverterMap.AddGeneric(targetType, converterType); | ||||
public Task<string> SerializeValue<T>(T obj, IServiceProvider services = null) => | |||||
_compTypeConverterMap.Get(typeof(T), services).SerializeAsync(obj); | |||||
internal TypeReader GetTypeReader(Type type, IServiceProvider services = null) => | internal TypeReader GetTypeReader(Type type, IServiceProvider services = null) => | ||||
_typeReaderMap.Get(type, services); | _typeReaderMap.Get(type, services); | ||||
/// <summary> | |||||
/// Add a concrete type <see cref="TypeReader"/>. | |||||
/// </summary> | |||||
/// <typeparam name="T">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</typeparam> | |||||
/// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
public void AddTypeReader<T>(TypeReader reader) => | public void AddTypeReader<T>(TypeReader reader) => | ||||
AddTypeReader(typeof(T), reader); | AddTypeReader(typeof(T), reader); | ||||
/// <summary> | |||||
/// Add a concrete type <see cref="TypeReader"/>. | |||||
/// </summary> | |||||
/// <param name="type">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</param> | |||||
/// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
public void AddTypeReader(Type type, TypeReader reader) => | public void AddTypeReader(Type type, TypeReader reader) => | ||||
_typeReaderMap.AddConcrete(type, reader); | _typeReaderMap.AddConcrete(type, reader); | ||||
/// <summary> | |||||
/// Add a generic type <see cref="TypeReader{T}"/>. | |||||
/// </summary> | |||||
/// <typeparam name="T">Generic Type constraint of the <see cref="Type"/> of the <see cref="TypeReader{T}"/>.</typeparam> | |||||
/// <param name="readerType">Type of the <see cref="TypeReader{T}"/>.</param> | |||||
public void AddGenericTypeReader<T>(Type readerType) => | public void AddGenericTypeReader<T>(Type readerType) => | ||||
AddGenericTypeReader(typeof(T), readerType); | AddGenericTypeReader(typeof(T), readerType); | ||||
/// <summary> | |||||
/// Add a generic type <see cref="TypeReader{T}"/>. | |||||
/// </summary> | |||||
/// <param name="targetType">Generic Type constraint of the <see cref="Type"/> of the <see cref="TypeReader{T}"/>.</param> | |||||
/// <param name="readerType">Type of the <see cref="TypeReader{T}"/>.</param> | |||||
public void AddGenericTypeReader(Type targetType, Type readerType) => | public void AddGenericTypeReader(Type targetType, Type readerType) => | ||||
_typeReaderMap.AddGeneric(targetType, readerType); | _typeReaderMap.AddGeneric(targetType, readerType); | ||||
/// <summary> | |||||
/// Serialize an object using a <see cref="TypeReader"/> into a <see cref="string"/> to be placed in a Component CustomId. | |||||
/// </summary> | |||||
/// <typeparam name="T">Type of the object to be serialized.</typeparam> | |||||
/// <param name="obj">Object to be serialized.</param> | |||||
/// <param name="services">Services that will be passed on to the TypeReader.</param> | |||||
/// <returns> | |||||
/// A task representing the conversion process. The task result contains the result of the conversion. | |||||
/// </returns> | |||||
public Task<string> SerializeValue<T>(T obj, IServiceProvider services = null) => | |||||
_typeReaderMap.Get(typeof(T), services).SerializeAsync(obj); | |||||
/// <summary> | /// <summary> | ||||
/// Loads and caches an <see cref="ModalInfo"/> for the provided <see cref="IModal"/>. | /// Loads and caches an <see cref="ModalInfo"/> for the provided <see cref="IModal"/>. | ||||
/// </summary> | /// </summary> | ||||
@@ -3,15 +3,36 @@ using System.Threading.Tasks; | |||||
namespace Discord.Interactions | namespace Discord.Interactions | ||||
{ | { | ||||
/// <summary> | |||||
/// Base class for creating Component TypeConverters. <see cref="InteractionService"/> uses TypeConverters to interface with Slash Command parameters. | |||||
/// </summary> | |||||
public abstract class ComponentTypeConverter : ITypeConverter<IComponentInteractionData> | public abstract class ComponentTypeConverter : ITypeConverter<IComponentInteractionData> | ||||
{ | { | ||||
/// <summary> | |||||
/// Will be used to search for alternative TypeConverters whenever the Command Service encounters an unknown parameter type. | |||||
/// </summary> | |||||
/// <param name="type">An object type.</param> | |||||
/// <returns> | |||||
/// The boolean result. | |||||
/// </returns> | |||||
public abstract bool CanConvertTo(Type type); | public abstract bool CanConvertTo(Type type); | ||||
/// <summary> | |||||
/// Will be used to read the incoming payload before executing the method body. | |||||
/// </summary> | |||||
/// <param name="context">Command exexution context.</param> | |||||
/// <param name="option">Recieved option payload.</param> | |||||
/// <param name="services">Service provider that will be used to initialize the command module.</param> | |||||
/// <returns> | |||||
/// The result of the read process. | |||||
/// </returns> | |||||
public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services); | public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services); | ||||
public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString()); | |||||
} | } | ||||
/// <inheritdoc/> | |||||
public abstract class ComponentTypeConverter<T> : ComponentTypeConverter | public abstract class ComponentTypeConverter<T> : ComponentTypeConverter | ||||
{ | { | ||||
/// <inheritdoc/> | |||||
public sealed override bool CanConvertTo(Type type) => | public sealed override bool CanConvertTo(Type type) => | ||||
typeof(T).IsAssignableFrom(type); | typeof(T).IsAssignableFrom(type); | ||||
} | } | ||||
@@ -1,7 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Collections; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord.Interactions | namespace Discord.Interactions | ||||
@@ -20,6 +20,8 @@ namespace Discord.Interactions | |||||
else | else | ||||
return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} must be a valid {typeof(T).Name} snowflake to be parsed."); | return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} must be a valid {typeof(T).Name} snowflake to be parsed."); | ||||
} | } | ||||
public override Task<string> SerializeAsync(object obj) => Task.FromResult((obj as ISnowflakeEntity)?.Id.ToString()); | |||||
} | } | ||||
internal sealed class DefaultUserReader<T> : DefaultSnowflakeReader<T> | internal sealed class DefaultUserReader<T> : DefaultSnowflakeReader<T> | ||||
@@ -1,27 +0,0 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Interactions | |||||
{ | |||||
internal sealed class EnumTypeReader<T> : TypeReader<T> | |||||
where T : struct, Enum | |||||
{ | |||||
public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services) | |||||
{ | |||||
if (Enum.TryParse<T>(option, out var result)) | |||||
return Task.FromResult(TypeConverterResult.FromSuccess(result)); | |||||
else | |||||
return Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option} cannot be converted to {nameof(T)}")); | |||||
} | |||||
public override Task<string> SerializeAsync(object obj) | |||||
{ | |||||
var name = Enum.GetName(typeof(T), obj); | |||||
if (name is null) | |||||
throw new ArgumentException($"Enum name cannot be parsed from {obj}"); | |||||
return Task.FromResult(name); | |||||
} | |||||
} | |||||
} |
@@ -3,15 +3,43 @@ using System.Threading.Tasks; | |||||
namespace Discord.Interactions | namespace Discord.Interactions | ||||
{ | { | ||||
/// <summary> | |||||
/// Base class for creating TypeConverters. <see cref="InteractionService"/> uses TypeConverters to interface with Slash Command parameters. | |||||
/// </summary> | |||||
public abstract class TypeReader : ITypeConverter<string> | public abstract class TypeReader : ITypeConverter<string> | ||||
{ | { | ||||
/// <summary> | |||||
/// Will be used to search for alternative TypeReaders whenever the Command Service encounters an unknown parameter type. | |||||
/// </summary> | |||||
/// <param name="type">An object type.</param> | |||||
/// <returns> | |||||
/// The boolean result. | |||||
/// </returns> | |||||
public abstract bool CanConvertTo(Type type); | public abstract bool CanConvertTo(Type type); | ||||
/// <summary> | |||||
/// Will be used to read the incoming payload before executing the method body. | |||||
/// </summary> | |||||
/// <param name="context">Command exexution context.</param> | |||||
/// <param name="option">Recieved option payload.</param> | |||||
/// <param name="services">Service provider that will be used to initialize the command module.</param> | |||||
/// <returns>The result of the read process.</returns> | |||||
public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services); | public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services); | ||||
/// <summary> | |||||
/// Will be used to serialize objects into strings. | |||||
/// </summary> | |||||
/// <param name="obj">Object to be serialized.</param> | |||||
/// <returns> | |||||
/// A task represting the conversion process. The result of the task contains the conversion result. | |||||
/// </returns> | |||||
public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString()); | public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString()); | ||||
} | } | ||||
/// <inheritdoc/> | |||||
public abstract class TypeReader<T> : TypeReader | public abstract class TypeReader<T> : TypeReader | ||||
{ | { | ||||
/// <inheritdoc/> | |||||
public sealed override bool CanConvertTo(Type type) => | public sealed override bool CanConvertTo(Type type) => | ||||
typeof(T).IsAssignableFrom(type); | typeof(T).IsAssignableFrom(type); | ||||
} | } | ||||