diff --git a/FileDeduplicator.Test/MenuTests.cs b/FileDeduplicator.Test/MenuTests.cs
index 0de466c..a650fd3 100644
--- a/FileDeduplicator.Test/MenuTests.cs
+++ b/FileDeduplicator.Test/MenuTests.cs
@@ -61,6 +61,31 @@ public void MenuRunsTheChosenVerbAndOffersTheMenuAgain()
Assert.AreEqual(2, output.Split("0. Exit").Length - 1, "The menu was not offered again after the verb ran.");
}
+ ///
+ /// FileDeduplicator -p dir with no verb lands on the menu, and the verb chosen from it
+ /// works on that directory rather than asking for a path again -- every time it is chosen.
+ ///
+ [TestMethod]
+ public void MenuHandsItsPathToTheChosenVerb()
+ {
+ // Arrange
+ using TempTree tree = new();
+ _ = tree.Write("a.txt", "alpha");
+ _ = tree.Write("aa.txt", "alpha");
+ Type[] menuVerbs = [.. Program.Verbs.Where(t => t != typeof(Menu))];
+ int scan = Array.IndexOf(menuVerbs, typeof(Scan)) + 1;
+ Assert.IsGreaterThan(0, scan, "Scan is not on the menu.");
+
+ // Act -- no path on stdin: the only path is the one the menu was given
+ string output = ConsoleCapture.Run(new Menu { PathString = tree.Root.WeakString }, $"{scan}\n{scan}\n0\n", out int exitCode);
+
+ // Assert
+ Assert.AreEqual(0, exitCode, output);
+ Assert.DoesNotContain("Enter the path", output, $"The menu dropped its -p path and asked again. Output was:\n{output}");
+ Assert.AreEqual(2, output.Split($"Scanning: {tree.Root}").Length - 1, $"Scan did not work on the menu's path both times. Output was:\n{output}");
+ Assert.AreEqual(2, output.Split("Found 1 group(s) of duplicate files").Length - 1, output);
+ }
+
[TestMethod]
public void MenuRejectsAChoiceThatIsNotOnIt()
{
diff --git a/FileDeduplicator/Verbs/Menu.cs b/FileDeduplicator/Verbs/Menu.cs
index 246af7d..39143c5 100644
--- a/FileDeduplicator/Verbs/Menu.cs
+++ b/FileDeduplicator/Verbs/Menu.cs
@@ -73,7 +73,7 @@ internal ControlRepeater BuildScrollMenu(out ExitCommand exit)
private void RunNumberedMenu()
{
- BaseVerb[] verbs = [.. MenuVerbs.Select(CreateVerb)];
+ ICommand[] verbs = [.. MenuVerbs.Select(CreateCommand)];
string[] texts = [.. MenuVerbs.Select(DescribeVerb)];
while (true)
@@ -105,11 +105,23 @@ private void RunNumberedMenu()
}
}
- private static BaseVerb CreateVerb(Type verbType)
+ ///
+ /// Creates the command a menu entry runs: the verb, handed the menu's own -p path each
+ /// time it is chosen.
+ ///
+ ///
+ /// The menu is the default verb, so FileDeduplicator -p dir lands here with the path set.
+ /// Dropping it would ask for the path again and invite a different one into a flow that can end
+ /// in deletion. It is re-applied on every run because clears the
+ /// verb's path when it finishes.
+ ///
+ /// The verb to run.
+ /// The command that runs it.
+ private PresetPathCommand CreateCommand(Type verbType)
{
BaseVerb? verb = Activator.CreateInstance(verbType) as BaseVerb;
Debug.Assert(verb != null);
- return verb;
+ return new(verb, () => PathString);
}
private static string DescribeVerb(Type verbType)
@@ -119,13 +131,29 @@ private static string DescribeVerb(Type verbType)
return string.IsNullOrEmpty(helpText) ? name : $"{name} - {helpText}";
}
- private static LabelMenuItem CreateMenuItem(Type verbType) => new()
+ private LabelMenuItem CreateMenuItem(Type verbType) => new()
{
Text = DescribeVerb(verbType),
- Command = CreateVerb(verbType),
+ Command = CreateCommand(verbType),
IsEnabled = true,
};
+ ///
+ /// Runs a verb with the path the menu was given, or with none so that the verb asks for one.
+ ///
+ /// The verb to run.
+ /// Reads the menu's path at the moment the verb runs.
+ private sealed class PresetPathCommand(BaseVerb verb, Func path) : ICommand
+ {
+ public bool IsActive => verb.IsActive;
+
+ public void Execute()
+ {
+ verb.PathString = path();
+ verb.Execute();
+ }
+ }
+
///
/// The menu item that leaves the menu, which otherwise could only be left with Ctrl+C.
///