| @@ -0,0 +1,21 @@ | |||||
| using System; | |||||
| using System.Reflection; | |||||
| namespace Discord.Commands.Attributes | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = true)] | |||||
| public class TypeReaderAttribute : Attribute | |||||
| { | |||||
| public Type Type { get; } | |||||
| public TypeInfo OverridingTypeReader { get; } | |||||
| 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(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -6,6 +6,7 @@ using System.Diagnostics; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Reflection; | using System.Reflection; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Discord.Commands.Attributes; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -30,7 +31,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 +75,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 +185,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 +200,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) | ||||
| @@ -3,6 +3,7 @@ using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Reflection; | using System.Reflection; | ||||
| using Discord.Commands.Attributes; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -19,8 +20,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 +47,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 +85,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); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||