Browse Source

add typereaders

pull/2169/head
Cenk Ergen 3 years ago
parent
commit
1468c0b435
21 changed files with 282 additions and 118 deletions
  1. +6
    -3
      src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
  2. +3
    -3
      src/Discord.Net.Interactions/Builders/Parameters/ComponentCommandParameterBuilder.cs
  3. +0
    -43
      src/Discord.Net.Interactions/ComponentTypeConverters/CompTypeConverter.cs
  4. +0
    -24
      src/Discord.Net.Interactions/Extensions/EnumExtensions.cs
  5. +10
    -31
      src/Discord.Net.Interactions/Info/Commands/ComponentCommandInfo.cs
  6. +4
    -3
      src/Discord.Net.Interactions/Info/Parameters/ComponentCommandParameterInfo.cs
  7. +40
    -11
      src/Discord.Net.Interactions/InteractionService.cs
  8. +18
    -0
      src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/ComponentTypeConverter.cs
  9. +42
    -0
      src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultArrayComponentConverter.cs
  10. +17
    -0
      src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs
  11. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs
  12. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultValueConverter.cs
  13. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/EnumConverter.cs
  14. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/NullableConverter.cs
  15. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/TimeSpanConverter.cs
  16. +0
    -0
      src/Discord.Net.Interactions/TypeConverters/SlashCommands/TypeConverter.cs
  17. +48
    -0
      src/Discord.Net.Interactions/TypeReaders/DefaultSnowflakeReader.cs
  18. +22
    -0
      src/Discord.Net.Interactions/TypeReaders/DefaultValueReader.cs
  19. +27
    -0
      src/Discord.Net.Interactions/TypeReaders/EnumReader.cs
  20. +27
    -0
      src/Discord.Net.Interactions/TypeReaders/EnumTypeReader.cs
  21. +18
    -0
      src/Discord.Net.Interactions/TypeReaders/TypeReader.cs

+ 6
- 3
src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs View File

@@ -231,9 +231,6 @@ namespace Discord.Interactions.Builders
private static void BuildComponentCommand (ComponentCommandBuilder builder, Func<IServiceProvider, IInteractionModuleBase> createInstance, MethodInfo methodInfo,
InteractionService commandService, IServiceProvider services)
{
if (!methodInfo.GetParameters().All(x => x.ParameterType == typeof(string) || x.ParameterType == typeof(string[])))
throw new InvalidOperationException($"Interaction method parameters all must be types of {typeof(string).Name} or {typeof(string[]).Name}");

var attributes = methodInfo.GetCustomAttributes();

builder.MethodName = methodInfo.Name;
@@ -445,6 +442,12 @@ namespace Discord.Interactions.Builders
builder.Name = Regex.Replace(builder.Name, "(?<=[a-z])(?=[A-Z])", "-").ToLower();
}

private static void BuildComponentParameter(ComponentCommandParameterBuilder builder, ParameterInfo paramInfo)
{
BuildParameter(builder, paramInfo);

}

private static void BuildParameter<TInfo, TBuilder> (ParameterBuilder<TInfo, TBuilder> builder, ParameterInfo paramInfo)
where TInfo : class, IParameterInfo
where TBuilder : ParameterBuilder<TInfo, TBuilder>


+ 3
- 3
src/Discord.Net.Interactions/Builders/Parameters/ComponentCommandParameterBuilder.cs View File

