@@ -0,0 +1,36 @@ | |||||
using System; | |||||
using System.Reflection; | |||||
namespace Discord.Commands | |||||
{ | |||||
/// <summary> | |||||
/// Allows to override the <see cref="TypeReader"/> used for a parameter/type. | |||||
/// </summary> | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = true)] | |||||
public class TypeReaderAttribute : Attribute | |||||
{ | |||||
/// <summary> | |||||
/// Type of the type that is read. | |||||
/// </summary> | |||||
public Type Type { get; } | |||||
/// <summary> | |||||
/// <see cref="TypeInfo"/> of the specified <see cref="TypeReader"/>. | |||||
/// </summary> | |||||
public TypeInfo OverridingTypeReader { get; } | |||||
/// <summary> | |||||
/// Specify a <see cref="TypeReader"/> for a particular type. | |||||
/// </summary> | |||||
/// <param name="forType">Type of the type to read.</param> | |||||
/// <param name="typeReader">Type of the <see cref="TypeReader"/> that reads the type.</param> | |||||
public TypeReaderAttribute(Type forType, Type typeReader) | |||||
{ | |||||
if (!typeof(TypeReader).GetTypeInfo().IsAssignableFrom(typeReader.GetTypeInfo())) | |||||
throw new ArgumentException($"Type of argument {nameof(typeReader)} must derive from {nameof(TypeReader)}", nameof(typeReader)); | |||||
Type = forType; | |||||
OverridingTypeReader = typeReader.GetTypeInfo(); | |||||
} | |||||
} | |||||
} |
@@ -30,7 +30,7 @@ namespace Discord.Commands | |||||
public IReadOnlyList<CommandParameter> Parameters { get; } | public IReadOnlyList<CommandParameter> Parameters { get; } | ||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; } | public IReadOnlyList<PreconditionAttribute> Preconditions { get; } | ||||
internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix) | |||||
internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
@@ -74,7 +74,7 @@ namespace Discord.Commands | |||||
var priorityAttr = source.GetCustomAttribute<PriorityAttribute>(); | var priorityAttr = source.GetCustomAttribute<PriorityAttribute>(); | ||||
Priority = priorityAttr?.Priority ?? 0; | Priority = priorityAttr?.Priority ?? 0; | ||||
Parameters = BuildParameters(source); | |||||
Parameters = BuildParameters(source, dependencyMap); | |||||
HasVarArgs = Parameters.Count > 0 ? Parameters[Parameters.Count - 1].IsMultiple : false; | HasVarArgs = Parameters.Count > 0 ? Parameters[Parameters.Count - 1].IsMultiple : false; | ||||
Preconditions = BuildPreconditions(source); | Preconditions = BuildPreconditions(source); | ||||
_action = BuildAction(source); | _action = BuildAction(source); | ||||
@@ -184,7 +184,7 @@ namespace Discord.Commands | |||||
return methodInfo.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray(); | return methodInfo.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray(); | ||||
} | } | ||||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) | |||||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
var parameters = methodInfo.GetParameters(); | var parameters = methodInfo.GetParameters(); | ||||
@@ -199,7 +199,13 @@ namespace Discord.Commands | |||||
if (isMultiple) | if (isMultiple) | ||||
type = type.GetElementType(); | type = type.GetElementType(); | ||||
var reader = Module.Service.GetTypeReader(type); | |||||
var trAttr = parameter.GetCustomAttribute<TypeReaderAttribute>(); | |||||
var reader = (trAttr != null && trAttr.Type == type) ? | |||||
ReflectionUtils.CreateObject<TypeReader>(trAttr.OverridingTypeReader, Module.Service, dependencyMap) : | |||||
(Module.OverridingTypeReaders.ContainsKey(type) ? | |||||
Module.OverridingTypeReaders[type] : | |||||
Module.Service.GetTypeReader(type)); | |||||
var typeInfo = type.GetTypeInfo(); | var typeInfo = type.GetTypeInfo(); | ||||
//Detect enums | //Detect enums | ||||
@@ -65,7 +65,7 @@ namespace Discord.Commands | |||||
} | } | ||||
//Modules | //Modules | ||||
public async Task<ModuleInfo> AddModule<T>() | |||||
public async Task<ModuleInfo> AddModule<T>(IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
await _moduleLock.WaitAsync().ConfigureAwait(false); | await _moduleLock.WaitAsync().ConfigureAwait(false); | ||||
try | try | ||||
@@ -80,14 +80,14 @@ namespace Discord.Commands | |||||
if (_moduleDefs.ContainsKey(typeof(T))) | if (_moduleDefs.ContainsKey(typeof(T))) | ||||
throw new ArgumentException($"This module has already been added."); | throw new ArgumentException($"This module has already been added."); | ||||
return AddModuleInternal(typeInfo); | |||||
return AddModuleInternal(typeInfo, dependencyMap); | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
_moduleLock.Release(); | _moduleLock.Release(); | ||||
} | } | ||||
} | } | ||||
public async Task<IEnumerable<ModuleInfo>> AddModules(Assembly assembly) | |||||
public async Task<IEnumerable<ModuleInfo>> AddModules(Assembly assembly, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
var moduleDefs = ImmutableArray.CreateBuilder<ModuleInfo>(); | var moduleDefs = ImmutableArray.CreateBuilder<ModuleInfo>(); | ||||
await _moduleLock.WaitAsync().ConfigureAwait(false); | await _moduleLock.WaitAsync().ConfigureAwait(false); | ||||
@@ -102,7 +102,7 @@ namespace Discord.Commands | |||||
{ | { | ||||
var dontAutoLoad = typeInfo.GetCustomAttribute<DontAutoLoadAttribute>(); | var dontAutoLoad = typeInfo.GetCustomAttribute<DontAutoLoadAttribute>(); | ||||
if (dontAutoLoad == null && !typeInfo.IsAbstract) | if (dontAutoLoad == null && !typeInfo.IsAbstract) | ||||
moduleDefs.Add(AddModuleInternal(typeInfo)); | |||||
moduleDefs.Add(AddModuleInternal(typeInfo, dependencyMap)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -113,9 +113,9 @@ namespace Discord.Commands | |||||
_moduleLock.Release(); | _moduleLock.Release(); | ||||
} | } | ||||
} | } | ||||
private ModuleInfo AddModuleInternal(TypeInfo typeInfo) | |||||
private ModuleInfo AddModuleInternal(TypeInfo typeInfo, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
var moduleDef = new ModuleInfo(typeInfo, this); | |||||
var moduleDef = new ModuleInfo(typeInfo, this, dependencyMap); | |||||
_moduleDefs[typeInfo.AsType()] = moduleDef; | _moduleDefs[typeInfo.AsType()] = moduleDef; | ||||
foreach (var cmd in moduleDef.Commands) | foreach (var cmd in moduleDef.Commands) | ||||
@@ -19,8 +19,9 @@ namespace Discord.Commands | |||||
public string Remarks { get; } | public string Remarks { get; } | ||||
public IEnumerable<CommandInfo> Commands { get; } | public IEnumerable<CommandInfo> Commands { get; } | ||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; } | public IReadOnlyList<PreconditionAttribute> Preconditions { get; } | ||||
public ImmutableDictionary<Type, TypeReader> OverridingTypeReaders { get; } | |||||
internal ModuleInfo(TypeInfo source, CommandService service) | |||||
internal ModuleInfo(TypeInfo source, CommandService service, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
Source = source; | Source = source; | ||||
Service = service; | Service = service; | ||||
@@ -45,19 +46,31 @@ namespace Discord.Commands | |||||
if (remarksAttr != null) | if (remarksAttr != null) | ||||
Remarks = remarksAttr.Text; | Remarks = remarksAttr.Text; | ||||
var typeReaders = new Dictionary<Type, TypeReader>(); | |||||
var trAttrs = source.GetCustomAttributes<TypeReaderAttribute>(); | |||||
foreach (var trAttr in trAttrs) | |||||
typeReaders[trAttr.Type] = GetOverridingTypeReader(trAttr, dependencyMap); | |||||
OverridingTypeReaders = typeReaders.ToImmutableDictionary(); | |||||
List<CommandInfo> commands = new List<CommandInfo>(); | List<CommandInfo> commands = new List<CommandInfo>(); | ||||
SearchClass(source, commands, Prefix); | |||||
SearchClass(source, commands, Prefix, dependencyMap); | |||||
Commands = commands; | Commands = commands; | ||||
Preconditions = Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray(); | Preconditions = Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray(); | ||||
} | } | ||||
private void SearchClass(TypeInfo parentType, List<CommandInfo> commands, string groupPrefix) | |||||
private TypeReader GetOverridingTypeReader(TypeReaderAttribute trAttr, IDependencyMap dependencyMap = null) | |||||
=> ReflectionUtils.CreateObject<TypeReader>(trAttr.OverridingTypeReader, Service, dependencyMap); | |||||
private void SearchClass(TypeInfo parentType, List<CommandInfo> commands, string groupPrefix, IDependencyMap dependencyMap = null) | |||||
{ | { | ||||
foreach (var method in parentType.DeclaredMethods) | foreach (var method in parentType.DeclaredMethods) | ||||
{ | { | ||||
var cmdAttr = method.GetCustomAttribute<CommandAttribute>(); | var cmdAttr = method.GetCustomAttribute<CommandAttribute>(); | ||||
if (cmdAttr != null) | if (cmdAttr != null) | ||||
commands.Add(new CommandInfo(method, this, cmdAttr, groupPrefix)); | |||||
commands.Add(new CommandInfo(method, this, cmdAttr, groupPrefix, dependencyMap)); | |||||
} | } | ||||
foreach (var type in parentType.DeclaredNestedTypes) | foreach (var type in parentType.DeclaredNestedTypes) | ||||
{ | { | ||||
@@ -71,7 +84,7 @@ namespace Discord.Commands | |||||
else | else | ||||
nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant(); | nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant(); | ||||
SearchClass(type, commands, nextGroupPrefix); | |||||
SearchClass(type, commands, nextGroupPrefix, dependencyMap); | |||||
} | } | ||||
} | } | ||||
} | } | ||||