diff --git a/CW_Core_Console/ConsoleCommands.cs b/CW_Core_Console/ConsoleCommands.cs new file mode 100644 index 0000000..1205338 --- /dev/null +++ b/CW_Core_Console/ConsoleCommands.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CW_Core_Console +{ + public class ConsoleCommands + { + public static string Test() => "Working"; + } +} diff --git a/CW_Core_Console/Program.cs b/CW_Core_Console/Program.cs index c8c449b..c5265de 100644 --- a/CW_Core_Console/Program.cs +++ b/CW_Core_Console/Program.cs @@ -1,4 +1,5 @@ -using System; +using Console.Waterworks.Core; +using System; namespace CW_Core_Console { @@ -6,7 +7,9 @@ namespace CW_Core_Console { static void Main(string[] args) { - System.Console.WriteLine("Hello World!"); + var liaison = new CW_Liaison(); + liaison.Run("CW_Core_Console", true); + } } } diff --git a/Console.Waterworks.Core/Assistants/CoOrdinatorAssistant.cs b/Console.Waterworks.Core/Assistants/CoOrdinatorAssistant.cs new file mode 100644 index 0000000..f543de6 --- /dev/null +++ b/Console.Waterworks.Core/Assistants/CoOrdinatorAssistant.cs @@ -0,0 +1,52 @@ +using Console.Waterworks.Core.Constants; +using Console.Waterworks.Core.Loggers; +using Console.Waterworks.Core.Specialists; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Console.Waterworks.Core.Assistants +{ + class CoOrdinatorAssistant + { + internal void SetConsoleTitle(CW_Logger logger, ProgramInfoSpecialist progInfoSpec, ConsoleIOSpecialist consoleSpec) + { + logger.LogInfoMessage("Attempting to set console title.."); + var title = progInfoSpec.GetProductName(); + if (!string.IsNullOrEmpty(title)) + { + consoleSpec.SetConsoleTitle(title); + logger.LogSuccessMessage("Console title is now set"); + } + else + logger.LogErrorMessage("Unable to determine the program's title. Check the manifest file to make sure it has been set"); + } + + internal void OutputProgramInfo(CW_Logger logger, ProgramInfoSpecialist progInfoSpec, ConsoleIOSpecialist consoleSpec) + { + logger.LogInfoMessage("Attempting to retrieve program information.."); + List programInfo = progInfoSpec.GatherProgramInfomation(); + logger.LogInfoMessage($"Found {programInfo.Count} out of {CW_Constants.PROGRAM_INFO_PROPERTIES_COUNT} properties"); + logger.LogInfoMessage($"Writing program properties to console.."); + consoleSpec.WriteProgramInfo(programInfo); + } + + + internal void LogCommandGatheringAttempt(List commandClasses, Dictionary>> commandLibraries, CW_Logger logger) + { + logger.LogInfoMessage($"Found {commandClasses.Count} command class(es).."); + logger.LogInfoMessage($"Found {GetFoundCommandsTotal(commandLibraries)} command(s).."); + logger.LogInfoMessage($"Found [{CW_Constants.COMMAND_CLASS_NAME}] class: { commandLibraries.ContainsKey(CW_Constants.COMMAND_CLASS_NAME)}"); + logger.LogNoteMessage($"WaterWorks is only looking for the methods in the [{CW_Constants.COMMAND_CLASS_NAME}] class in the specified namespace. Everything else will be ignored"); + logger.LogInfoMessage("Program is now initialised and awaiting input from the user. You are welcome ;-)"); + } + + internal int GetFoundCommandsTotal(Dictionary>> commandLibraries) + { + int total = 0; + foreach (var currentClass in commandLibraries.Values) + total += currentClass.Count; + return total; + } + } +} diff --git a/Console.Waterworks.Core/Assistants/CommandsAssistant.cs b/Console.Waterworks.Core/Assistants/CommandsAssistant.cs new file mode 100644 index 0000000..1bfa177 --- /dev/null +++ b/Console.Waterworks.Core/Assistants/CommandsAssistant.cs @@ -0,0 +1,106 @@ +using Console.Waterworks.Core.Models; +using Console.Waterworks.Core.Specialists; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.Core.Assistants +{ + class CommandsAssistant + { + internal bool ValidateClass(Command command, Dictionary>> commandLibraries) + => (commandLibraries.ContainsKey(command.ClassName)) ? true : false; + + internal bool ValidateCommand(Command command, Dictionary>> commandLibraries) + { + var methodDict = commandLibraries[command.ClassName]; + return (methodDict.ContainsKey(command.Name)) ? true : false; + } + + internal string ExecuteBadCommandProcedure(Command command, ConsoleIOSpecialist consoleSpec) + { + consoleSpec.WriteErrorSuffix(); + return $"The command \'{command.Name}\' is not recognised."; + } + + internal bool ValidateParamArguments(Command command, List paramInfoList) + { + var requiredParams = paramInfoList.Where(p => p.IsOptional == false); + var optionalParams = paramInfoList.Where(p => p.IsOptional == true); + int requiredCount = requiredParams.Count(); + int optionalCount = optionalParams.Count(); + int providedCount = command.Arguments.Count(); + return (requiredCount > providedCount) ? false : true; + } + + internal string ExecuteMissingArgumentProcedure(Command command, List paramInfoList, ConsoleIOSpecialist consoleSpec) + { + var requiredParams = paramInfoList.Where(p => p.IsOptional == false); + var optionalParams = paramInfoList.Where(p => p.IsOptional == true); + int requiredCount = requiredParams.Count(); + int optionalCount = optionalParams.Count(); + int providedCount = command.Arguments.Count(); + return $"Missing required argument. {requiredCount} required, {optionalCount} optional, {providedCount} provided."; + } + + internal List GetParametreValueList(Command command, List paramInfoList) + { + var methodParameterValueList = new List(); + if (paramInfoList.Count() > 0) + { + foreach (var param in paramInfoList) + { + methodParameterValueList.Add(param.DefaultValue); + } + for (int i = 0; i < command.Arguments.Count(); i++) + { + var methodParam = paramInfoList.ElementAt(i); + var typeRequired = methodParam.ParameterType; + object value = null; + try + { + value = CoercionSpecialist.CoerceArgument(typeRequired, command.Arguments.ElementAt(i)); + methodParameterValueList.RemoveAt(i); + methodParameterValueList.Insert(i, value); + } + catch (ArgumentException ex) + { + string message = $"The value passed for argument '{methodParam.Name}' cannot be parsed to type '{typeRequired.Name}'"; // The exception message is being used instead, for now. + throw new ArgumentException(ex.Message); + } + } + } + return methodParameterValueList; + } + + internal Type BuildCommandLibraryClass(Command command, string commandsNamespace) + { + Assembly programAssembly = Assembly.GetEntryAssembly(); + Type commandLibraryClass = programAssembly.GetType($"{commandsNamespace}.{command.ClassName}"); + return commandLibraryClass; + } + + internal string InvokeCommand(Command command, Type typeInfo, object[] inputArguments) + { + try + { + //var result = typeInfo.GetMethod(command.Name).Invoke(command.Name, inputArguments).ToString(); + // return result.ToString(); + return typeInfo.GetMethod(command.Name).Invoke(command.Name, inputArguments).ToString(); + } + catch (TargetInvocationException ex) + { + throw ex.InnerException; + } + } + + internal object[] GetInputArguments(List methodParametreValueList) + { + object[] inputArguments = null; + if (methodParametreValueList.Count > 0) + inputArguments = methodParametreValueList.ToArray(); + return inputArguments; + } + } +} diff --git a/Console.Waterworks.Core/Attributes/DescriptionAttribute.cs b/Console.Waterworks.Core/Attributes/DescriptionAttribute.cs new file mode 100644 index 0000000..4be4620 --- /dev/null +++ b/Console.Waterworks.Core/Attributes/DescriptionAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Console.Waterworks.Core.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class DescriptionAttribute : Attribute + { + public readonly string Description; + + public DescriptionAttribute(string description) + { + Description = description; + } + } +} diff --git a/Console.Waterworks.Core/Attributes/ListCommandAttribute.cs b/Console.Waterworks.Core/Attributes/ListCommandAttribute.cs new file mode 100644 index 0000000..2c4c636 --- /dev/null +++ b/Console.Waterworks.Core/Attributes/ListCommandAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace Console.Waterworks.Core.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class ListCommandAttribute : Attribute + { + public readonly bool ShowCommand; + public ListCommandAttribute() + { + ShowCommand = true; + } + public ListCommandAttribute(bool show) + { + ShowCommand = show; + } + } +} diff --git a/Console.Waterworks.Core/Attributes/ParametersAttribute.cs b/Console.Waterworks.Core/Attributes/ParametersAttribute.cs new file mode 100644 index 0000000..354d738 --- /dev/null +++ b/Console.Waterworks.Core/Attributes/ParametersAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Console.Waterworks.Core.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class ParametersAttribute : Attribute + { + public readonly string Parameters; + public ParametersAttribute(string arguments) + { + Parameters = arguments; + } + } +} diff --git a/Console.Waterworks.Core/Attributes/UsageAttribute.cs b/Console.Waterworks.Core/Attributes/UsageAttribute.cs new file mode 100644 index 0000000..44b33e0 --- /dev/null +++ b/Console.Waterworks.Core/Attributes/UsageAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Console.Waterworks.Core.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class UsageAttribute : Attribute + { + public readonly string UsageExample; + public UsageAttribute(string example) + { + UsageExample = example; + } + } +} diff --git a/Console.Waterworks.Core/CW_Liaison.cs b/Console.Waterworks.Core/CW_Liaison.cs new file mode 100644 index 0000000..9894755 --- /dev/null +++ b/Console.Waterworks.Core/CW_Liaison.cs @@ -0,0 +1,32 @@ +using Console.Waterworks.Core.CoOrdinators; + +namespace Console.Waterworks.Core +{ + public class CW_Liaison + { + CoOrdinator _kevin = new CoOrdinator(); + + /// + /// Hands control over to Console.Waterworks.Core so the program can "run" as intended. + /// + /// The place where the program's command-methods are. + /// Information about the console program, stated in Assembly Information. + public void Run(string consoleCommandsNamespace,bool includeProgramInfo) + { + _kevin.PrepareConsoleEnvironment(); + if (includeProgramInfo == true) _kevin.DisplayProgramInfo(); + _kevin.RunProgram(consoleCommandsNamespace); + } + + /// + /// Displays all the attribute information it can find, attached to the command-methods, at run-time. + /// + /// + /// The returned string does not include the "help" information. + /// It returns a string to indicate the method has finished collating the attribute information and displaying the information in the console. + /// This method is intended to be use in a command-method. So, it aims to be compatible with command-method behaviour + /// + /// The location of the programs command-method. + public string RequestHelpDocumentation(string consoleCommandsNamespace) => _kevin.DisplayHelpSection(consoleCommandsNamespace); + } +} diff --git a/Console.Waterworks.Core/Class1.cs b/Console.Waterworks.Core/Class1.cs deleted file mode 100644 index 992fdc5..0000000 --- a/Console.Waterworks.Core/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Console.Waterworks.Core -{ - public class Class1 - { - } -} diff --git a/Console.Waterworks.Core/CoOrdinators/CoOrdinator.cs b/Console.Waterworks.Core/CoOrdinators/CoOrdinator.cs new file mode 100644 index 0000000..9cc939f --- /dev/null +++ b/Console.Waterworks.Core/CoOrdinators/CoOrdinator.cs @@ -0,0 +1,96 @@ +using Console.Waterworks.Core.Assistants; +using Console.Waterworks.Core.Constants; +using Console.Waterworks.Core.Loggers; +using Console.Waterworks.Core.Models; +using Console.Waterworks.Core.Specialists; +using System; + +namespace Console.Waterworks.Core.CoOrdinators +{ + class CoOrdinator + { + CoOrdinatorAssistant _assistant = new CoOrdinatorAssistant(); + ProgramInfoSpecialist _progInfoSpec = new ProgramInfoSpecialist(); + ConsoleIOSpecialist _consoleSpec = new ConsoleIOSpecialist(); + CommandsSpecialist _commSpec = new CommandsSpecialist(); + HelpSpecialist _helpSpec = new HelpSpecialist(); + CW_Logger _logger = new CW_Logger(); + + internal void PrepareConsoleEnvironment() + { + _logger.LogInfoMessage($"Preparing console.."); + _consoleSpec.PrepareConsoleEnvironment(); + _consoleSpec.SetInputPrompt($"{_progInfoSpec.GetProductName()}> "); + _logger.LogSuccessMessage("Console environment is now setup"); + } + + internal void DisplayProgramInfo() + { + _logger.LogInfoMessage($"Displaying program information.."); + _assistant.SetConsoleTitle(_logger, _progInfoSpec, _consoleSpec); + _assistant.OutputProgramInfo(_logger, _progInfoSpec, _consoleSpec); + } + + internal void RunProgram(string commandsNamespace) + { + _logger.LogInfoMessage("Attempting to building commands library.."); + var commandClasses = _commSpec.GetCommandClasses(commandsNamespace); + var commandLibraries = _commSpec.GetCommandLibraries(commandClasses); + _assistant.LogCommandGatheringAttempt(commandClasses, commandLibraries, _logger); + while (true) + { + var consoleInput = _consoleSpec.GetInputFromUser(); + if (string.IsNullOrEmpty(consoleInput)) continue; + try + { + _logger.LogInfoMessage("Parsing input from user.."); + var command = new Command(consoleInput, commandsNamespace, CW_Constants.COMMAND_CLASS_NAME); + _logger.LogInfoMessage("Attempting to execute command.."); + var result = _commSpec.ExecuteCommand(commandsNamespace, command, commandClasses, commandLibraries, _consoleSpec); + _consoleSpec.WriteOutputToConsole(result); + _logger.LogSuccessMessage("Command has been executed."); + _logger.LogNoteMessage("An error message does not mean the command did not execute properly or sucessfully."); + } + catch (Exception ex) + { + _logger.LogErrorMessage("Command was not successfully executed. See the error message in console for further details"); + _consoleSpec.WriteErrorMessage(ex.Message); + } + _logger.LogInfoMessage("Resetting the console's formatting.."); + _logger.LogNoteMessage("This is to make sure no error messages or one-off formating change corrupts the console environment."); + _consoleSpec.ResetConsoleColour(); + _logger.LogSuccessMessage("Console's formating has been reset"); + } + } + + internal string DisplayHelpSection(string commandsNamespace) + { + _logger.LogInfoMessage("Attempting to display help section.."); + var commandClasses = _commSpec.GetCommandClasses(commandsNamespace); + if (commandClasses.Count == 0) + { + _logger.LogErrorMessage("Unable to find any help information. Make sure the commands hace the correct atrributes applied"); + _consoleSpec.WriteErrorSuffix(); + return "Unable to find help information"; + } + _logger.LogSuccessMessage("Found help information"); + _logger.LogInfoMessage("Attempting to display the help information.."); + _consoleSpec.WriteOutputToConsole("Displaying Help section..."); + _consoleSpec.LineBreak(); + var commandMembers = _helpSpec.GetCommandMembers(commandClasses); + foreach (var command in commandMembers) + { + if (_helpSpec.ListCommand(command) == true) + { + _consoleSpec.WriteOutputToConsole($"Command Name: {command.Name}"); + _consoleSpec.WriteOutputToConsole($"Parameters: {_helpSpec.GetParametres(command)}"); + _consoleSpec.WriteOutputToConsole($"Description: {_helpSpec.GetDescription(command)}"); + _consoleSpec.WriteOutputToConsole($"Example: {_helpSpec.GetUsageExamples(command)}"); + _consoleSpec.LineBreak(); + } + } + _logger.LogSuccessMessage("Help section displayed in the console"); + return "End of Help section."; + } + } +} diff --git a/Console.Waterworks.Core/Constants/CW_Constants.cs b/Console.Waterworks.Core/Constants/CW_Constants.cs new file mode 100644 index 0000000..c7ea7fa --- /dev/null +++ b/Console.Waterworks.Core/Constants/CW_Constants.cs @@ -0,0 +1,18 @@ +namespace Console.Waterworks.Core.Constants +{ + class CW_Constants + { + /// + /// This represents the maximum number of properties the console can display when the client "DisplayProgramInfo" to true. + /// This is used mostly for logging purposes. + /// + public const int PROGRAM_INFO_PROPERTIES_COUNT = 4; + + /// + /// This specifies the name of the class Console.Waterworks.Core looks for when it tries to build its command library. + /// This class should not be in Console.Waterworks.Core. + /// It should be in the clients console program. + /// + public const string COMMAND_CLASS_NAME = "ConsoleCommands"; + } +} diff --git a/Console.Waterworks.Core/Loggers/CW_Logger.cs b/Console.Waterworks.Core/Loggers/CW_Logger.cs new file mode 100644 index 0000000..2f28c8e --- /dev/null +++ b/Console.Waterworks.Core/Loggers/CW_Logger.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace Console.Waterworks.Core.Loggers +{ + class CW_Logger + { + internal void LogInfoMessage(string message) => Debug.WriteLine($"[Console.Waterworks.Core] INFO: {message}."); + internal void LogNoteMessage(string message) => Debug.WriteLine($"[Console.Waterworks.Core] NOTE: {message}."); + internal void LogSuccessMessage(string message) => Debug.WriteLine($"[Console.Waterworks.Core] SUCCESS: {message}."); + internal void LogErrorMessage(string message) => Debug.WriteLine($"[Console.Waterworks.Core] ERROR: {message}."); + } +} diff --git a/Console.Waterworks.Core/Models/Command.cs b/Console.Waterworks.Core/Models/Command.cs new file mode 100644 index 0000000..3641dc1 --- /dev/null +++ b/Console.Waterworks.Core/Models/Command.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Console.Waterworks.Core.Models +{ + class Command + { + public string Name { get; set; } + public string ClassName { get; set; } + + List _arguments; + public IEnumerable Arguments + { + get { return _arguments; } + } + + /* + * Let us assume the end-user entered: [> Say "Hello, World!" 2]. + * The input entered needs to be parse and seperated. + * This is so ClassName and Arguments can be set. + * Once everything has been parsed, _arguments should look like the following, + * _arguments["Hello, World!", "2"] (This will be "mirrored" in Arguments). + * Using the example at the top of this comment block, the end result will look something like, + * ClassName = "Say" | Arguments = ["Hello, World!", "2"] + * This object will then be used as the guide to locate the command-method this model describes. + */ + public Command(string input, string commandsNamespace, string className) + { + var splitInput = Regex.Split(input, "(?<=^[^\"]*(?:\"[^\"]*\"[^\"]*)*) (?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); + _arguments = new List(); + for (int i = 0; i < splitInput.Length; i++) + { + if (i == 0) + establishCommand(splitInput, i, className); + else + establishArguments(splitInput, i); + } + } + + void establishCommand(string[] splitInput, int splitInputLocation, string nameOfClass) + { + Name = splitInput[splitInputLocation]; + ClassName = nameOfClass; // The default is "ConsoleCommands.cs" + string[] splitCommandInput = splitInput[0].Split('.'); + + // This next part is a fancy feature and will almost never be used. + if (splitCommandInput.Length == 2) + { + // In short, it takes something like [> TestClass1.TestMethod2 "Hello, World"]. + // And, it isolates the "TestClass1" and TestMethod2" parts. + ClassName = splitCommandInput[0]; // "TestClass1" + Name = splitCommandInput[1]; // TestMethod2" + } + } + + void establishArguments(string[] splitInput, int splitInputLocation) + { + var inputArgument = splitInput[splitInputLocation]; + string argument = inputArgument; + + // This bit is just a check to see if the current part of the input is a quoted string. + // If it is quoted text, "matches" will hold the various parts of the quote. + var regex = new Regex("\"(.*?)\"", RegexOptions.Singleline); + var matches = regex.Match(inputArgument); + if (matches.Captures.Count > 0) + { + // Each part of the quoted text is added to "argument" one-by-one. + // The aim is to add the quoted text as a single line. + var captureQuotedText = new Regex("[^\"]*[^\"]"); + var quoted = captureQuotedText.Match(matches.Captures[0].Value); + argument = quoted.Captures[0].Value; + } + _arguments.Add(argument); + } + } +} diff --git a/Console.Waterworks.Core/Specialists/CoercionSpecialist.cs b/Console.Waterworks.Core/Specialists/CoercionSpecialist.cs new file mode 100644 index 0000000..f05c6fd --- /dev/null +++ b/Console.Waterworks.Core/Specialists/CoercionSpecialist.cs @@ -0,0 +1,174 @@ +using System; + +namespace Console.Waterworks.Core.Specialists +{ + internal static class CoercionSpecialist + { + internal static object CoerceArgument(Type requiredType, string inputValue) + { + var requiredTypeCode = Type.GetTypeCode(requiredType); + string exceptionMessage = $"Cannnot coerce the input argument {inputValue} to required type {requiredType.Name}"; + + object result = null; + switch (requiredTypeCode) + { + case TypeCode.String: + result = inputValue; + break; + + case TypeCode.Int16: + short number16; + if (Int16.TryParse(inputValue, out number16)) + { + result = number16; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.Int32: + int number32; + if (Int32.TryParse(inputValue, out number32)) + { + result = number32; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.Int64: + long number64; + if (Int64.TryParse(inputValue, out number64)) + { + result = number64; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.Boolean: + bool trueFalse; + if (bool.TryParse(inputValue, out trueFalse)) + { + result = trueFalse; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.Byte: + byte byteValue; + if (byte.TryParse(inputValue, out byteValue)) + { + result = byteValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.Char: + char charValue; + if (char.TryParse(inputValue, out charValue)) + { + result = charValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + + case TypeCode.DateTime: + DateTime dateValue; + if (DateTime.TryParse(inputValue, out dateValue)) + { + result = dateValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.Decimal: + Decimal decimalValue; + if (Decimal.TryParse(inputValue, out decimalValue)) + { + result = decimalValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.Double: + Double doubleValue; + if (Double.TryParse(inputValue, out doubleValue)) + { + result = doubleValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.Single: + Single singleValue; + if (Single.TryParse(inputValue, out singleValue)) + { + result = singleValue; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.UInt16: + UInt16 uInt16Value; + if (UInt16.TryParse(inputValue, out uInt16Value)) + { + result = uInt16Value; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.UInt32: + UInt32 uInt32Value; + if (UInt32.TryParse(inputValue, out uInt32Value)) + { + result = uInt32Value; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + case TypeCode.UInt64: + UInt64 uInt64Value; + if (UInt64.TryParse(inputValue, out uInt64Value)) + { + result = uInt64Value; + } + else + { + throw new ArgumentException(exceptionMessage); + } + break; + default: + throw new ArgumentException(exceptionMessage); + } + return result; + } + } +} diff --git a/Console.Waterworks.Core/Specialists/CommandsSpecialist.cs b/Console.Waterworks.Core/Specialists/CommandsSpecialist.cs new file mode 100644 index 0000000..b6091e5 --- /dev/null +++ b/Console.Waterworks.Core/Specialists/CommandsSpecialist.cs @@ -0,0 +1,60 @@ +using Console.Waterworks.Core.Assistants; +using Console.Waterworks.Core.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.Core.Specialists +{ + class CommandsSpecialist + { + CommandsAssistant _assistant = new CommandsAssistant(); + + internal List GetCommandClasses(string commandsNameSpace) + { + string theNamespace = commandsNameSpace; + var q = from t in Assembly.GetEntryAssembly().GetTypes() + where t.GetTypeInfo().IsClass && t.Namespace == theNamespace + select t; + return q.ToList(); + } + + internal Dictionary>> GetCommandLibraries(List commandClasses) + { + var commandLibraries = new Dictionary>>(); + foreach (var commandClass in commandClasses) + { + var methods = commandClass.GetMethods(BindingFlags.Static | BindingFlags.Public); + var methodDictionary = new Dictionary>(); + foreach (var method in methods) + { + string commandName = method.Name; + methodDictionary.Add(commandName, method.GetParameters()); + } + commandLibraries.Add(commandClass.Name, methodDictionary); + } + return commandLibraries; + } + + internal string ExecuteCommand(string commandsNamespace, Command command, List commandClasses, Dictionary>> commandLibraries, ConsoleIOSpecialist consoleSpec) + { + var classValidated = _assistant.ValidateClass(command, commandLibraries); + var commandNameValidated = _assistant.ValidateCommand(command, commandLibraries); + Dictionary> methodDictionary; + if (classValidated != true || commandNameValidated != true) + return _assistant.ExecuteBadCommandProcedure(command, consoleSpec); + else + methodDictionary = commandLibraries[command.ClassName]; + var paramInfoList = methodDictionary[command.Name].ToList(); + var paramsValidated = _assistant.ValidateParamArguments(command, paramInfoList); + if (paramsValidated == false) + return _assistant.ExecuteMissingArgumentProcedure(command, paramInfoList, consoleSpec); + var methodParametreValueList = _assistant.GetParametreValueList(command, paramInfoList); + var typeInfo = _assistant.BuildCommandLibraryClass(command, commandsNamespace); + var inputArguments = _assistant.GetInputArguments(methodParametreValueList); + var result = _assistant.InvokeCommand(command, typeInfo, inputArguments); + return result; + } + } +} diff --git a/Console.Waterworks.Core/Specialists/ConsoleIOSpecialist.cs b/Console.Waterworks.Core/Specialists/ConsoleIOSpecialist.cs new file mode 100644 index 0000000..54edcc9 --- /dev/null +++ b/Console.Waterworks.Core/Specialists/ConsoleIOSpecialist.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace Console.Waterworks.Core.Specialists +{ + class ConsoleIOSpecialist + { + string _consolePrompt; + + internal void PrepareConsoleEnvironment() + { +#if DEBUG + System.Console.BackgroundColor = ConsoleColor.DarkGray; + System.Console.Clear(); + System.Console.ForegroundColor = ConsoleColor.White; + System.Console.WriteLine(" ------------ "); + System.Console.WriteLine("| DEBUG MODE |"); + System.Console.WriteLine(" ------------ "); +#endif + } + + internal void SetConsoleTitle(string title) => System.Console.Title = title; + + internal void SetInputPrompt(string prompt) => _consolePrompt = prompt; + + internal void WriteProgramInfo(List programInfo) + { + foreach (var line in programInfo) + { + if (!string.IsNullOrEmpty(line)) System.Console.WriteLine(line); + } + } + + internal string GetInputFromUser() + { + System.Console.ForegroundColor = ConsoleColor.Green; + System.Console.Write(_consolePrompt); + System.Console.ResetColor(); + return System.Console.ReadLine(); + } + + internal void WriteOutputToConsole(string message) => System.Console.WriteLine(message); + + internal void LineBreak() => System.Console.WriteLine(); + + internal void WriteErrorSuffix() + { + System.Console.ForegroundColor = ConsoleColor.Red; + System.Console.Write("[ERROR] "); + } + + internal void WriteErrorMessage(string message) + { + System.Console.ForegroundColor = ConsoleColor.Red; + System.Console.WriteLine($"[ERROR] {message}"); + System.Console.ResetColor(); + } + + internal void ResetConsoleColour() => System.Console.ResetColor(); + + internal void WriteInfoSuffix() + { + System.Console.ForegroundColor = ConsoleColor.Yellow; + System.Console.Write("[INFO] "); + } + } +} diff --git a/Console.Waterworks.Core/Specialists/HelpSpecialist.cs b/Console.Waterworks.Core/Specialists/HelpSpecialist.cs new file mode 100644 index 0000000..1b6ab1c --- /dev/null +++ b/Console.Waterworks.Core/Specialists/HelpSpecialist.cs @@ -0,0 +1,63 @@ +using Console.Waterworks.Core.Attributes; +using Console.Waterworks.Core.Loggers; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Console.Waterworks.Core.Specialists +{ + class HelpSpecialist + { + CW_Logger _logger = new CW_Logger(); + + internal MemberInfo[] GetCommandMembers(List commandClasses) => commandClasses[0].GetMembers(); + + internal bool ListCommand(MemberInfo command) + { + var customAttributes = GetCustomAttributes(command); + var castedAttributes = CheckForAttributesType(customAttributes); + return (castedAttributes != null) ? castedAttributes.ShowCommand : false; + } + + T CheckForAttributesType(object[] attributes) + { + T theAttributes = default(T); + for (int i = 0; i < attributes.Length; i++) + { + try + { + theAttributes = (T)attributes[i]; + break; + } + catch (Exception) + { + _logger.LogInfoMessage("The attempted casting attempt failed. Do not panic. This was expected"); + } + } + return theAttributes; + } + + object[] GetCustomAttributes(MemberInfo command) => command.GetCustomAttributes(true) as object[]; + + internal object GetParametres(MemberInfo command) + { + var attributes = GetCustomAttributes(command); + var castedAttributes = CheckForAttributesType(attributes); + return (castedAttributes != null) ? castedAttributes.Parameters : "Parameters values could not be found"; + } + + internal object GetDescription(MemberInfo command) + { + var attributes = GetCustomAttributes(command); + var castedAttributes = CheckForAttributesType(attributes); + return (castedAttributes != null) ? castedAttributes.Description : "Description could not be found"; + } + + internal object GetUsageExamples(MemberInfo command) + { + var attributes = GetCustomAttributes(command); + var castedAttributes = CheckForAttributesType(attributes); + return (castedAttributes != null) ? castedAttributes.UsageExample : "Example could not be found"; + } + } +} diff --git a/Console.Waterworks.Core/Specialists/ProgramInfoSpecialist.cs b/Console.Waterworks.Core/Specialists/ProgramInfoSpecialist.cs new file mode 100644 index 0000000..88b783f --- /dev/null +++ b/Console.Waterworks.Core/Specialists/ProgramInfoSpecialist.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.Core.Specialists +{ + class ProgramInfoSpecialist + { + internal List GatherProgramInfomation() + { + var info = new List() + { + addProductLine(), + addCompanyLine(), + addCopyRightLine(), + addProductDescription() + }; + return info; + } + + private string addCopyRightLine() + { + var assembly = Assembly.GetEntryAssembly(); + var copyRightAttribute = assembly.GetCustomAttributes().FirstOrDefault(); + return copyRightAttribute?.Copyright; + } + + string addProductLine() + { + var name = GetProductName(); + var buildInfo = getBuildInfo(); + return (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(buildInfo)) ? $"{name} {buildInfo}" : null; + } + + string addCompanyLine() + { + var assembly = Assembly.GetEntryAssembly(); + var companyAttribute = assembly.GetCustomAttributes().FirstOrDefault(); + return companyAttribute?.Company; + } + + string addProductDescription() + { + var assembly = Assembly.GetEntryAssembly(); + var descriptionAttribute = assembly.GetCustomAttributes().FirstOrDefault(); + return descriptionAttribute?.Description; + } + + internal string GetProductName() + { + var assembly = Assembly.GetEntryAssembly(); + var productAttribute = assembly.GetCustomAttributes().FirstOrDefault(); + return productAttribute?.Product; + } + + string getBuildInfo() => Assembly.GetEntryAssembly().GetName().Version.ToString(); + } +}