using System; using System.Collections.Generic; using System.Linq; using System.Text; using HeuristicLab.CommandLineInterface.Data; using HeuristicLab.Common; namespace HeuristicLab.CommandLineInterface { /// /// Static class with output methods. /// public static class CLIConsole { #region Constants private const int Padding = 2; private const int ShortcutWidth = 4; private const int LRowWidth = 20; private const int MRowWidth = 20; private const int RRowWidth = 60; #endregion /// /// Prints the help to the console based on the data saved in CommandData. /// /// /// internal static void PrintHelp(CommandData cmdData, Exception exception = null) { WriteHeader(cmdData); if (exception != null) WriteErrors(exception); WriteSyntax(cmdData); if (cmdData.Options.Count > 0) { WriteOptionBox(cmdData); Console.WriteLine(); } if (cmdData.Commands.Count > 0) WriteCommandBox(cmdData); //Exit the application after every help call. Environment.Exit((exception != null) ? 1 : 0); } #region PrintHelp steps /// /// Writes the header of the help. /// /// private static void WriteHeader(CommandData cmdData) { Console.WriteLine($"{CLIApplication.AppName} {CLIApplication.AppVersion}"); if (cmdData.Description != null) Console.WriteLine($"{cmdData.Description}"); Console.WriteLine(); } /// /// Writes an list of errors. /// /// private static void WriteErrors(Exception exception) { Console.WriteLine($"ERROR(S):"); AggregateException ae = exception as AggregateException; if (ae != null) { foreach (var e in ae.InnerExceptions) { Console.WriteLine($" -> {e} "); } } else { Console.WriteLine($" -> {exception}"); } Console.WriteLine(); } /// /// Writes the syntax of a specific command. /// /// private static void WriteSyntax(CommandData cmdData) { Console.Write($"Syntax: {BuildParentCommandString(cmdData).ToLower()}{cmdData.Identifier.ToLower()}"); if (cmdData.Options.Count > 0) Console.Write(" [OPTION]"); if (cmdData.Commands.Count > 0) Console.Write(" COMMAND"); string valueSyntax = BuildValueSyntaxString(cmdData); Console.WriteLine(" " + valueSyntax); Console.WriteLine(); } /// /// Writes the help box for the options of a specific command. /// /// private static void WriteOptionBox(CommandData cmdData) { WriteBoxSeparatorLine(); WriteBoxLine("Option", "Parameter", "Description"); WriteBoxSeparatorLine(); foreach (OptionData opt in cmdData.Options) if (!opt.Hidden) WriteBoxLine( ((opt.Shortcut != null) ? $"{(OptionData.ShortcutPrefix + opt.Shortcut).ToLower() + ",",-ShortcutWidth}" : $"" ) + $"{(OptionData.LongformPrefix + opt.Identifier).ToLower()}", opt.Property?.PropertyType.GetPrettyName(), opt.Description ); WriteBoxSeparatorLine(); } /// /// Writes the help box for the commands of a specific command. /// /// private static void WriteCommandBox(CommandData cmdData) { WriteBoxSeparatorLine(); WriteBoxLine("Commands", "Parameter", "Description"); WriteBoxSeparatorLine(); foreach (CommandData c in cmdData.Commands) WriteBoxLine(c.Identifier.ToLower(), BuildValueSyntaxString(c), c.Description); WriteBoxSeparatorLine(); Console.WriteLine(); Console.WriteLine($"Enter \"{BuildParentCommandString(cmdData).ToLower()}" + $"{cmdData.Identifier.ToLower()} COMMAND --help\" to show more information for a command."); } /// /// Writes a horizontal seperator line. Used to print a help box. /// /// /// private static void WriteBoxSeparatorLine(char edge = '+', char fill = '-') { Console.Write($"{edge}"); WriteChar(4 * Padding + LRowWidth + MRowWidth + RRowWidth, fill); Console.Write($"{edge}\n"); } /// /// Writes a single line for the help box. /// /// /// /// private static void WriteBoxLine(string lrow, string mrow, string rrow) { IList lf = FitBoxString(lrow ?? "", LRowWidth); IList mf = FitBoxString(mrow ?? "", MRowWidth); IList rf = FitBoxString(rrow ?? "", RRowWidth); // to correctly print lines. for (int i = 0; i < lf.Count || i < mf.Count || i < rf.Count; ++i) { Console.Write($"|{"",Padding}"); if (i < lf.Count) Console.Write($"{lf[i],-LRowWidth}"); else Console.Write($"{"",-LRowWidth}"); Console.Write($"{"",Padding}"); if (i < mf.Count) Console.Write($"{mf[i],-MRowWidth}"); else Console.Write($"{"",-MRowWidth}"); Console.Write($"{"",Padding}"); if (i < rf.Count) Console.Write($"{rf[i],-RRowWidth}"); else Console.Write($"{"",-RRowWidth}"); Console.Write($"{"",-Padding}|\n"); } } #endregion #region Helper /// /// Iterates through command values and creates a string with their type name. /// /// /// A string with the type names of the command's values. private static string BuildValueSyntaxString(CommandData cmdData) { StringBuilder additionalSyntax = new StringBuilder(); int i = 0; foreach (var x in cmdData.Values) { additionalSyntax.Append(x.Property.PropertyType.GetPrettyName()); if ((i + 1) < cmdData.Values.Count) additionalSyntax.Append(" "); } return additionalSyntax.ToString(); } /// /// Iterates through parent commends of specified commends to create a valid parent command string. /// For example: MyRootCommand Command1 ChildOfCommand1 ... /// /// /// A string with parent commends. private static string BuildParentCommandString(CommandData cmdData) { StringBuilder builder = new StringBuilder(); CommandData p = cmdData.Parent; IList cmds = new List(); while (p != null) { cmds.Add(p); p = p.Parent; } cmds.Reverse(); foreach (CommandData parent in cmds) builder.Append($"{parent.Identifier} "); return builder.ToString(); } /// /// Fits a string to specified width. /// /// /// /// Array of string, which stands for lines. private static IList FitBoxString(string str, int width) { string[] splits = str.Split(' '); IList list = new List(); string line = ""; foreach (string split in splits) { string tmp = split; if (tmp.Length > width) // if a word is longer then the specified width, it gets "cutted" into multiple lines. while (tmp.Length > width - line.Length) { list.Add(line + tmp.Substring(0, width - line.Length)); tmp = tmp.Substring(width - line.Length); line = ""; } if (line.Length + tmp.Length <= width - 1) line += tmp + " "; else { list.Add(line); line = tmp; } } if (!string.IsNullOrEmpty(line)) list.Add(line); return list; } /// /// Writes a specific char multiple times. /// /// /// private static void WriteChar(int iterations = 1, char ch = '-') { for (int i = 0; i < iterations; ++i) Console.Write(ch); } #endregion } }