| @@ -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); | |||
| } | |||
| } | |||
| } | |||