Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 96 additions & 36 deletions src/Neo.ConsoleService/ConsoleServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ public abstract class ConsoleServiceBase
/// For example, if a method defined as `void Method(string arg1, int arg2, bool arg3)`,
/// the arguments will be parsed as `"arg1" 2 true`.
/// </summary>
/// <param name="method">Method</param>
/// <param name="args">Arguments</param>
/// <returns>Arguments</returns>
/// <param name="method">the MethodInfo of the called method</param>
/// <param name="args">the raw arguments</param>
/// <returns>the parsed arguments</returns>
/// <exception cref="ArgumentException">Missing argument</exception>
internal object?[] ParseSequentialArguments(MethodInfo method, IList<CommandToken> args)
{
Expand All @@ -78,8 +78,9 @@ public abstract class ConsoleServiceBase
/// For example, if a method defined as `void Method(string arg1, int arg2, bool arg3)`,
/// the arguments will be parsed as `Method --arg1 "arg1" --arg2 2 --arg3`.
/// </summary>
/// <param name="method">Method</param>
/// <param name="args">Arguments</param>
/// <param name="method">the MethodInfo of the called method</param>
/// <param name="args">the raw arguments</param>
/// <returns>the parsed arguments</returns>
internal object?[] ParseIndicatorArguments(MethodInfo method, IList<CommandToken> args)
{
var parameters = method.GetParameters();
Expand Down Expand Up @@ -199,6 +200,18 @@ private bool TryProcessValue(Type parameterType, IList<CommandToken> args, bool

#region Commands

private static string ParameterGuide(ParameterInfo info)
{
if (info.HasDefaultValue)
{
var defaultValue = info.DefaultValue?.ToString();
return string.IsNullOrEmpty(defaultValue) ?
$"[ --{info.Name} {info.ParameterType.Name} ]" :
$"[ --{info.Name} {info.ParameterType.Name}({defaultValue}) ]";
}
return $"--{info.Name} {info.ParameterType.Name}";
}

/// <summary>
/// Process "help" command
/// </summary>
Expand Down Expand Up @@ -229,17 +242,10 @@ protected void OnHelpCommand(string key = "")
// Sort and show
withHelp.Sort((a, b) =>
{
var cate = string.Compare(a.HelpCategory, b.HelpCategory, StringComparison.Ordinal);
if (cate == 0)
{
cate = string.Compare(a.Key, b.Key, StringComparison.Ordinal);
}
return cate;
var category = string.Compare(a.HelpCategory, b.HelpCategory, StringComparison.Ordinal);
return category == 0 ? string.Compare(a.Key, b.Key, StringComparison.Ordinal) : category;
});

var guide = (ParameterInfo parameterInfo) => parameterInfo.HasDefaultValue
? $"[ --{parameterInfo.Name} {parameterInfo.DefaultValue?.ToString() ?? ""}]"
: $"--{parameterInfo.Name}";
if (string.IsNullOrEmpty(key) || key.Equals("help", StringComparison.InvariantCultureIgnoreCase))
{
string? last = null;
Expand All @@ -252,40 +258,94 @@ protected void OnHelpCommand(string key = "")
}

Console.Write($"\t{command.Key}");
Console.WriteLine(" " + string.Join(' ', command.Method.GetParameters().Select(guide)));
Console.WriteLine(" " + string.Join(' ', command.Method.GetParameters().Select(ParameterGuide)));
}
}
else
{
// Show help for this specific command
string? last = null;
string? lastKey = null;
bool found = false;
ShowHelpForCommand(key, withHelp);
}
}

foreach (var command in withHelp.Where(u => u.Key == key))
/// <summary>
/// Show help for a specific command
/// </summary>
/// <param name="key">Command key</param>
/// <param name="withHelp">List of commands</param>
private void ShowHelpForCommand(string key, List<ConsoleCommandMethod> withHelp)
{
bool found = false;
string helpMessage = string.Empty;
string lastKey = string.Empty;
foreach (var command in withHelp.Where(u => u.Key == key))
{
found = true;
if (helpMessage != command.HelpMessage)
{
found = true;
if (last != command.HelpMessage)
{
Console.WriteLine($"{command.HelpMessage}");
last = command.HelpMessage;
}

if (lastKey != command.Key)
{
Console.WriteLine("You can call this command like this:");
lastKey = command.Key;
}
Console.WriteLine($"{command.HelpMessage}");
helpMessage = command.HelpMessage;
}

Console.Write($"\t{command.Key}");
Console.WriteLine(" " + string.Join(' ', command.Method.GetParameters().Select(guide)));
if (lastKey != command.Key)
{
Console.WriteLine("You can call this command like this:");
lastKey = command.Key;
}

if (!found)
Console.Write($"\t{command.Key}");
Console.WriteLine(" " + string.Join(' ', command.Method.GetParameters().Select(ParameterGuide)));

var parameters = command.Method.GetParameters();
if (parameters.Length > 0) // Show parameter info for this command
{
throw new ArgumentException($"Command '{key}' not found. Use 'help' to see available commands.");
Console.WriteLine($"Parameters for command `{command.Key}`:");
foreach (var item in parameters)
{
var info = item.HasDefaultValue ? $"(optional, default: {item.DefaultValue?.ToString() ?? "null"})" : "(required)";
Console.WriteLine($"\t{item.Name}: {item.ParameterType.Name} {info}");
}
}
}

if (!found)
throw new ArgumentException($"Command '{key}' not found. Use 'help' to see available commands.");

Console.WriteLine();
Console.WriteLine("You can also use 'how to input' to see how to input arguments.");
}

/// <summary>
/// Show `how to input` guide
/// </summary>
[ConsoleCommand("how to input", Category = "Base Commands")]
internal void OnHowToInput()
{
Console.WriteLine("""
1. Sequential Arguments (Positional)
Arguments are provided in the order they appear in the method signature.
Usage:
> create wallet "path/to/wallet"
> create wallet "path/to/wallet" "wif-or-file" "wallet-name"

Note: String values can be quoted or unquoted. Use quotes for values with spaces.

2. Indicator Arguments (Named Parameters)
Arguments are provided with parameter names prefixed with "--", and parameter order doesn't matter.
Usage:
> create wallet --path "path/to/wallet"
> create wallet --path "path/to/wallet" --wifOrFile "wif-or-file" --walletName "wallet-name"

3. Tips:
- String: Can be quoted or unquoted, use quotes for spaces. It's recommended to use quotes for complex values.
- String[]: Use comma-separated or space-separated values, If space separated, it must be the last argument.
- UInt160, UInt256: Specified in hex format, for example: 0x1234567890abcdef1234567890abcdef12345678
- Numeric: Standard number parsing
- Boolean: Can be specified without a value (defaults to true), true/false, 1/0, yes/no, y/n
- Enum: Case-insensitive enum value names
- JSON: Input as JSON string
- Escape characters: \\, \", \', \n, \r, \t, \v, \b, \f, \a, \e, \0, \ (whitespace).
If want to input without escape, quote the value with backtick(`).
""");
}

/// <summary>
Expand Down
Loading