1
0
mirror of synced 2024-06-26 14:27:38 +00:00

#33 Tapeti.Cmd confirm overwrite existing files when exporting

Also fixed issue with input remaining in buffer which can cause accidental confirms
This commit is contained in:
Mark van Renswoude 2021-09-15 19:56:24 +02:00
parent 71df3e3f22
commit 0fb2c48083
4 changed files with 115 additions and 24 deletions

View File

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading; using System.Threading;
using Console = System.Console;
namespace Tapeti.Cmd.ConsoleHelper namespace Tapeti.Cmd.ConsoleHelper
{ {
@ -138,39 +140,79 @@ namespace Tapeti.Cmd.ConsoleHelper
public abstract bool Enabled { get; } public abstract bool Enabled { get; }
public abstract void WriteCaptured(string value, Action processInput);
public abstract void WriteLine(string value); public abstract void WriteLine(string value);
public void Confirm(string message) public void Confirm(string message)
{ {
WriteLine(message); WriteLine(message);
TryReadKey(false, out _);
// Clear any previous key entered before this confirmation
while (!Owner.Cancelled && Console.KeyAvailable)
Console.ReadKey(true);
while (!Owner.Cancelled && !Console.KeyAvailable)
Thread.Sleep(50);
if (Owner.Cancelled)
return;
Console.ReadKey(true);
} }
public bool ConfirmYesNo(string message) public bool ConfirmYesNo(string message)
{ {
WriteLine($"{message} (Y/N) "); var confirmed = false;
if (!TryReadKey(true, out var key))
return false;
return key.KeyChar == 'y' || key.KeyChar == 'Y'; WriteCaptured($"{message} (Y/N) ", () =>
}
private bool TryReadKey(bool showKeyOutput, out ConsoleKeyInfo keyInfo)
{
while (!Owner.Cancelled && !Console.KeyAvailable)
Thread.Sleep(50);
if (Owner.Cancelled)
{ {
keyInfo = default; // Clear any previous key entered before this confirmation
return false; while (!Owner.Cancelled && Console.KeyAvailable)
} Console.ReadKey(true);
keyInfo = Console.ReadKey(!showKeyOutput); var input = new StringBuilder();
return true;
while (!Owner.Cancelled)
{
if (!Console.KeyAvailable)
{
Thread.Sleep(50);
continue;
}
var keyInfo = Console.ReadKey(false);
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault - by design
switch (keyInfo.Key)
{
case ConsoleKey.Enter:
Console.WriteLine();
confirmed = input.ToString().Equals("Y", StringComparison.CurrentCultureIgnoreCase);
return;
case ConsoleKey.Backspace:
if (input.Length > 0)
{
input.Remove(input.Length - 1, 1);
// We need to handle erasing the character ourselves, as we want to use ReadKey so that we can monitor Cancelled
Console.Write(" \b");
}
break;
default:
if (keyInfo.KeyChar != -1)
input.Append(keyInfo.KeyChar);
break;
}
}
});
return confirmed;
} }
} }
@ -185,6 +227,22 @@ namespace Tapeti.Cmd.ConsoleHelper
public override bool Enabled => true; public override bool Enabled => true;
public override void WriteCaptured(string value, Action waitForInput)
{
Owner.AcquirePermanent();
try
{
Console.Write(value);
waitForInput();
}
finally
{
Owner.ReleasePermanent();
}
}
public override void WriteLine(string value) public override void WriteLine(string value)
{ {
Owner.AcquirePermanent(); Owner.AcquirePermanent();
@ -217,6 +275,13 @@ namespace Tapeti.Cmd.ConsoleHelper
public override bool Enabled => !Console.IsOutputRedirected; public override bool Enabled => !Console.IsOutputRedirected;
public override void WriteCaptured(string value, Action waitForInput)
{
WriteLine(value);
waitForInput();
}
public override void WriteLine(string value) public override void WriteLine(string value)
{ {
if (!Enabled) if (!Enabled)

View File

@ -31,6 +31,12 @@ namespace Tapeti.Cmd.Serialization
} }
public static bool OutputExists(string path)
{
return Directory.Exists(path) && Directory.GetFiles(path, "*.message.txt").Length > 0;
}
public void Dispose() public void Dispose()
{ {
} }

View File

@ -2,7 +2,6 @@
using System.IO; using System.IO;
using System.Text; using System.Text;
using CommandLine; using CommandLine;
using RabbitMQ.Client;
using Tapeti.Cmd.ConsoleHelper; using Tapeti.Cmd.ConsoleHelper;
using Tapeti.Cmd.Serialization; using Tapeti.Cmd.Serialization;
@ -17,6 +16,9 @@ namespace Tapeti.Cmd.Verbs
[Option('o', "output", Required = true, HelpText = "Path or filename (depending on the chosen serialization method) where the messages will be output to.")] [Option('o', "output", Required = true, HelpText = "Path or filename (depending on the chosen serialization method) where the messages will be output to.")]
public string OutputPath { get; set; } public string OutputPath { get; set; }
[Option('y', "overwrite", HelpText = "If the output exists, do not ask to overwrite.")]
public bool Overwrite { get; set; }
[Option('r', "remove", HelpText = "If specified messages are acknowledged and removed from the queue. If not messages are kept.")] [Option('r', "remove", HelpText = "If specified messages are acknowledged and removed from the queue. If not messages are kept.")]
public bool RemoveMessages { get; set; } public bool RemoveMessages { get; set; }
@ -40,8 +42,12 @@ namespace Tapeti.Cmd.Verbs
public void Execute(IConsole console) public void Execute(IConsole console)
{ {
var consoleWriter = console.GetPermanentWriter(); var consoleWriter = console.GetPermanentWriter();
using var messageSerializer = GetMessageSerializer(options, consoleWriter);
if (messageSerializer == null)
return;
var factory = options.CreateConnectionFactory(console); var factory = options.CreateConnectionFactory(console);
using var messageSerializer = GetMessageSerializer(options);
using var connection = factory.CreateConnection(); using var connection = factory.CreateConnection();
using var channel = connection.CreateModel(); using var channel = connection.CreateModel();
@ -86,16 +92,27 @@ namespace Tapeti.Cmd.Verbs
} }
private static IMessageSerializer GetMessageSerializer(ExportOptions options) private static IMessageSerializer GetMessageSerializer(ExportOptions options, IConsoleWriter consoleWriter)
{ {
switch (options.SerializationMethod) switch (options.SerializationMethod)
{ {
case SerializationMethod.SingleFileJSON: case SerializationMethod.SingleFileJSON:
// ReSharper disable once InvertIf - causes two lines of "new SingleFileJSONMessageSerializer". DRY ReSharper.
if (!options.Overwrite && File.Exists(options.OutputPath))
{
if (!consoleWriter.ConfirmYesNo($"The output file '{options.OutputPath}' already exists, do you want to overwrite it?"))
return null;
}
return new SingleFileJSONMessageSerializer(new FileStream(options.OutputPath, FileMode.Create, FileAccess.Write, FileShare.Read), true, Encoding.UTF8); return new SingleFileJSONMessageSerializer(new FileStream(options.OutputPath, FileMode.Create, FileAccess.Write, FileShare.Read), true, Encoding.UTF8);
case SerializationMethod.EasyNetQHosepipe: case SerializationMethod.EasyNetQHosepipe:
if (string.IsNullOrEmpty(options.OutputPath)) // ReSharper disable once InvertIf - causes two lines of "new SingleFileJSONMessageSerializer". DRY ReSharper.
throw new ArgumentException("An output path must be provided when using EasyNetQHosepipe serialization"); if (!options.Overwrite && EasyNetQMessageSerializer.OutputExists(options.OutputPath))
{
if (!consoleWriter.ConfirmYesNo($"The output path '{options.OutputPath}' already contains a previous export, do you want to overwrite it?"))
return null;
}
return new EasyNetQMessageSerializer(options.OutputPath); return new EasyNetQMessageSerializer(options.OutputPath);

View File

@ -45,6 +45,9 @@ Fetches messages from a queue and writes them to disk.
-o <target>, --output <target> -o <target>, --output <target>
*Required*. Path or filename (depending on the chosen serialization method) where the messages will be output to. *Required*. Path or filename (depending on the chosen serialization method) where the messages will be output to.
-y, --overwrite
If the output exists, do not ask to overwrite.
-r, --remove -r, --remove
If specified messages are acknowledged and removed from the queue. If not messages are kept. If specified messages are acknowledged and removed from the queue. If not messages are kept.