96 lines
3.1 KiB
C#
96 lines
3.1 KiB
C#
namespace FinalFactory.Rendezvous.TestClient;
|
|
|
|
internal enum TestClientExitCode
|
|
{
|
|
Success = 0,
|
|
Usage = 2,
|
|
Configuration = 3,
|
|
ServiceFailure = 10,
|
|
NoCompatibleSession = 11,
|
|
TraversalFailed = 12,
|
|
DirectTrafficFailed = 13,
|
|
Cancelled = 130,
|
|
}
|
|
|
|
internal interface ITestClientCommandRunner
|
|
{
|
|
Task<TestClientExitCode> RunAsync(
|
|
TestClientOptions options,
|
|
TestClientOutput output,
|
|
TextReader input,
|
|
CancellationToken cancellationToken);
|
|
}
|
|
|
|
internal sealed class TestClientApplication(ITestClientCommandRunner runner)
|
|
{
|
|
private readonly ITestClientCommandRunner _runner = runner ?? throw new ArgumentNullException(nameof(runner));
|
|
|
|
internal async Task<int> RunAsync(
|
|
string[] args,
|
|
TextReader input,
|
|
TextWriter standardOutput,
|
|
TextWriter standardError,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
bool jsonRequested = args.Contains("--json", StringComparer.Ordinal);
|
|
TestClientParseResult parsed = TestClientOptionParser.Parse(args);
|
|
TestClientOutput output = new(standardOutput, standardError, jsonRequested);
|
|
if (parsed.ShowHelp)
|
|
{
|
|
if (jsonRequested)
|
|
{
|
|
output.Write(
|
|
"cli.help",
|
|
"complete",
|
|
phase: "configuration",
|
|
message: "Run without --json to read the full command reference.");
|
|
}
|
|
else
|
|
{
|
|
await standardOutput.WriteLineAsync(TestClientOptionParser.Usage).ConfigureAwait(false);
|
|
}
|
|
return (int)TestClientExitCode.Success;
|
|
}
|
|
if (!parsed.Succeeded || parsed.Options is null)
|
|
{
|
|
if (jsonRequested)
|
|
{
|
|
output.WriteError(
|
|
"cli.usage",
|
|
"failed",
|
|
parsed.Error ?? "Invalid command line.",
|
|
phase: "configuration");
|
|
}
|
|
else
|
|
{
|
|
await standardError.WriteLineAsync(parsed.Error ?? "Invalid command line.").ConfigureAwait(false);
|
|
await standardError.WriteLineAsync("Use --help for documented options.").ConfigureAwait(false);
|
|
}
|
|
return (int)TestClientExitCode.Usage;
|
|
}
|
|
|
|
output = new TestClientOutput(standardOutput, standardError, parsed.Options.Json);
|
|
try
|
|
{
|
|
return (int)await _runner.RunAsync(
|
|
parsed.Options,
|
|
output,
|
|
input,
|
|
cancellationToken).ConfigureAwait(false);
|
|
}
|
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
|
{
|
|
output.Write("lifecycle.cancelled", "cancelled", phase: "lifecycle");
|
|
return (int)TestClientExitCode.Cancelled;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
output.WriteError(
|
|
"lifecycle.failed",
|
|
"failed",
|
|
$"Unexpected {exception.GetType().Name}; credentials remain redacted.");
|
|
return (int)TestClientExitCode.ServiceFailure;
|
|
}
|
|
}
|
|
}
|