Browse Source

Add AddEntityTypeReader

pull/1487/head
SubZero0 5 years ago
parent
commit
bf144d605a
1 changed files with 60 additions and 12 deletions
  1. +60
    -12
      src/Discord.Net.Commands/CommandService.cs

+ 60
- 12
src/Discord.Net.Commands/CommandService.cs View File

@@ -48,6 +48,7 @@ namespace Discord.Commands
private readonly SemaphoreSlim _moduleLock;
private readonly ConcurrentDictionary<Type, ModuleInfo> _typedModuleDefs;
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, TypeReader>> _typeReaders;
private readonly ConcurrentDictionary<Type, ConcurrentQueue<Type>> _userEntityTypeReaders;
private readonly ConcurrentDictionary<Type, TypeReader> _defaultTypeReaders;
private readonly ConcurrentDictionary<Type, TypeReader> _overrideTypeReaders;
private readonly ImmutableList<(Type EntityType, Type TypeReaderType)> _entityTypeReaders;
@@ -78,6 +79,15 @@ namespace Discord.Commands
/// </summary>
public ILookup<Type, TypeReader> TypeReaders => _typeReaders.SelectMany(x => x.Value.Select(y => new { y.Key, y.Value })).ToLookup(x => x.Key, x => x.Value);

/// <summary>
/// Represents all entity type reader <see cref="Type" />s loaded within <see cref="CommandService"/>.
/// </summary>
/// <returns>
/// A <see cref="ILookup{TKey, TElement}"/> that the key is the object type to be read by the <see cref="TypeReader"/>
/// and the element is the type of the <see cref="TypeReader"/> generic definition.
/// </returns>
public ILookup<Type, Type> EntityTypeReaders => _userEntityTypeReaders.SelectMany(x => x.Value.Select(y => new { x.Key, TypeReaderType = y })).ToLookup(x => x.Key, y => y.TypeReaderType);

/// <summary>
/// Initializes a new <see cref="CommandService"/> class.
/// </summary>
@@ -110,6 +120,7 @@ namespace Discord.Commands
_moduleDefs = new HashSet<ModuleInfo>();
_map = new CommandMap(this);
_typeReaders = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, TypeReader>>();
_userEntityTypeReaders = new ConcurrentDictionary<Type, ConcurrentQueue<Type>>();
_overrideTypeReaders = new ConcurrentDictionary<Type, TypeReader>();

_defaultTypeReaders = new ConcurrentDictionary<Type, TypeReader>();
@@ -331,8 +342,6 @@ namespace Discord.Commands
/// type.
/// If <typeparamref name="T" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> will
/// also be added.
/// If a default <see cref="TypeReader" /> exists for <typeparamref name="T" />, a warning will be logged
/// and the default <see cref="TypeReader" /> will be replaced.
/// </summary>
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
@@ -343,8 +352,6 @@ namespace Discord.Commands
/// type.
/// If <paramref name="type" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> for the
/// value type will also be added.
/// If a default <see cref="TypeReader" /> exists for <paramref name="type" />, a warning will be logged and
/// the default <see cref="TypeReader" /> will be replaced.
/// </summary>
/// <param name="type">A <see cref="Type" /> instance for the type to be read.</param>
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
@@ -357,6 +364,40 @@ namespace Discord.Commands
AddNullableTypeReader(type, reader);
}
/// <summary>
/// Adds a custom entity <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied
/// object type.
/// </summary>
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
/// <param name="typeReaderType">
/// A <see cref="Type" /> that is a generic type definition with a single open argument
/// of the <see cref="TypeReader" /> to be added.
/// </param>
public void AddEntityTypeReader<T>(Type typeReaderGenericType)
=> AddEntityTypeReader(typeof(T), typeReaderGenericType);
/// <summary>
/// Adds a custom entity <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied
/// object type.
/// </summary>
/// <param name="type">A <see cref="Type" /> instance for the type to be read.</param>
/// <param name="typeReaderType">
/// A <see cref="Type" /> that is a generic type definition with a single open argument
/// of the <see cref="TypeReader" /> to be added.
/// </param>
public void AddEntityTypeReader(Type type, Type typeReaderGenericType)
{
if (!typeReaderGenericType.IsGenericTypeDefinition)
throw new ArgumentException("TypeReader type must be a generic type definition.", nameof(typeReaderGenericType));
Type[] genericArgs = typeReaderGenericType.GetGenericArguments();
if (genericArgs.Length != 1)
throw new ArgumentException("TypeReader type must accept one and only one open generic argument.", nameof(typeReaderGenericType));
if (!genericArgs[0].IsGenericParameter)
throw new ArgumentException("TypeReader type must accept one and only one open generic argument.", nameof(typeReaderGenericType));
if (!genericArgs[0].GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint))
throw new ArgumentException("TypeReader generic argument must have a reference type constraint.", nameof(typeReaderGenericType));
var readers = _userEntityTypeReaders.GetOrAdd(type, x => new ConcurrentQueue<Type>());
readers.Enqueue(typeReaderGenericType);
}
/// <summary>
/// Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
/// type.
/// If <typeparamref name="T" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> will
@@ -418,28 +459,35 @@ namespace Discord.Commands
if (_typeReaders.TryGetValue(type, out var definedTypeReaders))
return definedTypeReaders;

var entityReaders = _typeReaders.Where(x => x.Key.IsAssignableFrom(type));
var assignableEntityReaders = _userEntityTypeReaders.Where(x => x.Key.IsAssignableFrom(type));

int assignableTo = -1;
KeyValuePair<Type, ConcurrentDictionary<Type, TypeReader>>? typeReader = null;
foreach (var entityReader in entityReaders)
KeyValuePair<Type, ConcurrentQueue<Type>>? entityReaders = null;
foreach (var entityReader in assignableEntityReaders)
{
int assignables = entityReaders.Sum(x => !x.Equals(entityReader) && x.Key.IsAssignableFrom(entityReader.Key) ? 1 : 0);
int assignables = assignableEntityReaders.Sum(x => !x.Equals(entityReader) && x.Key.IsAssignableFrom(entityReader.Key) ? 1 : 0);
if (assignableTo == -1)
{
// First time
assignableTo = assignables;
typeReader = entityReader;
entityReaders = entityReader;
}
// Try to get the "higher" interface. IMessageChannel is assignable to IChannel, but not the inverse
// Try to get the most specific type reader, i.e. IMessageChannel is assignable to IChannel, but not the inverse
else if (assignables > assignableTo)
{
assignableTo = assignables;
typeReader = entityReader;
entityReaders = entityReader;
}
}

return typeReader?.Value;
if (entityReaders != null)
{
var entityTypeReaderType = entityReaders.Value.Value.First();
TypeReader reader = Activator.CreateInstance(entityTypeReaderType.MakeGenericType(type)) as TypeReader;
AddTypeReader(type, reader);
return GetTypeReaders(type);
}
return null;
}
internal TypeReader GetDefaultTypeReader(Type type)
{


Loading…
Cancel
Save