1 /* 2 * hunt-console eases the creation of beautiful and testable command line interfaces. 3 * 4 * Copyright (C) 2018-2019, HuntLabs 5 * 6 * Website: https://www.huntlabs.net 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.console.descriptor.TextDescriptor; 13 14 import std.string; 15 import std.conv; 16 import hunt.console.Console; 17 import hunt.console.command.Command; 18 import hunt.console.input.InputArgument; 19 import hunt.console.input.InputDefinition; 20 import hunt.console.input.InputOption; 21 import hunt.console.util.StringUtils; 22 23 import hunt.collection.List; 24 import hunt.collection.Map; 25 import hunt.math.Helper; 26 import hunt.Integer; 27 import hunt.Boolean; 28 import hunt.console.descriptor.AbstractDescriptor; 29 import hunt.console.descriptor.DescriptorOptions; 30 import hunt.console.descriptor.ConsoleDescription; 31 32 class TextDescriptor : AbstractDescriptor 33 { 34 override protected void describeInputArgument(InputArgument argument, DescriptorOptions options) 35 { 36 string defaultValue = ""; 37 if (argument.getDefaultValue() !is null) { 38 defaultValue = format("<comment> (default: %s)</comment>", argument.getDefaultValue()); 39 } 40 41 int nameWidth = options.has("name_width") ? Integer.valueOf(options.get("name_width")).intValue() : cast(int)(argument.getName().length); 42 43 writeText(format(" <info>%-" ~ nameWidth.to!string ~ "s</info> %s%s", 44 argument.getName(), 45 argument.getDescription() is null ? "" : argument.getDescription().replace("\\n", "\\n" ~ StringUtils.repeat(" ", nameWidth + 2)), 46 defaultValue is null ? "" : defaultValue 47 ), options); 48 } 49 50 override protected void describeInputOption(InputOption option, DescriptorOptions options) 51 { 52 string defaultValue = ""; 53 if (option.getDefaultValue() !is null) { 54 defaultValue = format("<comment> (default: %s)</comment>", option.getDefaultValue()); 55 } 56 57 int nameWidth = options.has("name_width") ? Integer.valueOf(options.get("name_width")).intValue() : cast(int)(option.getName().length); 58 int nameWithShortcutWidth = nameWidth - cast(int)(option.getName().length) - 2; 59 60 writeText(format(" <info>%s</info> %-" ~ nameWithShortcutWidth.to!string ~ "s%s%s%s", 61 "--" ~ option.getName(), 62 option.getShortcut() is null ? "" : format("(-%s) ", option.getShortcut()), 63 option.getDescription().replace("\\n", "\\n" ~ StringUtils.repeat(" ", nameWidth + 2)), 64 defaultValue, 65 option.isArray() ? "<comment> (multiple values allowed)</comment>": "" 66 ), options); 67 } 68 69 override protected void describeInputDefinition(InputDefinition definition, DescriptorOptions options) 70 { 71 int nameWidth = 0; 72 int nameLength; 73 foreach (InputOption option ; definition.getOptions()) { 74 nameLength = cast(int)(option.getName().length) + 2; 75 if (option.getShortcut() !is null) { 76 nameLength += cast(int)(option.getShortcut().length) + 3; 77 } 78 nameWidth = MathHelper.max(nameWidth, nameLength); 79 } 80 foreach (InputArgument argument ; definition.getArguments()) { 81 nameWidth = MathHelper.max(nameWidth, cast(int)(argument.getName().length)); 82 } 83 ++nameWidth; 84 85 if (definition.getArgumentCount() > 0) { 86 writeText("<comment>Arguments:</comment>", options); 87 writeNewline(); 88 foreach (InputArgument argument ; definition.getArguments()) { 89 describeInputArgument(argument, (new DescriptorOptions()).set("name_width", nameWidth.to!string)); 90 writeNewline(); 91 } 92 if (definition.getOptions().size() > 0) { 93 writeNewline(); 94 } 95 } 96 97 if (definition.getOptions().size() > 0) { 98 writeText("<comment>Options:</comment>", options); 99 writeNewline(); 100 foreach (InputOption option ; definition.getOptions()) { 101 describeInputOption(option, (new DescriptorOptions()).set("name_width", nameWidth.to!string)); 102 writeNewline(); 103 } 104 } 105 } 106 107 override protected void describeCommand(Command command, DescriptorOptions options) 108 { 109 command.getSynopsis(); 110 command.mergeConsoleDefinition(); 111 112 writeText("<comment>Usage:</comment>", options); 113 writeNewline(); 114 writeText(" " ~ command.getSynopsis(), options); 115 writeNewline(); 116 117 if (command.getAliases().length > 0) { 118 writeNewline(); 119 writeText("<comment>Aliases:</comment> <info>" ~ StringUtils.join(command.getAliases(), ", ") ~ "</info>", options); 120 } 121 122 InputDefinition definition = command.getNativeDefinition(); 123 if (definition !is null) { 124 writeNewline(); 125 describeInputDefinition(definition, options); 126 } 127 128 writeNewline(); 129 130 string help = command.getProcessedHelp(); 131 if (help !is null && !(help.length == 0)) { 132 writeText("<comment>Help:</comment>", options); 133 writeNewline(); 134 writeText(" " ~ help.replace("\\n", "\\\n "), options); 135 writeNewline(); 136 } 137 } 138 139 override protected void describeConsole(Console application, DescriptorOptions options) 140 { 141 string describedNamespace = options.has("namespace") ? options.get("namespace") : null; 142 ConsoleDescription description = new ConsoleDescription(application, describedNamespace); 143 144 int width = getColumnWidth(description.getCommands()); 145 146 if (options.has("raw_text") && Boolean.parseBoolean(options.get("raw_text"))) { 147 foreach (Command command ; description.getCommands().values()) { 148 writeText(format("%-" ~ width.to!string ~ "s %s", command.getName(), command.getDescription()), options); 149 writeNewline(); 150 } 151 } else { 152 writeText(application.getHelp(), options); 153 writeNewline(); 154 155 if (describedNamespace !is null) { 156 writeText(format("<comment>Available commands for the '%s' namespace:</comment>", describedNamespace), options); 157 } else { 158 writeText("<comment>Available commands:</comment>", options); 159 } 160 161 // add commands by namespace 162 foreach (string k,List!(string) v ; description.getNamespaces()) { 163 164 if (describedNamespace is null && !(ConsoleDescription.GLOBAL_NAMESPACE == k)) { 165 writeNewline(); 166 writeText("<comment>" ~ k ~ "</comment>", options); 167 } 168 169 foreach (string name ; v) { 170 writeNewline(); 171 writeText(format(" <info>%-" ~ width.to!string ~ "s</info> %s", name, description.getCommand(name).getDescription() is null ? "" : description.getCommand(name).getDescription()), options); 172 } 173 } 174 175 writeNewline(); 176 } 177 } 178 179 private void writeNewline() 180 { 181 writeText("\r\n"/* System.getProperty("line.separator") */, new DescriptorOptions()); 182 } 183 184 private void writeText(string content, DescriptorOptions options) 185 { 186 write( 187 options.has("raw_text") && Boolean.parseBoolean("raw_text") ? StringUtils.stripTags(content) : content, 188 !options.has("raw_output") || !Boolean.parseBoolean(options.get("raw_output")) 189 ); 190 } 191 192 private int getColumnWidth(Map!(string, Command) commands) 193 { 194 int width = 0; 195 foreach (Command command ; commands.values()) { 196 width = MathHelper.max(width, cast(int)(command.getName().length)); 197 } 198 199 return width; 200 } 201 }