Feature: Component TypeConverters and CustomID TypeReaders (#2169)

* fix sharded client current user

* add custom setter to group property of module builder

* rename serilazation method

* init

* create typemap and default typereaders

* add default readers

* create typereader targetting flags

* seperate custom id readers with component typeconverters

* add typereaders

* add customid readers

* clean up component info argument parsing

* remove obsolete method

* add component typeconverters to modals

* fix build errors

* add inline docs

* bug fixes

* code cleanup and refactorings

* fix build errors

* add GenerateCustomIdString method to interaction service

* add GenerateCustomIdString method to interaction service

* add inline docs to componentparameterbuilder

* add inline docs to GenerateCustomIdStringAsync method
This commit is contained in:
Cenk Ergen
2022-03-09 23:10:00 +03:00
committed by GitHub
parent cc6918d157
commit fb4250b88c
34 changed files with 816 additions and 242 deletions

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Discord.Interactions
{
internal class TypeMap<TConverter, TData>
where TConverter : class, ITypeConverter<TData>
{
private readonly ConcurrentDictionary<Type, TConverter> _concretes;
private readonly ConcurrentDictionary<Type, Type> _generics;
private readonly InteractionService _interactionService;
public TypeMap(InteractionService interactionService, IDictionary<Type, TConverter> concretes = null, IDictionary<Type, Type> generics = null)
{
_interactionService = interactionService;
_concretes = concretes is not null ? new(concretes) : new();
_generics = generics is not null ? new(generics) : new();
}
internal TConverter Get(Type type, IServiceProvider services = null)
{
if (_concretes.TryGetValue(type, out var specific))
return specific;
if (_generics.Any(x => x.Key.IsAssignableFrom(type)
|| x.Key.IsGenericTypeDefinition && type.IsGenericType && x.Key.GetGenericTypeDefinition() == type.GetGenericTypeDefinition()))
{
services ??= EmptyServiceProvider.Instance;
var converterType = GetMostSpecific(type);
var converter = ReflectionUtils<TConverter>.CreateObject(converterType.MakeGenericType(type).GetTypeInfo(), _interactionService, services);
_concretes[type] = converter;
return converter;
}
if (_concretes.Any(x => x.Value.CanConvertTo(type)))
return _concretes.First(x => x.Value.CanConvertTo(type)).Value;
throw new ArgumentException($"No type {typeof(TConverter).Name} is defined for this {type.FullName}", nameof(type));
}
public void AddConcrete<TTarget>(TConverter converter) =>
AddConcrete(typeof(TTarget), converter);
public void AddConcrete(Type type, TConverter converter)
{
if (!converter.CanConvertTo(type))
throw new ArgumentException($"This {converter.GetType().FullName} cannot read {type.FullName} and cannot be registered as its {nameof(TypeConverter)}");
_concretes[type] = converter;
}
public void AddGeneric<TTarget>(Type converterType) =>
AddGeneric(typeof(TTarget), converterType);
public void AddGeneric(Type targetType, Type converterType)
{
if (!converterType.IsGenericTypeDefinition)
throw new ArgumentException($"{converterType.FullName} is not generic.");
var genericArguments = converterType.GetGenericArguments();
if (genericArguments.Length > 1)
throw new InvalidOperationException($"Valid generic {converterType.FullName}s cannot have more than 1 generic type parameter");
var constraints = genericArguments.SelectMany(x => x.GetGenericParameterConstraints());
if (!constraints.Any(x => x.IsAssignableFrom(targetType)))
throw new InvalidOperationException($"This generic class does not support type {targetType.FullName}");
_generics[targetType] = converterType;
}
private Type GetMostSpecific(Type type)
{
if (_generics.TryGetValue(type, out var matching))
return matching;
if (type.IsGenericType && _generics.TryGetValue(type.GetGenericTypeDefinition(), out var genericDefinition))
return genericDefinition;
var typeInterfaces = type.GetInterfaces();
var candidates = _generics.Where(x => x.Key.IsAssignableFrom(type))
.OrderByDescending(x => typeInterfaces.Count(y => y.IsAssignableFrom(x.Key)));
return candidates.First().Value;
}
}
}