@@ -4,7 +4,7 @@ namespace Discord.Interactions.Builders
{
public class ComponentCommandParameterBuilder : ParameterBuilder<ComponentCommandParameterInfo, ComponentCommandParameterBuilder>
{
public CompTypeConverter TypeReader { get; private set; }
public ComponentTypeConverter TypeConverter { get; private set; }
protected override ComponentCommandParameterBuilder Instance => this;

public ComponentCommandParameterBuilder(ICommandBuilder command) : base(command) { }
@@ -18,14 +18,14 @@ namespace Discord.Interactions.Builders
/// Sets <see cref="ParameterBuilder{TInfo, TBuilder}.ParameterType"/>.
/// </summary>
/// <param name="type">New value of the <see cref="ParameterBuilder{TInfo, TBuilder}.ParameterType"/>.</param>
/// <param name="services">Service container to be used to resolve the dependencies of this parameters <see cref="TypeConverter"/>.</param>
/// <param name="services">Service container to be used to resolve the dependencies of this parameters <see cref="Interactions.TypeConverter"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services = null)
{
base.SetParameterType(type);
TypeReader = Command.Module.InteractionService.GetTypeReader(ParameterType, services);
TypeConverter = Command.Module.InteractionService.GetComponentTypeConverter(ParameterType, services);
return this;
}



+ 0
- 43
src/Discord.Net.Interactions/ComponentTypeConverters/CompTypeConverter.cs View File

@@ -1,43 +0,0 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
/// <summary>
/// Base class for creating <see cref="CompTypeConverter"/>s. <see cref="InteractionService"/> uses <see cref="CompTypeConverter"/>s to parse string values into entities.
/// </summary>
/// <remarks>
/// <see cref="CompTypeConverter"/>s are mainly used to parse message component values. For interfacing with Slash Command parameters use <see cref="TypeConverter"/>s instead.
/// </remarks>
public abstract class CompTypeConverter : ITypeConverter<IComponentInteractionData>
{
/// <summary>
/// Will be used to search for alternative TypeReaders whenever the Command Service encounters an unknown parameter type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
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="input">Raw string input value.</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 data, IServiceProvider services);

/// <summary>
/// Will be used to manipulate the outgoing command option, before the command gets registered to Discord.
/// </summary>
public virtual string Serialize(object value) => null;
}

/// <inheritdoc/>
public abstract class CompTypeConverter<T> : CompTypeConverter
{
/// <inheritdoc/>
public sealed override bool CanConvertTo(Type type) =>
typeof(T).IsAssignableFrom(type);
}
}

+ 0
- 24
src/Discord.Net.Interactions/Extensions/EnumExtensions.cs View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Discord.Interactions
{
internal static class EnumExtensions
{
public static IEnumerable<T> GetFlags<T>(this T flags) where T : struct, Enum
{
if (!typeof(T).IsEnum || typeof(T).IsDefined(typeof(FlagsAttribute), false))
throw new ArgumentException($"{typeof(T).FullName} isn't a flags enum.", nameof(T));

return Enum.GetValues<T>().Where(x => flags.HasFlag(x));
}

public static TypeReaderTarget ToTypeReaderTarget(this ComponentType componentType) =>
(componentType) switch
{
ComponentType.SelectMenu => TypeReaderTarget.SelectMenu,
_ => throw new InvalidOperationException($"{componentType} isn't supported by {nameof(CompTypeConverter)}s.");
};
}
}

+ 10
- 31
src/Discord.Net.Interactions/Info/Commands/ComponentCommandInfo.cs View File

@@ -42,19 +42,11 @@ namespace Discord.Interactions
if (context.Interaction is not IComponentInteraction componentInteraction)
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Component Interaction");

var args = new List<object>();

if (additionalArgs is not null)
args.AddRange(additionalArgs);

if (componentInteraction.Data?.Values is not null)
args.Add(componentInteraction.Data.Values);

return await ExecuteAsync(context, Parameters, args, services);
return await ExecuteAsync(context, Parameters, additionalArgs, componentInteraction.Data, services);
}

