| @@ -0,0 +1,97 @@ | |||||
| using Discord.Logging; | |||||
| using System; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| internal static class TypeInfoExtensions | |||||
| { | |||||
| public static async Task<bool> IsValidModuleType(this TypeInfo typeInfo, bool autoLoadable = false, Logger logger = null) | |||||
| { | |||||
| if(IsOuterSubTypeCandidate(typeInfo, out Type parentType)) | |||||
| { | |||||
| return await IsValidOuterSubTypeAsync(typeInfo, parentType, autoLoadable, logger); | |||||
| } | |||||
| else | |||||
| { | |||||
| return await IsModuleAsync(typeInfo, logger, autoLoadable); | |||||
| } | |||||
| } | |||||
| public static bool IsOuterSubTypeCandidate(this TypeInfo typeInfo, out Type parentType) | |||||
| { | |||||
| var groupAttr = typeInfo.GetCustomAttribute<GroupAttribute>(); | |||||
| if (groupAttr != null) | |||||
| { | |||||
| if (groupAttr.ParentModule != null) | |||||
| { | |||||
| parentType = groupAttr.ParentModule; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| parentType = null; | |||||
| return false; | |||||
| } | |||||
| /// <param name="autoLoadable">If true, must not contain [DontAutoLoad] or will return false.</param> | |||||
| public static async Task<bool> IsValidOuterSubTypeAsync(this TypeInfo typeInfo, Type parentType, bool autoLoadable = false, Logger logger = null) | |||||
| { | |||||
| TypeInfo parentTypeInfo = parentType.GetTypeInfo(); | |||||
| bool targetValid = await IsModuleAsync(typeInfo, logger, autoLoadable); | |||||
| if (targetValid) | |||||
| { | |||||
| bool parentValid = await IsModuleAsync(parentTypeInfo, logger, autoLoadable); | |||||
| if (!parentValid) | |||||
| { | |||||
| if (logger != null) | |||||
| { | |||||
| await logger.WarningAsync($"Parent Class {parentTypeInfo.FullName} is not a module. Group module {typeInfo.FullName} was not loaded.").ConfigureAwait(false); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return targetValid; | |||||
| } | |||||
| private static async Task<bool> IsModuleAsync(TypeInfo typeInfo, Logger logger, bool autoLoadable) | |||||
| { | |||||
| if(IsValidModuleDefinition(typeInfo)) | |||||
| { | |||||
| if (autoLoadable) | |||||
| { | |||||
| if (typeInfo.IsPublic || typeInfo.IsNestedPublic) | |||||
| { | |||||
| return !typeInfo.IsDefined(typeof(DontAutoLoadAttribute)); | |||||
| } | |||||
| else if (IsAutoLoadableModuleCandidate(typeInfo)) | |||||
| { | |||||
| if(logger != null) | |||||
| { | |||||
| await logger.WarningAsync($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.").ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| internal static bool IsValidModuleDefinition(this TypeInfo typeInfo) => | |||||
| ModuleClassBuilder.ModuleTypeInfo.IsAssignableFrom(typeInfo) && | |||||
| !typeInfo.IsAbstract && | |||||
| !typeInfo.ContainsGenericParameters; | |||||
| private static bool IsAutoLoadableModuleCandidate(TypeInfo typeInfo) => | |||||
| typeInfo.DeclaredMethods.Any( | |||||
| x => x.GetCustomAttribute<CommandAttribute>() != null) && | |||||
| typeInfo.GetCustomAttribute<DontAutoLoadAttribute>() == null; | |||||
| } | |||||
| } | |||||