@@ -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<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 | |||
{ | |||
@@ -74,7 +74,7 @@ namespace Discord.Commands | |||
var priorityAttr = source.GetCustomAttribute<PriorityAttribute>(); | |||
Priority = priorityAttr?.Priority ?? 0; | |||
Parameters = BuildParameters(source); | |||
Parameters = BuildParameters(source, dependencyMap); | |||
HasVarArgs = Parameters.Count > 0 ? Parameters[Parameters.Count - 1].IsMultiple : false; | |||
Preconditions = BuildPreconditions(source); | |||
_action = BuildAction(source); | |||
@@ -184,7 +184,7 @@ namespace Discord.Commands | |||
return methodInfo.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray(); | |||
} | |||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) | |||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo, IDependencyMap dependencyMap = null) | |||
{ | |||
var parameters = methodInfo.GetParameters(); | |||
@@ -199,7 +199,13 @@ namespace Discord.Commands | |||
if (isMultiple) | |||
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(); | |||
//Detect enums | |||
@@ -65,7 +65,7 @@ namespace Discord.Commands | |||
} | |||
//Modules | |||
public async Task<ModuleInfo> AddModule<T>() | |||
public async Task<ModuleInfo> AddModule<T>(IDependencyMap dependencyMap = null) | |||
{ | |||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||
try | |||
@@ -80,14 +80,14 @@ namespace Discord.Commands | |||
if (_moduleDefs.ContainsKey(typeof(T))) | |||
throw new ArgumentException($"This module has already been added."); | |||
return AddModuleInternal(typeInfo); | |||
return AddModuleInternal(typeInfo, dependencyMap); | |||
} | |||
finally | |||
{ | |||
_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>(); | |||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||
@@ -102,7 +102,7 @@ namespace Discord.Commands | |||
{ | |||
var dontAutoLoad = typeInfo.GetCustomAttribute<DontAutoLoadAttribute>(); | |||
if (dontAutoLoad == null && !typeInfo.IsAbstract) | |||
moduleDefs.Add(AddModuleInternal(typeInfo)); | |||
moduleDefs.Add(AddModuleInternal(typeInfo, dependencyMap)); | |||
} | |||
} | |||
} | |||
@@ -113,9 +113,9 @@ namespace Discord.Commands | |||
_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; | |||
foreach (var cmd in moduleDef.Commands) | |||
@@ -19,8 +19,9 @@ namespace Discord.Commands | |||
public string Remarks { get; } | |||
public IEnumerable<CommandInfo> Commands { 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; | |||
Service = service; | |||
@@ -45,19 +46,31 @@ namespace Discord.Commands | |||
if (remarksAttr != null) | |||
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>(); | |||
SearchClass(source, commands, Prefix); | |||
SearchClass(source, commands, Prefix, dependencyMap); | |||
Commands = commands; | |||
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) | |||
{ | |||
var cmdAttr = method.GetCustomAttribute<CommandAttribute>(); | |||
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) | |||
{ | |||
@@ -71,7 +84,7 @@ namespace Discord.Commands | |||
else | |||
nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant(); | |||
SearchClass(type, commands, nextGroupPrefix); | |||
SearchClass(type, commands, nextGroupPrefix, dependencyMap); | |||
} | |||
} | |||
} | |||