/// <inheritdoc/>
public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<object> values,
public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<string> wildcardCaptures, IComponentInteractionData data,
IServiceProvider services)
{
if (context.Interaction is not IComponentInteraction messageComponent)
@@ -62,33 +54,21 @@ namespace Discord.Interactions

try
{
var valueCount = values.Count();
var args = new object[paramList.Count()];
var paramCount = paramList.Count();
var captureCount = wildcardCaptures?.Count() ?? 0;
var args = new object[paramCount];

for(var i = 0; i < paramList.Count(); i++)
for(var i = 0; i < paramCount; i++)
{
var parameter = Parameters.ElementAt(i);

if(i > valueCount)
{
if (!parameter.IsRequired)
args[i] = parameter.DefaultValue;
else
{
var result = ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command was invoked with too few parameters");
await InvokeModuleEvent(context, result).ConfigureAwait(false);
return result;
}
}
if (i <= captureCount)
args[i] = wildcardCaptures.ElementAt(i);
else
{
var value = values.ElementAt(i);
var typeReader = parameter.TypeReader;
var readResult = await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false);

var readResult = await typeReader.ReadAsync(context, value, services).ConfigureAwait(false);

if (!readResult.IsSuccess)
if(!readResult.IsSuccess)
{
await InvokeModuleEvent(context, readResult).ConfigureAwait(false);
return readResult;
@@ -98,7 +78,6 @@ namespace Discord.Interactions
}
}


return await RunAsync(context, args, services).ConfigureAwait(false);
}
catch (Exception ex)


+ 4
- 3
src/Discord.Net.Interactions/Info/Parameters/ComponentCommandParameterInfo.cs View File

@@ -1,4 +1,5 @@
using Discord.Interactions.Builders;
using System;

namespace Discord.Interactions
{
@@ -8,14 +9,14 @@ namespace Discord.Interactions
public class ComponentCommandParameterInfo : CommandParameterInfo
{
/// <summary>
/// Gets the <see cref="TypeReader"/> 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"/>.
/// </summary>
public CompTypeConverter TypeReader { get; }
public ComponentTypeConverter TypeConverter { get; }

internal ComponentCommandParameterInfo(ComponentCommandParameterBuilder builder, ICommandInfo command) : base(builder, command)
{
TypeReader = builder.TypeReader;
TypeConverter = builder.TypeConverter;
}
}
}

+ 40
- 11
src/Discord.Net.Interactions/InteractionService.cs View File

@@ -3,6 +3,7 @@ using Discord.Logging;
using Discord.Rest;
using Discord.WebSocket;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -67,7 +68,8 @@ namespace Discord.Interactions
private readonly CommandMap<ModalCommandInfo> _modalCommandMap;
private readonly HashSet<ModuleInfo> _moduleDefs;
private readonly TypeMap<TypeConverter, IApplicationCommandInteractionDataOption> _typeConverterMap;
private readonly TypeMap<CompTypeConverter, IComponentInteractionData> _compTypeConverterMap;
private readonly TypeMap<ComponentTypeConverter, IComponentInteractionData> _compTypeConverterMap;
private readonly TypeMap<TypeReader, string> _typeReaderMap;
private readonly ConcurrentDictionary<Type, IAutocompleteHandler> _autocompleteHandlers = new();
private readonly ConcurrentDictionary<Type, ModalInfo> _modalInfos = new();
private readonly SemaphoreSlim _lock;
@@ -194,11 +196,23 @@ namespace Discord.Interactions
[typeof(Nullable<>)] = typeof(NullableConverter<>),
});

_compTypeConverterMap = new TypeMap<CompTypeConverter, IComponentInteractionData>(this, new Dictionary<Type, CompTypeConverter>
{
}, new Dictionary<Type, Type>
{
});
_compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new Dictionary<Type, ComponentTypeConverter>(),
new Dictionary<Type, Type>
{
[typeof(Array)] = typeof(DefaultArrayComponentConverter<>),
[typeof(string)] = typeof(DefaultValueConverter<>)
});

