using QFSW.QC.Utilities; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace QFSW.QC { public static partial class QuantumConsoleProcessor { private const string helpStr = "Welcome to Quantum Console! In order to see specific help about any specific command, " + "please use the 'man' command. Use 'man man' to see more about the man command. To see a full list of all " + "commands, use 'all-commands'.\n\n" + "mono-targets\nVarious commands may show a mono-target in their command signature.\n" + "This means they are not static commands, and instead requires instance(s) of the class in order to invoke the command." + "\nEach mono-target works differently as follows:" + "\n - single: uses the first instance of the type found in the scene" + "\n - all: uses all instances of the type found in the scene" + "\n - registry: uses all instances of the type found in the registry" + "\n - singleton: creates and manages a single instance automatically" + "\n\nThe registry is a part of the Quantum Registry that allows you to decide which specific instances of the class " + "should be used when invoking the command. In order to add an object to the registry, either use " + "QFSW.QC.QuantumRegistry.RegisterObject or the runtime command 'register-object'."; [Command("help", "Shows a basic help guide for Quantum Console")] private static string GetHelp() { return helpStr; } [Command("manual")] [Command("man")] private static string ManualHelp() { return "To use the man command, simply put the desired command name in front of it. For example, 'man my-command' will generate the manual for 'my-command'"; } [CommandDescription("Generates a user manual for any given command, including built in ones. To use the man command, simply put the desired command name infront of it. For example, 'man my-command' will generate the manual for 'my-command'")] [Command("help")] [Command("manual")] [Command("man")] private static string GenerateCommandManual(string commandName) { string[] matchingCommands = _commandTable.Keys.Where((string key) => key.Split('(')[0] == commandName).OrderBy((string key) => key).ToArray(); if (matchingCommands.Length == 0) { throw new ArgumentException($"No command with the name {commandName} was found."); } else { Dictionary foundParams = new Dictionary(); Dictionary foundGenericArguments = new Dictionary(); Dictionary foundParamDescriptions = new Dictionary(); List declaringTypes = new List(1); string manual = $"Generated user manual for {commandName}\nAvailable command signatures:"; for (int i = 0; i < matchingCommands.Length; i++) { CommandData currentCommand = _commandTable[matchingCommands[i]]; declaringTypes.Add(currentCommand.MethodData.DeclaringType); manual += $"\n - {currentCommand.CommandSignature}"; if (!currentCommand.IsStatic) { manual += $" (mono-target = {currentCommand.MonoTarget.ToString().ToLower()})"; } for (int j = 0; j < currentCommand.ParamCount; j++) { ParameterInfo param = currentCommand.MethodParamData[j]; if (!foundParams.ContainsKey(param.Name)) { foundParams.Add(param.Name, param); } if (!foundParamDescriptions.ContainsKey(param.Name)) { CommandParameterDescriptionAttribute descriptionAttribute = param.GetCustomAttribute(); if (descriptionAttribute != null && descriptionAttribute.Valid) { foundParamDescriptions.Add(param.Name, descriptionAttribute); } } } if (currentCommand.IsGeneric) { Type[] genericArgs = currentCommand.GenericParamTypes; for (int j = 0; j < genericArgs.Length; j++) { Type arg = genericArgs[j]; if (!foundGenericArguments.ContainsKey(arg.Name)) { foundGenericArguments.Add(arg.Name, arg); } } } } if (foundParams.Count > 0) { manual += "\nParameter info:"; ParameterInfo[] commandParams = foundParams.Values.ToArray(); for (int i = 0; i < commandParams.Length; i++) { ParameterInfo currentParam = commandParams[i]; manual += $"\n - {currentParam.Name}: {currentParam.ParameterType.GetDisplayName()}"; } } string genericConstraintInformation = ""; if (foundGenericArguments.Count > 0) { Type[] genericArgs = foundGenericArguments.Values.ToArray(); for (int i = 0; i < genericArgs.Length; i++) { Type arg = genericArgs[i]; Type[] typeConstraints = arg.GetGenericParameterConstraints(); GenericParameterAttributes attributes = arg.GenericParameterAttributes; List formattedConstraints = new List(); if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) { formattedConstraints.Add("struct"); } if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) { formattedConstraints.Add("class"); } for (int j = 0; j < typeConstraints.Length; j++) { formattedConstraints.Add(typeConstraints[i].GetDisplayName()); } if (attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) { formattedConstraints.Add("new()"); } if (formattedConstraints.Count > 0) { genericConstraintInformation += $"\n - {arg.Name}: {string.Join(", ", formattedConstraints)}"; } } } if (!string.IsNullOrWhiteSpace(genericConstraintInformation)) { manual += $"\nGeneric constraints:{genericConstraintInformation}"; } for (int i = 0; i < matchingCommands.Length; i++) { CommandData currentCommand = _commandTable[matchingCommands[i]]; if (currentCommand.HasDescription) { manual += $"\n\nCommand description:\n{currentCommand.CommandDescription}"; i = matchingCommands.Length; } } if (foundParamDescriptions.Count > 0) { manual += "\n\nParameter descriptions:"; ParameterInfo[] commandParams = foundParams.Values.ToArray(); for (int i = 0; i < commandParams.Length; i++) { ParameterInfo currentParam = commandParams[i]; if (foundParamDescriptions.ContainsKey(currentParam.Name)) { manual += $"\n - {currentParam.Name}: {foundParamDescriptions[currentParam.Name].Description}"; } } } declaringTypes = declaringTypes.Distinct().ToList(); manual += "\n\nDeclared in"; if (declaringTypes.Count == 1) { manual += $" {declaringTypes[0].GetDisplayName(true)}"; } else { manual += ":"; foreach (Type type in declaringTypes) { manual += $"\n - {type.GetDisplayName(true)}"; } } return manual; } } /// /// Gets all loaded unique commands. Unique excludes multiple overloads of the same command from appearing. /// /// All loaded unique commands. public static IEnumerable GetUniqueCommands() { return GetAllCommands() .DistinctBy(x => x.CommandName) .OrderBy(x => x.CommandName); } [CommandDescription("Generates a list of all commands currently loaded by the Quantum Console Processor")] [Command("commands")] [Command("all-commands")] private static string GenerateCommandList() { string output = "List of all commands loaded by the Quantum Processor. Use 'man' on any command to see more:"; foreach (CommandData command in GetUniqueCommands()) { output += $"\n - {command.CommandName}"; } return output; } [Command("user-commands", "Generates a list of all commands added by the user")] private static IEnumerable GenerateUserCommandList() { return GetUniqueCommands() .Where(x => !x.MethodData.DeclaringType.Assembly.FullName.StartsWith("QFSW.QC")) .Select(x => $" - {x.CommandName}"); } } }