Browse Source

create typereader targetting flags

pull/2169/head
Cenk Ergen 3 years ago
parent
commit
ad4999d489
6 changed files with 91 additions and 22 deletions
  1. +1
    -1
      src/Discord.Net.Interactions/Builders/Parameters/ComponentCommandParameterBuilder.cs
  2. +11
    -0
      src/Discord.Net.Interactions/Entities/TypeReaderTarget.cs
  3. +24
    -0
      src/Discord.Net.Interactions/Extensions/EnumExtensions.cs
  4. +46
    -14
      src/Discord.Net.Interactions/InteractionService.cs
  5. +5
    -1
      src/Discord.Net.Interactions/TypeReaders/ArrayReader.cs
  6. +4
    -6
      src/Discord.Net.Interactions/TypeReaders/TypeReader.cs

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

@@ -25,7 +25,7 @@ namespace Discord.Interactions.Builders
public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services = null)
{
base.SetParameterType(type);
TypeReader = Command.Module.InteractionService.GetTypeReader(ParameterType, services);
TypeReader = Command.Module.InteractionService.GetTypeReader(ParameterType, services);
return this;
}



+ 11
- 0
src/Discord.Net.Interactions/Entities/TypeReaderTarget.cs View File

@@ -0,0 +1,11 @@
using System;

namespace Discord.Interactions
{
[Flags]
public enum TypeReaderTarget
{
CustomId = 1,
SelectMenu = 2
}
}

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

@@ -0,0 +1,24 @@
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(TypeReader)}s.");
};
}
}

+ 46
- 14
src/Discord.Net.Interactions/InteractionService.cs View File

@@ -67,7 +67,7 @@ namespace Discord.Interactions
private readonly CommandMap<ModalCommandInfo> _modalCommandMap;
private readonly HashSet<ModuleInfo> _moduleDefs;
private readonly TypeMap<TypeConverter> _typeConverterMap;
private readonly TypeMap<TypeReader> _typeReaderMap;
private readonly ConcurrentDictionary<TypeReaderTarget, TypeMap<TypeReader>> _typeReaderMaps;
private readonly ConcurrentDictionary<Type, IAutocompleteHandler> _autocompleteHandlers = new();
private readonly ConcurrentDictionary<Type, ModalInfo> _modalInfos = new();
private readonly SemaphoreSlim _lock;
@@ -194,7 +194,7 @@ namespace Discord.Interactions
[typeof(Nullable<>)] = typeof(NullableConverter<>),
});

_typeReaderMap = new TypeMap<TypeReader>(this);
_typeReaderMaps = new ConcurrentDictionary<TypeReaderTarget, TypeMap<TypeReader>>();
}

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

internal TypeReader GetTypeReader(Type type, IServiceProvider services = null)
=> _typeReaderMap.Get(type, services);
internal IEnumerable<TypeReader> GetTypeReader(Type type, TypeReaderTarget targets, IServiceProvider services = null)
{
var flattenedTargets = targets.GetFlags();

foreach (var target in flattenedTargets)
yield return _typeReaderMaps[target].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="reader">The <see cref="TypeReader"/> instance.</param>
public void AddTypeReader<T>(TypeReader reader) =>
_typeReaderMap.AddConcrete<T>(reader);
public void AddTypeReader<T>(TypeReader reader, TypeReaderTarget targets)
{
var flattenedTargets = targets.GetFlags();

foreach (var target in flattenedTargets)
_typeReaderMaps[target].AddConcrete<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="reader">The <see cref="TypeReader"/> instance.</param>
public void AddTypeReader(Type type, TypeReader converter) =>
_typeReaderMap.AddConcrete(type, converter);
public void AddTypeReader(Type type, TypeReader reader, TypeReaderTarget targets)
{
var flattenedTargets = targets.GetFlags();

foreach (var target in flattenedTargets)
_typeReaderMaps[target].AddConcrete(type, reader);
}

/// <summary>
/// Add a generic type <see cref="TypeReader{T}"/>.
@@ -830,19 +845,36 @@ namespace Discord.Interactions
/// <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) =>
_typeReaderMap.AddGeneric<T>(readerType);
public void AddGenericTypeReader<T>(Type readerType, TypeReaderTarget targets)
{
var flattenedTargets = targets.GetFlags();

foreach (var target in flattenedTargets)
_typeReaderMaps[target].AddGeneric<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) =>
_typeConverterMap.AddGeneric(targetType, readerType);
public void AddGenericTypeReader(Type targetType, Type readerType, TypeReaderTarget targets)
{
var flattenedTargets = targets.GetFlags();

foreach(var target in flattenedTargets)
_typeReaderMaps[target].AddGeneric(targetType, readerType);
}

public string SerializeWithTypeReader<T>(object obj, IServiceProvider services = null) =>
_typeReaderMap.Get(typeof(T), services)?.Serialize(obj);
public string SerializeWithTypeReader<T>(object obj, TypeReaderTarget targets, IServiceProvider services = null)
{
var flattenedTargets = targets.GetFlags();

if (flattenedTargets.Count() != 1)
throw new ArgumentException("Cannot serialize object for multiple targets.", nameof(targets));

return _typeReaderMaps[flattenedTargets.First()].Get(typeof(T), services)?.Serialize(obj);
}

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


+ 5
- 1
src/Discord.Net.Interactions/TypeReaders/ArrayReader.cs View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;

namespace Discord.Interactions
{
internal class ArrayReader<T> : TypeReader<T> where T : IEnumerable
internal class ArrayReader<T> : TypeReader where T : IEnumerable
{
private readonly TypeReader _baseReader;

@@ -18,6 +18,10 @@ namespace Discord.Interactions
interactionService.GetTypeReader(typeof)
}

public override TypeReaderTarget[] TypeReaderTargets { get; }

public override bool CanConvertTo(Type type) => throw new NotImplementedException();

public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, object input, IServiceProvider services)
{
if(input is IEnumerable enumerable)


+ 4
- 6
src/Discord.Net.Interactions/TypeReaders/TypeReader.cs View File

@@ -9,7 +9,7 @@ namespace Discord.Interactions
/// <remarks>
/// <see cref="TypeReader"/>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 TypeReader<T> : ITypeHandler
public abstract class TypeReader : ITypeHandler
{
/// <summary>
/// Will be used to search for alternative TypeReaders whenever the Command Service encounters an unknown parameter type.
@@ -25,21 +25,19 @@ namespace Discord.Interactions
/// <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, T input, IServiceProvider services);
public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, object input, IServiceProvider services);

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

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

public abstract class TypeReader : TypeReader<object> { }
}

Loading…
Cancel
Save