using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.UIElements; using UnityEditorInternal; using UnityEngine.Pool; using Object = UnityEngine.Object; namespace Module.ProjectValidator.Editor { internal sealed class ProjectValidatorSettings : ScriptableObject { public List assemblies = new(); public List validators = new(); public const string MenuPath = "Project/Project Validator"; private const string AssetPath = "ProjectSettings/ProjectValidatorSettings.asset"; private const string StyleSheetName = "StyleSheetProjectValidatorSettings"; internal static ProjectValidatorSettings GetOrCreate() { var objects = InternalEditorUtility.LoadSerializedFileAndForget(AssetPath); ProjectValidatorSettings settings; if (objects.Length != 0) settings = (ProjectValidatorSettings)objects[0]; else settings = CreateInstance(); PopulateValidatorList(settings); InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] { settings }, AssetPath, true); return settings; } [SettingsProvider] public static SettingsProvider CreateProvider() { return new SettingsProvider(MenuPath, SettingsScope.Project) { label = "Project Validator", activateHandler = (_, root) => { var settings = GetOrCreate(); var serializedObject = new SerializedObject(settings); var container = new VisualElement { style = { flexDirection = FlexDirection.Column } }; var assemblyField = new PropertyField(serializedObject.FindProperty(nameof(assemblies)), "Assemblies"); container.Add(assemblyField); var enabledField = new PropertyField(serializedObject.FindProperty(nameof(validators)), "Validators"); container.Add(enabledField); root.Add(container); root.Bind(serializedObject); root.RegisterCallback(_ => InternalEditorUtility.SaveToSerializedFileAndForget(new[] { serializedObject.targetObject }, AssetPath, true)); root.RegisterCallback(_ => InternalEditorUtility.SaveToSerializedFileAndForget(new[] { serializedObject.targetObject }, AssetPath, true)); root.styleSheets.Add(EditorAssetUtility.LoadFirstAsset(StyleSheetName)); }, keywords = new HashSet(new[] { "Project", "Validator", "Assemblies" }) }; } private static void PopulateValidatorList(ProjectValidatorSettings settings) { using var pool0 = ListPool.Get(out var list); using var pool1 = ListPool.Get(out var temp); FetchValidatorsOfType(typeof(IAssetValidator<>), list); FetchValidatorsOfType(typeof(IAttributeValidator<>), list); FetchValidatorsOfType(typeof(IComponentValidator<>), list); FetchValidatorsOfType(typeof(IGameObjectValidator), list); for (var i = 0; i < settings.validators.Count; i++) { temp.Add(settings.validators[i].type); } for (var i = 0; i < list.Count; i++) { if (!temp.Contains(list[i])) settings.validators.Add(new ValidatorEnabled(list[i], true)); } for (var i = temp.Count - 1; i >= 0; i--) { if (!list.Contains(temp[i])) settings.validators.RemoveAt(i); } } private static void FetchValidatorsOfType(Type type, List typeNames) { var types = TypeCache.GetTypesDerivedFrom(type); for (var i = 0; i < types.Count; i++) { if (!types[i].IsInterface && !types[i].IsAbstract) typeNames.Add(types[i].FullName); } } [Serializable] public sealed class ValidatorEnabled { public string type; public bool enabled; public ValidatorEnabled(string type, bool enabled) { this.type = type; this.enabled = enabled; } } } }