-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathCommandRunner.cs
140 lines (122 loc) · 5.07 KB
/
CommandRunner.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Swashbuckle.AspNetCore.Cli
{
public class CommandRunner
{
private readonly Dictionary<string, string> _argumentDescriptors;
private readonly Dictionary<string, string> _optionDescriptors;
private Func<IDictionary<string, string>, int> _runFunc;
private readonly List<CommandRunner> _subRunners;
private readonly TextWriter _output;
public CommandRunner(string commandName, string commandDescription, TextWriter output)
{
CommandName = commandName;
CommandDescription = commandDescription;
_argumentDescriptors = new Dictionary<string, string>();
_optionDescriptors = new Dictionary<string, string>();
_runFunc = (namedArgs) => { return 1; }; // noop
_subRunners = new List<CommandRunner>();
_output = output;
}
public string CommandName { get; private set; }
public string CommandDescription { get; private set; }
public void Argument(string name, string description)
{
_argumentDescriptors.Add(name, description);
}
public void Option(string name, string description)
{
if (!name.StartsWith("--")) throw new ArgumentException("name of option must begin with --");
_optionDescriptors.Add(name, description);
}
public void OnRun(Func<IDictionary<string, string>, int> runFunc)
{
_runFunc = runFunc;
}
public void SubCommand(string name, string description, Action<CommandRunner> configAction)
{
var runner = new CommandRunner($"{CommandName} {name}", description, _output);
configAction(runner);
_subRunners.Add(runner);
}
public int Run(IEnumerable<string> args)
{
if (args.Any())
{
var subRunner = _subRunners.FirstOrDefault(r => r.CommandName.Split(' ').Last() == args.First());
if (subRunner != null) return subRunner.Run(args.Skip(1));
}
if (_subRunners.Any() || !TryParseArgs(args, out IDictionary<string, string> namedArgs))
{
PrintUsage();
return 1;
}
return _runFunc(namedArgs);
}
private bool TryParseArgs(IEnumerable<string> args, out IDictionary<string, string> namedArgs)
{
namedArgs = new Dictionary<string, string>();
var argsQueue = new Queue<string>(args);
// Process options first
while (argsQueue.Any() && argsQueue.Peek().StartsWith("--"))
{
// Ensure it's expected and that the value is also provided
var name = argsQueue.Dequeue();
if (!_optionDescriptors.ContainsKey(name) || !argsQueue.Any() || argsQueue.Peek().StartsWith("--"))
return false;
namedArgs.Add(name, argsQueue.Dequeue());
}
// Process required args - ensure corresponding values are provided
foreach (var name in _argumentDescriptors.Keys)
{
if (!argsQueue.Any() || argsQueue.Peek().StartsWith("--")) return false;
namedArgs.Add(name, argsQueue.Dequeue());
}
return argsQueue.Count() == 0;
}
private void PrintUsage()
{
if (_subRunners.Any())
{
// List sub commands
_output.WriteLine(CommandDescription);
_output.WriteLine("Commands:");
foreach (var runner in _subRunners)
{
var shortName = runner.CommandName.Split(' ').Last();
if (shortName.StartsWith("_")) continue; // convention to hide commands
_output.WriteLine($" {shortName}: {runner.CommandDescription}");
}
_output.WriteLine();
}
else
{
// Usage for this command
var optionsPart = _optionDescriptors.Any() ? "[options] " : "";
var argParts = _argumentDescriptors.Keys.Select(name => $"[{name}]");
_output.WriteLine($"Usage: {CommandName} {optionsPart}{string.Join(" ", argParts)}");
_output.WriteLine();
// Arguments
foreach (var entry in _argumentDescriptors)
{
_output.WriteLine($"{entry.Key}:");
_output.WriteLine($" {entry.Value}");
_output.WriteLine();
}
// Options
if (_optionDescriptors.Any())
{
_output.WriteLine("options:");
foreach (var entry in _optionDescriptors)
{
_output.WriteLine($" {entry.Key}: {entry.Value}");
}
_output.WriteLine();
}
}
}
}
}