_typeReaderMap = new TypeMap<TypeReader, string>(this, new Dictionary<Type, TypeReader>(),
new Dictionary<Type, Type>
{
[typeof(IChannel)] = typeof(DefaultChannelReader<>),
[typeof(IRole)] = typeof(DefaultRoleReader<>),
[typeof(IUser)] = typeof(DefaultUserReader<>),
[typeof(IMessage)] = typeof(DefaultUserReader<>),
[typeof(IConvertible)] = typeof(DefaultValueReader<>),
[typeof(Enum)] = typeof(EnumReader<>)
});
}

/// <summary>
@@ -809,7 +823,7 @@ namespace Discord.Interactions
public void AddGenericTypeConverter(Type targetType, Type converterType) =>
_typeConverterMap.AddGeneric(targetType, converterType);

internal CompTypeConverter GetComponentTypeConverter(Type type, IServiceProvider services = null) =>
internal ComponentTypeConverter GetComponentTypeConverter(Type type, IServiceProvider services = null) =>
_compTypeConverterMap.Get(type, services);

/// <summary>
@@ -817,7 +831,7 @@ namespace Discord.Interactions
/// </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 AddComponentTypeConverter<T>(CompTypeConverter converter) =>
public void AddComponentTypeConverter<T>(ComponentTypeConverter converter) =>
AddComponentTypeConverter(typeof(T), converter);

/// <summary>
@@ -825,7 +839,7 @@ namespace Discord.Interactions
/// </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 AddComponentTypeConverter(Type type, CompTypeConverter converter) =>
public void AddComponentTypeConverter(Type type, ComponentTypeConverter converter) =>
_compTypeConverterMap.AddConcrete(type, converter);

/// <summary>
@@ -845,8 +859,23 @@ namespace Discord.Interactions
public void AddGenericComponentTypeConverter(Type targetType, Type converterType) =>
_compTypeConverterMap.AddGeneric(targetType, converterType);

public string SerializeWithTypeReader<T>(object obj, IServiceProvider services = null) =>
_compTypeConverterMap.Get(typeof(T), services).Serialize(obj);
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) =>
_typeReaderMap.Get(type, services);

public void AddTypeReader<T>(TypeReader reader) =>
AddTypeReader(typeof(T), reader);

public void AddTypeReader(Type type, TypeReader reader) =>
_typeReaderMap.AddConcrete(type, reader);

public void AddGenericTypeReader<T>(Type readerType) =>
AddGenericTypeReader(typeof(T), readerType);

public void AddGenericTypeReader(Type targetType, Type readerType) =>
_typeReaderMap.AddGeneric(targetType, readerType);

internal IAutocompleteHandler GetAutocompleteHandler(Type autocompleteHandlerType, IServiceProvider services = null)
{


+ 18
- 0
src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/ComponentTypeConverter.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
public abstract class ComponentTypeConverter : ITypeConverter<IComponentInteractionData>
{
public abstract bool CanConvertTo(Type type);
public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services);
public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString());
}

public abstract class ComponentTypeConverter<T> : ComponentTypeConverter
{
public sealed override bool CanConvertTo(Type type) =>
typeof(T).IsAssignableFrom(type);
}
}

+ 42
- 0
src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultArrayComponentConverter.cs View File

@@ -0,0 +1,42 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Discord.Interactions
{
internal sealed class DefaultArrayComponentConverter<T> : ComponentTypeConverter<T>
{
private readonly TypeReader _typeReader;
private readonly Type _underlyingType;

public DefaultArrayComponentConverter(InteractionService interactionService)
{
var type = typeof(T);

if (!type.IsArray)
throw new InvalidOperationException($"{nameof(DefaultArrayComponentConverter<T>)} cannot be used to convert a non-array type.");

_underlyingType = typeof(T).GetElementType();
_typeReader = interactionService.GetTypeReader(_underlyingType);
}

public override async Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services)
{
var results = new List<TypeConverterResult>();

foreach (var value in option.Values)
{
var result = await _typeReader.ReadAsync(context, value, services).ConfigureAwait(false);

if (!result.IsSuccess)
return result;

results.Add(result);
}

return TypeConverterResult.FromSuccess(results.Select(x => x.Value).ToArray());
}
}
}

