| @@ -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); | ||||
| } | } | ||||