diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c4efe2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/Console.Waterworks/CW_Console/App.config b/Console.Waterworks/CW_Console/App.config new file mode 100644 index 0000000..9d2c7ad --- /dev/null +++ b/Console.Waterworks/CW_Console/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Console.Waterworks/CW_Console/CW_Console.csproj b/Console.Waterworks/CW_Console/CW_Console.csproj new file mode 100644 index 0000000..25c60e4 --- /dev/null +++ b/Console.Waterworks/CW_Console/CW_Console.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {E26D7001-2A4E-4618-8C27-8BF504993EE9} + Exe + CW_Console + CW_Console + v4.7 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + {d1354760-61f7-4aee-8d6f-e78a463ee3c9} + Console.Waterworks + + + + \ No newline at end of file diff --git a/Console.Waterworks/CW_Console/ConsoleCommands.cs b/Console.Waterworks/CW_Console/ConsoleCommands.cs new file mode 100644 index 0000000..8cc8b86 --- /dev/null +++ b/Console.Waterworks/CW_Console/ConsoleCommands.cs @@ -0,0 +1,106 @@ +using Console.Waterworks; +using Console.Waterworks.Attributes; +using System; + +namespace CW_Console +{ + public class ConsoleCommands + { + [ListCommand()] + [Description("Displays the Help section at run-time")] + [Parameters("None")] + [Usage("CW_Console> Help")] + public static string Help() + { + CW_Liaison liaison = new CW_Liaison(); + return liaison.RequestHelpDocumentation("CW_Console"); + } + + [ListCommand()] + [Description("Outputs to the console the command has been completed.")] + [Parameters("None")] + [Usage("CW_Console> Command1")] + public static string Command1() => "Command1 has completed"; + + [ListCommand()] + [Description("Repeats back to the user the string they entered.")] + [Parameters(" input")] + [Usage("CW_Console> Command2 \"Hello, World.\"")] + public static string Command2(string input) => $"Command2 has completed... {input} was entered"; + + [ListCommand()] + [Description("Repeats back to the user what int they entered.")] + [Parameters(" int1")] + [Usage("CW_Console> Command3 31")] + public static string Command3(int input) => $"Command3 has completed... The number {input} was entered"; + + [ListCommand()] + [Description("Takes the two ints and adds them together.")] + [Parameters(" int, int2")] + [Usage("CW_Console> Command4 31 10")] + public static string Command4(int int1, int int2) => $"Command4 has completed... {int1} and {int2} was entered and make {int1 + int2} when added together"; + + [ListCommand()] + [Description("Take the int and double and adds them together.")] + [Parameters(" int1, double1")] + [Usage("CW_Console> Command5 31 25.4")] + public static string Command5(int int1, double double1) => $"Command5 has completed... {int1} and {double1} was entered and make {int1 + double1} when added together"; + + [ListCommand()] + [Description("Terminates the program.")] + [Parameters("None")] + [Usage("CW_Console> Quit")] + public static void Quit() => Environment.Exit(-1); + + #region Alias-Methods + + /* + * A NOTE ABOUT ALIAS-METHODS - DELETE AFTER READING + * ========================================================= + * These methods are shorthand versions of the ones above. + * For the most part, this is a hack. + * But, it is a useful one. + * When new users start using your console program, they need help getting started. + * This is why I recommend using descriptive names for your command-methods. + * But, when your users become more familiar with the program, they will want terser commands. + * They will no longer need their hand holding. This is where these alias-commands come in. + */ + + [ListCommand(false)] // change to true or delete "false" for it to show at run-time. + [Description("Alias for Command1. See Command1 for details.")] + [Parameters("None")] + [Usage("CW_Console> c1")] + public static string c1() => Command1(); + + [ListCommand(false)] + [Description("Alias for Command2. See Command2 for details.")] + [Parameters("None")] + [Usage("CW_Console> c2 \"Hello, World.\"")] + public static string c2(string input) => Command2(input); + + [ListCommand(false)] + [Description("Alias for Command3. See Command3 for details.")] + [Parameters("None")] + [Usage("CW_Console> c3 78")] + public static string c3(int input) => Command3(input); + + [ListCommand(false)] + [Description("Alias for Command4. See Command4 for details.")] + [Parameters("None")] + [Usage("CW_Console> c4 24 67")] + public static string c4(int int1, int int2) => Command4(int1, int2); + + [ListCommand(false)] + [Description("Alias for Command5. See Command5 for details.")] + [Parameters("None")] + [Usage("CW_Console> c5 12 46.3")] + public static string c5(int int1, double double1) => Command5(int1, double1); + + [ListCommand(false)] + [Description("Alias for Quit. See Quit for details.")] + [Parameters("None")] + [Usage("CW_Console> quit")] + public static void quit() => Quit(); + #endregion + } +} diff --git a/Console.Waterworks/CW_Console/Program.cs b/Console.Waterworks/CW_Console/Program.cs new file mode 100644 index 0000000..8c1d3cc --- /dev/null +++ b/Console.Waterworks/CW_Console/Program.cs @@ -0,0 +1,15 @@ +using Console.Waterworks; + +namespace CW_Console +{ + class Program + { + static void Main(string[] args) + { + CW_Liaison bob = new CW_Liaison(); + bob.Run("CW_Console", true); + // That is it. You are done here. + // Head over to ConsoleCommands.cs to begin adding features... + } + } +} diff --git a/Console.Waterworks/CW_Console/Properties/AssemblyInfo.cs b/Console.Waterworks/CW_Console/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a104d39 --- /dev/null +++ b/Console.Waterworks/CW_Console/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CW_Console")] +[assembly: AssemblyDescription("This is the console program for testing the Console.Waterworks Nuget package.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Craig Oates")] +[assembly: AssemblyProduct("CW_Console")] +[assembly: AssemblyCopyright("Copyright © 2017 Craig Oates")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e26d7001-2a4e-4618-8c27-8bf504993ee9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguage("en-GB")] + diff --git a/Console.Waterworks/Console.Waterworks.sln b/Console.Waterworks/Console.Waterworks.sln new file mode 100644 index 0000000..dd94c8a --- /dev/null +++ b/Console.Waterworks/Console.Waterworks.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console.Waterworks", "Console.Waterworks\Console.Waterworks.csproj", "{D1354760-61F7-4AEE-8D6F-E78A463EE3C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CW_Console", "CW_Console\CW_Console.csproj", "{E26D7001-2A4E-4618-8C27-8BF504993EE9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D1354760-61F7-4AEE-8D6F-E78A463EE3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1354760-61F7-4AEE-8D6F-E78A463EE3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1354760-61F7-4AEE-8D6F-E78A463EE3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1354760-61F7-4AEE-8D6F-E78A463EE3C9}.Release|Any CPU.Build.0 = Release|Any CPU + {E26D7001-2A4E-4618-8C27-8BF504993EE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E26D7001-2A4E-4618-8C27-8BF504993EE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E26D7001-2A4E-4618-8C27-8BF504993EE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E26D7001-2A4E-4618-8C27-8BF504993EE9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BC405CAC-EBE7-4A72-AEE1-93C99AE240E3} + EndGlobalSection +EndGlobal diff --git a/Console.Waterworks/Console.Waterworks/Assistants/CoOrdinatorAssistant.cs b/Console.Waterworks/Console.Waterworks/Assistants/CoOrdinatorAssistant.cs new file mode 100644 index 0000000..2f661cb --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Assistants/CoOrdinatorAssistant.cs @@ -0,0 +1,52 @@ +using Console.Waterworks.Constants; +using Console.Waterworks.Loggers; +using Console.Waterworks.Specialists; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Console.Waterworks.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/Console.Waterworks/Assistants/CommandsAssistant.cs b/Console.Waterworks/Console.Waterworks/Assistants/CommandsAssistant.cs new file mode 100644 index 0000000..cfa76cf --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Assistants/CommandsAssistant.cs @@ -0,0 +1,105 @@ +using Console.Waterworks.Models; +using Console.Waterworks.Specialists; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.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.InvokeMember(command.Name, BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, inputArguments); + return result.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/Console.Waterworks/Attributes/DescriptionAttribute.cs b/Console.Waterworks/Console.Waterworks/Attributes/DescriptionAttribute.cs new file mode 100644 index 0000000..54dbbee --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Attributes/DescriptionAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Console.Waterworks.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class DescriptionAttribute : Attribute + { + public readonly string Description; + + public DescriptionAttribute(string description) + { + Description = description; + } + } +} diff --git a/Console.Waterworks/Console.Waterworks/Attributes/ListCommandAttribute.cs b/Console.Waterworks/Console.Waterworks/Attributes/ListCommandAttribute.cs new file mode 100644 index 0000000..edc452b --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Attributes/ListCommandAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace Console.Waterworks.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/Console.Waterworks/Attributes/ParametersAttribute.cs b/Console.Waterworks/Console.Waterworks/Attributes/ParametersAttribute.cs new file mode 100644 index 0000000..d412fc7 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Attributes/ParametersAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Console.Waterworks.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class ParametersAttribute : Attribute + { + public readonly string Parameters; + public ParametersAttribute(string arguments) + { + Parameters = arguments; + } + } +} diff --git a/Console.Waterworks/Console.Waterworks/Attributes/UsageAttribute.cs b/Console.Waterworks/Console.Waterworks/Attributes/UsageAttribute.cs new file mode 100644 index 0000000..d77ff68 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Attributes/UsageAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Console.Waterworks.Attributes +{ + [AttributeUsage(AttributeTargets.All)] + public class UsageAttribute : Attribute + { + public readonly string UsageExample; + public UsageAttribute(string example) + { + UsageExample = example; + } + } +} diff --git a/Console.Waterworks/Console.Waterworks/CW_Liaison.cs b/Console.Waterworks/Console.Waterworks/CW_Liaison.cs new file mode 100644 index 0000000..46a200d --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/CW_Liaison.cs @@ -0,0 +1,32 @@ +using Console.Waterworks.CoOrdinators; + +namespace Console.Waterworks +{ + public class CW_Liaison + { + CoOrdinator _kevin = new CoOrdinator(); + + /// + /// Hands control over to Console.Waterworks 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/Console.Waterworks/CoOrdinators/CoOrdinator.cs b/Console.Waterworks/Console.Waterworks/CoOrdinators/CoOrdinator.cs new file mode 100644 index 0000000..74f9ffd --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/CoOrdinators/CoOrdinator.cs @@ -0,0 +1,96 @@ +using Console.Waterworks.Assistants; +using Console.Waterworks.Constants; +using Console.Waterworks.Loggers; +using Console.Waterworks.Models; +using Console.Waterworks.Specialists; +using System; + +namespace Console.Waterworks.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/Console.Waterworks/Console.Waterworks.csproj b/Console.Waterworks/Console.Waterworks/Console.Waterworks.csproj new file mode 100644 index 0000000..4d27078 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Console.Waterworks.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {D1354760-61F7-4AEE-8D6F-E78A463EE3C9} + Library + Properties + Console.Waterworks + Console.Waterworks + v4.7 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + WaterWorksIcon.ico + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Waterworks/Console.Waterworks/Constants/CW_Constants.cs b/Console.Waterworks/Console.Waterworks/Constants/CW_Constants.cs new file mode 100644 index 0000000..52e3b4f --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Constants/CW_Constants.cs @@ -0,0 +1,18 @@ +namespace Console.Waterworks.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 looks for when it tries to build its command library. + /// This class should not be in Console.WaterWorks. + /// It should be in the clients console program. + /// + public const string COMMAND_CLASS_NAME = "ConsoleCommands"; + } +} diff --git a/Console.Waterworks/Console.Waterworks/Loggers/CW_Logger.cs b/Console.Waterworks/Console.Waterworks/Loggers/CW_Logger.cs new file mode 100644 index 0000000..89f9f52 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Loggers/CW_Logger.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace Console.Waterworks.Loggers +{ + class CW_Logger + { + internal void LogInfoMessage(string message) => Debug.WriteLine($"[Console.WaterWorks] INFO: {message}."); + internal void LogNoteMessage(string message) => Debug.WriteLine($"[Console.WaterWorks] NOTE: {message}."); + internal void LogSuccessMessage(string message) => Debug.WriteLine($"[Console.WaterWorks] SUCCESS: {message}."); + internal void LogErrorMessage(string message) => Debug.WriteLine($"[Console.WaterWorks] ERROR: {message}."); + } +} diff --git a/Console.Waterworks/Console.Waterworks/Models/Command.cs b/Console.Waterworks/Console.Waterworks/Models/Command.cs new file mode 100644 index 0000000..1c3360e --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Models/Command.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Console.Waterworks.Models +{ + class Command + { + public string Name { get; set; } + public string ClassName { get; set; } + + private List _arguments; + public IEnumerable Arguments + { + get + { + return _arguments; + } + } + + public Command(string input, string commandsNamespace, string className) + { + // Ugly regex to split string on spaces, but preserve quoted text intact: + var stringArray = + Regex.Split(input, "(?<=^[^\"]*(?:\"[^\"]*\"[^\"]*)*) (?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); + + _arguments = new List(); + for (int i = 0; i < stringArray.Length; i++) + { + // The first element is always the command: + if (i == 0) + { + Name = stringArray[i]; + + // Set the class name + ClassName = className; + string[] s = stringArray[0].Split('.'); + if (s.Length == 2) + { + ClassName = s[0]; + Name = s[1]; + } + } + else + { + var inputArgument = stringArray[i]; + + // Assume that most of the time, the input argument is NOT quoted text: + string argument = inputArgument; + + // Is the argument a quoted text string? + var regex = new Regex("\"(.*?)\"", RegexOptions.Singleline); + var match = regex.Match(inputArgument); + + // If it IS quoted, there will be at least one capture: + if (match.Captures.Count > 0) + { + // Get the unquoted text from within the qoutes: + var captureQuotedText = new Regex("[^\"]*[^\"]"); + var quoted = captureQuotedText.Match(match.Captures[0].Value); + + // The argument should include all text from between the quotes + // as a single string: + argument = quoted.Captures[0].Value; + } + _arguments.Add(argument); + } + } + } + } +} diff --git a/Console.Waterworks/Console.Waterworks/Properties/AssemblyInfo.cs b/Console.Waterworks/Console.Waterworks/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7d1c0ee --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Console.Waterworks")] +[assembly: AssemblyDescription("Console.Waterworks is a Nuget package which helps you write command-based console programs in .Net.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Console.Waterworks")] +[assembly: AssemblyCopyright("Copyright © 2017 Craig Oates")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d1354760-61f7-4aee-8d6f-e78a463ee3c9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: NeutralResourcesLanguage("en-GB")] + diff --git a/Console.Waterworks/Console.Waterworks/Specialists/CoercionSpecialist.cs b/Console.Waterworks/Console.Waterworks/Specialists/CoercionSpecialist.cs new file mode 100644 index 0000000..ffae1be --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Specialists/CoercionSpecialist.cs @@ -0,0 +1,174 @@ +using System; + +namespace Console.Waterworks.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/Console.Waterworks/Specialists/CommandsSpecialist.cs b/Console.Waterworks/Console.Waterworks/Specialists/CommandsSpecialist.cs new file mode 100644 index 0000000..534f8cc --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Specialists/CommandsSpecialist.cs @@ -0,0 +1,60 @@ +using Console.Waterworks.Assistants; +using Console.Waterworks.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.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.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/Console.Waterworks/Specialists/ConsoleIOSpecialist.cs b/Console.Waterworks/Console.Waterworks/Specialists/ConsoleIOSpecialist.cs new file mode 100644 index 0000000..983d3eb --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Specialists/ConsoleIOSpecialist.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace Console.Waterworks.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/Console.Waterworks/Specialists/HelpSpecialist.cs b/Console.Waterworks/Console.Waterworks/Specialists/HelpSpecialist.cs new file mode 100644 index 0000000..b7df353 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Specialists/HelpSpecialist.cs @@ -0,0 +1,63 @@ +using Console.Waterworks.Attributes; +using Console.Waterworks.Loggers; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Console.Waterworks.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); + + 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/Console.Waterworks/Specialists/ProgramInfoSpecialist.cs b/Console.Waterworks/Console.Waterworks/Specialists/ProgramInfoSpecialist.cs new file mode 100644 index 0000000..c8dfe72 --- /dev/null +++ b/Console.Waterworks/Console.Waterworks/Specialists/ProgramInfoSpecialist.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Console.Waterworks.Specialists +{ + class ProgramInfoSpecialist + { + internal List GatherProgramInfomation() + { + var info = new List() + { + addProductLine(), + addCompanyLine(), + addCopyRightLine(), + addProductDescription() + }; + return info; + } + + private string addCopyRightLine() + { + Assembly assembly = Assembly.GetEntryAssembly(); + var copyRightAttribute = assembly + .GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false) + .OfType() + .FirstOrDefault(); + return (copyRightAttribute != null) ? copyRightAttribute.Copyright : null; + } + + string addProductLine() + { + var name = GetProductName(); + var buildInfo = getBuildInfo(); + return (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(buildInfo)) ? $"{name} {buildInfo}" : null; + } + + string addCompanyLine() + { + Assembly assembly = Assembly.GetEntryAssembly(); + var companyAttribute = assembly + .GetCustomAttributes(typeof(AssemblyCompanyAttribute), false) + .OfType() + .FirstOrDefault(); + return (companyAttribute != null) ? companyAttribute.Company : null; + } + + string addProductDescription() + { + Assembly assembly = Assembly.GetEntryAssembly(); + var descriptionAttribute = assembly + .GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false) + .OfType() + .FirstOrDefault(); + return (descriptionAttribute != null) ? descriptionAttribute.Description : null; + } + + internal string GetProductName() + { + Assembly assembly = Assembly.GetEntryAssembly(); + var productAttribute = assembly + .GetCustomAttributes(typeof(AssemblyProductAttribute), false) + .OfType() + .FirstOrDefault(); + return (productAttribute != null) ? productAttribute.Product : null; + } + + string getBuildInfo() => Assembly.GetEntryAssembly().GetName().Version.ToString(); + } +} diff --git a/Console.Waterworks/Console.Waterworks/WaterWorksIcon.ico b/Console.Waterworks/Console.Waterworks/WaterWorksIcon.ico new file mode 100644 index 0000000..ca3bed5 Binary files /dev/null and b/Console.Waterworks/Console.Waterworks/WaterWorksIcon.ico differ diff --git a/README.md b/README.md index 936ab87..e98dbfa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,100 @@ -# Console.Waterworks -Console.Waterworks is a Nuget package which helps you write command-based console programs in .Net. +# Console.Waterworks - This is incomplete + +Console.Waterworks is a Nuget package. It aims to help you write extendable and command-based console programs in C# and .Net. +One of the main struggles with writing software is the need to write "plumbing code". It is the code which does not solve the main problem but it is necessary for us to reach a solution. This is why we all feel frustrated when we need to write it. It feels like we are going the long way round to that dream holiday in the sun. This is why I created Console.Waterworks. I believe we should face frustration when it finds us and only then. Why go looking for it? + +Amongst other things, Console.Waterworks has this one very handy trick. It allows you to write methods which become a run-time command -- which is quite the time saver. When you use Console.Waterworks, you, also, do not need to parse end-user input. The reason why is because Console.Waterworks does it for you. This means, when people run your program, they type the method name in and your code begins excuting. On top of that, Console.Waterworks handles bad input and method parameters, as well. + +Do not panic if none of what I just said made no sense. I threw a lot of information at you in a short span of time. It is okay and not unexpected. Because of this, I have prepared a gif showing Console.Waterworks in action. Please feel free to check it out. It should help explain what Console.Waterwork is and how it works. + +**Insert gif here** + +## Pre-Requisites + +To use Console.Waterworks, I recommend you meet the following pre-requisites: + +- You have experience with C#. +- You can create a .Net console program in Visual Studio 2017 or Visual Studio Code. +- You have experience with Nuget. +- You have Microsoft .Net Framework 4.7 or higher. + +## Quick Start + +Before continuing... I am skipping the part about creating a .Net console program. This is because I assume you know to do that. I am, also, assuming you are using Visual Studio 2017 and C# (and not VB or F#). + +1 Add Console.Waterworks to your project via Nuget. You can do this using Visual Studio's (2017) Package Manager GUI. Or, you can use the Package Manager Console. Here is the command for the PM Console, + +```powershell +Install-Package Console.Waterworks -Version 0.1.0-alpha1 +``` + +2 Create a class called `ConsoleCommands`. It can go anywhere as long as it is in the same project as the console program project. + +3 Make a note of the `ConsoleCommands` namespace. + +4 Head to the `Main` method is `Program.cs` and add the following lines of code, + +```c# +public static void Main(string[] args) +{ + CW_Liaison bob = new CW_Liaison(); + bob.Run("CW_Console", true); +} +``` + +5 Head back to `ConsoleCommands` and make it public. + +6 Stay in `ConsoleCommands` and write the following line, + +```c# +public static string Test() +{ + return "Congratulations! It works."; +} +``` + +7 Run the program and type *"Test"* into the console. + +8 If all has gone well, you should see the *“Congratulations! It works."* message. + +9 Add more methods to you console program. + +## Guides + +There are several guides for Console.Waterworks and in various forms. I am hopeful you will find at least one of them helpful. + +### Offline + +I have created two guides for offline usage: + +- The Quick Guide to Console.Waterworks +- The Complete Guide to Console.Waterworks + +Both of these guides are printer friendly. And, they are best consumed on your favourite reading chair. If you are away from the world-wide web, these guides are, also, for you. + +### Online + +For those sitting at their computer and connected to the world-wide web, there is a Wiki: + +- GitHub Wiki + +This guide aims to be a concise as possible. It assumes you are sitting at your desk and wanting the quick answer -- not the full answer. + +## Code of Conduct + +Please visit the Code of Conduct page for Console.Waterworks at, + +- Code of Conduct page + +## Contributing to the Console.Waterworks Repository + +Please visit the Contributing page for Console.Waterworks at, + +- Contributing page + +## About the Creator + +Hi, my name is Craig and I'm the creator of Console.Waterworks. Thanks for checking it out. + +- Email: craig@craigoates.net +- Web: http://www.craigoates.net \ No newline at end of file