+ 17
- 0
src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs View File

@@ -0,0 +1,17 @@
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)));
}
}
}

src/Discord.Net.Interactions/TypeConverters/DefaultEntityTypeConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs View File


src/Discord.Net.Interactions/TypeConverters/DefaultValueConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultValueConverter.cs View File


src/Discord.Net.Interactions/TypeConverters/EnumConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/EnumConverter.cs View File


src/Discord.Net.Interactions/TypeConverters/NullableConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/NullableConverter.cs View File


src/Discord.Net.Interactions/TypeConverters/TimeSpanConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/TimeSpanConverter.cs View File


src/Discord.Net.Interactions/TypeConverters/TypeConverter.cs → src/Discord.Net.Interactions/TypeConverters/SlashCommands/TypeConverter.cs View File


+ 48
- 0
src/Discord.Net.Interactions/TypeReaders/DefaultSnowflakeReader.cs View File

@@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
internal abstract class DefaultSnowflakeReader<T> : TypeReader<T>
where T : class, ISnowflakeEntity
{
protected abstract Task<T> GetEntity(ulong id, IInteractionContext ctx);

public override async Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services)
{
if (!ulong.TryParse(option, out var snowflake))
return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} isn't a valid snowflake thus cannot be converted into {typeof(T).Name}");

var result = GetEntity(snowflake, context);

if (result is not null)
return TypeConverterResult.FromSuccess(result);
else
return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} must be a valid {typeof(T).Name} snowflake to be parsed.");
}
}

internal sealed class DefaultUserReader<T> : DefaultSnowflakeReader<T>
where T : class, IUser
{
protected override async Task<T> GetEntity(ulong id, IInteractionContext ctx) => await ctx.Client.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T;
}

internal sealed class DefaultChannelReader<T> : DefaultSnowflakeReader<T>
where T : class, IChannel
{
protected override async Task<T> GetEntity(ulong id, IInteractionContext ctx) => await ctx.Client.GetChannelAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T;
}

internal sealed class DefaultRoleReader<T> : DefaultSnowflakeReader<T>
where T : class, IUser
{
protected override Task<T> GetEntity(ulong id, IInteractionContext ctx) => Task.FromResult(ctx.Guild?.GetRole(id) as T);
}

internal sealed class DefaultMessageReader<T> : DefaultSnowflakeReader<T>
where T : class, IMessage
{
protected override async Task<T> GetEntity(ulong id, IInteractionContext ctx) => await ctx.Channel.GetMessageAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T;
}
}

+ 22
- 0
src/Discord.Net.Interactions/TypeReaders/DefaultValueReader.cs View File

@@ -0,0 +1,22 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
internal sealed class DefaultValueReader<T> : TypeReader<T>
where T : class, IConvertible
{
public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services)
{
try
{
var converted = Convert.ChangeType(option, typeof(T));
return Task.FromResult(TypeConverterResult.FromSuccess(converted));
}
catch (InvalidCastException castEx)
{
return Task.FromResult(TypeConverterResult.FromError(castEx));
}
}
}
}

+ 27
- 0
src/Discord.Net.Interactions/TypeReaders/EnumReader.cs View File

@@ -0,0 +1,27 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
internal sealed class EnumReader<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);
}
}
}

+ 27
- 0
src/Discord.Net.Interactions/TypeReaders/EnumTypeReader.cs View File

@@ -0,0 +1,27 @@
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);
}
}
}

+ 18
- 0
src/Discord.Net.Interactions/TypeReaders/TypeReader.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
public abstract class TypeReader : ITypeConverter<string>
{
public abstract bool CanConvertTo(Type type);
public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services);
public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString());
}

public abstract class TypeReader<T> : TypeReader
{
public sealed override bool CanConvertTo(Type type) =>
typeof(T).IsAssignableFrom(type);
}
}

Loading…
Cancel
Save