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