From 8634d6065af85880f2fac526e02ae4d94fab8f19 Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Sat, 24 Feb 2018 15:46:31 +1000 Subject: [PATCH 1/5] Add support for a subcommand --- src/SeqCli/Cli/CommandAttribute.cs | 6 ++++++ src/SeqCli/Cli/CommandLineHost.cs | 14 ++++++++++++-- src/SeqCli/Cli/CommandMetadata.cs | 1 + src/SeqCli/Cli/ICommandMetadata.cs | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/SeqCli/Cli/CommandAttribute.cs b/src/SeqCli/Cli/CommandAttribute.cs index a2b29b0b..81199286 100644 --- a/src/SeqCli/Cli/CommandAttribute.cs +++ b/src/SeqCli/Cli/CommandAttribute.cs @@ -20,6 +20,7 @@ namespace SeqCli.Cli public class CommandAttribute : Attribute, ICommandMetadata { public string Name { get; } + public string SubCommand { get; } public string HelpText { get; } public string Example { get; set; } @@ -29,5 +30,10 @@ public CommandAttribute(string name, string helpText) Name = name; HelpText = helpText; } + + public CommandAttribute(string name, string subCommand, string helpText) : this(name, helpText) + { + SubCommand = subCommand; + } } } diff --git a/src/SeqCli/Cli/CommandLineHost.cs b/src/SeqCli/Cli/CommandLineHost.cs index 2b635f1c..0a035767 100644 --- a/src/SeqCli/Cli/CommandLineHost.cs +++ b/src/SeqCli/Cli/CommandLineHost.cs @@ -37,11 +37,21 @@ public async Task Run(string[] args) if (args.Length > 0) { + var amountToSkip = 1; var norm = args[0].ToLowerInvariant(); - var cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm); + Meta, CommandMetadata> cmd; + if (!args[1].Contains("-")) + { + amountToSkip = 2; + cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm && c.Metadata.SubCommand == args[1].ToLowerInvariant()); + } + else + { + cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm); + } if (cmd != null) { - return await cmd.Value.Value.Invoke(args.Skip(1).ToArray()); + return await cmd.Value.Value.Invoke(args.Skip(amountToSkip).ToArray()); } } diff --git a/src/SeqCli/Cli/CommandMetadata.cs b/src/SeqCli/Cli/CommandMetadata.cs index acf71400..f1bd8fe7 100644 --- a/src/SeqCli/Cli/CommandMetadata.cs +++ b/src/SeqCli/Cli/CommandMetadata.cs @@ -17,6 +17,7 @@ namespace SeqCli.Cli public class CommandMetadata : ICommandMetadata { public string Name { get; set; } + public string SubCommand { get; set; } public string HelpText { get; set; } public string Example { get; set; } } diff --git a/src/SeqCli/Cli/ICommandMetadata.cs b/src/SeqCli/Cli/ICommandMetadata.cs index b5f653d5..7d4d0c4b 100644 --- a/src/SeqCli/Cli/ICommandMetadata.cs +++ b/src/SeqCli/Cli/ICommandMetadata.cs @@ -17,6 +17,7 @@ namespace SeqCli.Cli interface ICommandMetadata { string Name { get; } + string SubCommand { get; } string HelpText { get; } } } From 46a0617b8ca4ff73c33051879a3b3b1b9d2da9d6 Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Sat, 24 Feb 2018 16:17:13 +1000 Subject: [PATCH 2/5] Only pick up main command if no subcommand specified --- src/SeqCli/Cli/CommandLineHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeqCli/Cli/CommandLineHost.cs b/src/SeqCli/Cli/CommandLineHost.cs index 0a035767..3e93033a 100644 --- a/src/SeqCli/Cli/CommandLineHost.cs +++ b/src/SeqCli/Cli/CommandLineHost.cs @@ -47,7 +47,7 @@ public async Task Run(string[] args) } else { - cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm); + cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm && c.Metadata.SubCommand == default); } if (cmd != null) { From bdae353bcee93335913297a26eb216161dbeb0df Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Sat, 24 Feb 2018 16:40:25 +1000 Subject: [PATCH 3/5] Add test to ensure no commands using the same name and subcommand --- test/SeqCli.Tests/Cli/NoDuplicateCommands.cs | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/SeqCli.Tests/Cli/NoDuplicateCommands.cs diff --git a/test/SeqCli.Tests/Cli/NoDuplicateCommands.cs b/test/SeqCli.Tests/Cli/NoDuplicateCommands.cs new file mode 100644 index 00000000..532ddef5 --- /dev/null +++ b/test/SeqCli.Tests/Cli/NoDuplicateCommands.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using SeqCli.Cli; +using Xunit; + +namespace SeqCli.Tests.Cli +{ + + public class NoDuplicateCommands + { + [Fact] + public void EnsureNoDuplicateCommands() + { + var anyDuplicates = typeof(Command).GetTypeInfo().Assembly.GetExportedTypes() + .Where(t => t.IsAssignableFrom(typeof(Command))) + .Select(t => new {CommandType = t, Attribute = t.GetCustomAttribute()}) + .GroupBy(t => new {t.Attribute.Name, t.Attribute.SubCommand}) + .Any(t => t.Count() > 1); + + Assert.Equal(anyDuplicates, false); + } + } +} From 929ef8a5eb41726fd1ade26cb0babcb6f4ebb6bb Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Sun, 25 Feb 2018 09:59:56 +1000 Subject: [PATCH 4/5] Cleaned up subcommand finding code --- src/SeqCli/Cli/CommandLineHost.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/SeqCli/Cli/CommandLineHost.cs b/src/SeqCli/Cli/CommandLineHost.cs index 3e93033a..bc725e3c 100644 --- a/src/SeqCli/Cli/CommandLineHost.cs +++ b/src/SeqCli/Cli/CommandLineHost.cs @@ -37,20 +37,12 @@ public async Task Run(string[] args) if (args.Length > 0) { - var amountToSkip = 1; var norm = args[0].ToLowerInvariant(); - Meta, CommandMetadata> cmd; - if (!args[1].Contains("-")) - { - amountToSkip = 2; - cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm && c.Metadata.SubCommand == args[1].ToLowerInvariant()); - } - else - { - cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm && c.Metadata.SubCommand == default); - } + var subCommandNorm = args.Length > 1 && !args[1].Contains("-") ? args[1].ToLowerInvariant() : default; + var cmd = _availableCommands.SingleOrDefault(c => c.Metadata.Name == norm && c.Metadata.SubCommand == subCommandNorm); if (cmd != null) { + var amountToSkip = subCommandNorm == default ? 1 : 2; return await cmd.Value.Value.Invoke(args.Skip(amountToSkip).ToArray()); } } From 1b19c0444744f496c779246f59d459e96f8067f0 Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Sun, 25 Feb 2018 10:22:45 +1000 Subject: [PATCH 5/5] Add some tests to ensure correct command and subcommands are picked --- test/SeqCli.Tests/Cli/CommandLineHostTests.cs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 test/SeqCli.Tests/Cli/CommandLineHostTests.cs diff --git a/test/SeqCli.Tests/Cli/CommandLineHostTests.cs b/test/SeqCli.Tests/Cli/CommandLineHostTests.cs new file mode 100644 index 00000000..049c24df --- /dev/null +++ b/test/SeqCli.Tests/Cli/CommandLineHostTests.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Autofac.Features.Metadata; +using SeqCli.Cli; +using SeqCli.Cli.Commands; +using Xunit; + +namespace SeqCli.Tests.Cli +{ + public class CommandLineHostTests + { + [Fact] + public async Task CheckCommandLineHostPicksCorrectCommand() + { + var commandsRan = new List(); + var availableCommands = new List, CommandMetadata>> + { + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test"))), + new CommandMetadata() {Name = "test"}), + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test2"))), + new CommandMetadata() {Name = "test2"}) + }; + var commandLineHost = new CommandLineHost(availableCommands); + await commandLineHost.Run(new []{ "test"}); + + Assert.Equal(commandsRan.First(), "test"); + } + + [Fact] + public async Task WhenCommandAndSubcommandAndTheUserRunsWithoutSubcommandEnsurePickedCorrect() + { + var commandsRan = new List(); + var availableCommands = + new List, CommandMetadata>> + { + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test"))), + new CommandMetadata() {Name = "test"}), + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test-subcommand"))), + new CommandMetadata() {Name = "test", SubCommand = "subcommand"}) + }; + var commandLineHost = new CommandLineHost(availableCommands); + await commandLineHost.Run(new[] { "test" }); + + Assert.Equal(commandsRan.First(), "test"); + } + + [Fact] + public async Task WhenCommandAndSubcommandAndTheUserRunsWithSubcommandEnsurePickedCorrect() + { + var commandsRan = new List(); + var availableCommands = + new List, CommandMetadata>> + { + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test"))), + new CommandMetadata() {Name = "test"}), + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test-subcommand"))), + new CommandMetadata() {Name = "test", SubCommand = "subcommand"}) + }; + var commandLineHost = new CommandLineHost(availableCommands); + await commandLineHost.Run(new[] { "test", "subcommand" }); + + Assert.Equal(commandsRan.First(), "test-subcommand"); + } + + [Fact] + public async Task WhenMoreThanOneSubcommandAndTheUserRunsWithSubcommandEnsurePickedCorrect() + { + var commandsRan = new List(); + var availableCommands = + new List, CommandMetadata>> + { + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test-subcommand1"))), + new CommandMetadata() {Name = "test", SubCommand = "subcommand1"}), + new Meta, CommandMetadata>( + new Lazy(() => new ActionCommand(() => commandsRan.Add("test-subcommand2"))), + new CommandMetadata() {Name = "test", SubCommand = "subcommand2"}) + }; + var commandLineHost = new CommandLineHost(availableCommands); + await commandLineHost.Run(new[] { "test", "subcommand2" }); + + Assert.Equal(commandsRan.First(), "test-subcommand2"); + } + + class ActionCommand : Command + { + public ActionCommand(Action action) + { + action.Invoke(); + } + } + } +}