From f45b8407055b0a590d38c7f40fee0373770d3162 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Sat, 25 Sep 2021 23:04:18 +1000 Subject: [PATCH] line endings --- .gitattributes | 1 + shamir.sln | 112 +++--- src/console/Program.cs | 310 ++++++++-------- src/console/shamir.csproj | 30 +- tests/console.tests/CommandTreeTests.cs | 454 +++++++++++------------ tests/console.tests/console.tests.csproj | 38 +- 6 files changed, 473 insertions(+), 472 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/shamir.sln b/shamir.sln index 9dda248..6dea34d 100644 --- a/shamir.sln +++ b/shamir.sln @@ -1,56 +1,56 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5384980-CE26-4FC0-9894-0A7AE7915FA0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shamir", "src\console\shamir.csproj", "{5264C707-99B4-43B7-AD5C-5EDD8609E697}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1008129-BAE8-49BD-8807-D0A2E0082307}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "console.tests", "tests\console.tests\console.tests.csproj", "{9133E2DC-9E86-4DF9-A080-4122B0468FDE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x64.ActiveCfg = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x64.Build.0 = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x86.ActiveCfg = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x86.Build.0 = Debug|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|Any CPU.Build.0 = Release|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x64.ActiveCfg = Release|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x64.Build.0 = Release|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x86.ActiveCfg = Release|Any CPU - {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x86.Build.0 = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x64.ActiveCfg = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x64.Build.0 = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x86.ActiveCfg = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x86.Build.0 = Debug|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|Any CPU.Build.0 = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x64.ActiveCfg = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x64.Build.0 = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.ActiveCfg = Release|Any CPU - {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {5264C707-99B4-43B7-AD5C-5EDD8609E697} = {A5384980-CE26-4FC0-9894-0A7AE7915FA0} - {9133E2DC-9E86-4DF9-A080-4122B0468FDE} = {A1008129-BAE8-49BD-8807-D0A2E0082307} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5384980-CE26-4FC0-9894-0A7AE7915FA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shamir", "src\console\shamir.csproj", "{5264C707-99B4-43B7-AD5C-5EDD8609E697}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1008129-BAE8-49BD-8807-D0A2E0082307}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "console.tests", "tests\console.tests\console.tests.csproj", "{9133E2DC-9E86-4DF9-A080-4122B0468FDE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x64.ActiveCfg = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x64.Build.0 = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x86.ActiveCfg = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x86.Build.0 = Debug|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|Any CPU.Build.0 = Release|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x64.ActiveCfg = Release|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x64.Build.0 = Release|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x86.ActiveCfg = Release|Any CPU + {5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x86.Build.0 = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x64.ActiveCfg = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x64.Build.0 = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x86.ActiveCfg = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x86.Build.0 = Debug|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|Any CPU.Build.0 = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x64.ActiveCfg = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x64.Build.0 = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.ActiveCfg = Release|Any CPU + {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5264C707-99B4-43B7-AD5C-5EDD8609E697} = {A5384980-CE26-4FC0-9894-0A7AE7915FA0} + {9133E2DC-9E86-4DF9-A080-4122B0468FDE} = {A1008129-BAE8-49BD-8807-D0A2E0082307} + EndGlobalSection +EndGlobal diff --git a/src/console/Program.cs b/src/console/Program.cs index a9d311d..aa11f10 100644 --- a/src/console/Program.cs +++ b/src/console/Program.cs @@ -1,155 +1,155 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading.Tasks; -using CommandLine; -using CommandLine.Text; -using SysConsole = System.Console; - -namespace Shamir.Console -{ - public class CdnOperations : ICommandSet - { - [Verb("ls", HelpText = "List files on the CDN")] - public class CdnLsOptions - { - } - - [Verb("add", HelpText = "Upload a file to the CDN")] - public class CdnAddOptions - { - } - - [Verb("mv", HelpText = "Move a file to the CDN")] - public class CdnMvOptions - { - } - - public static IEnumerable VerbOptionTypes - { - get - { - yield return typeof(CdnLsOptions); - yield return typeof(CdnAddOptions); - yield return typeof(CdnMvOptions); - } - } - - public ICommand? FindCommand(ReadOnlySpan args) - { - if (args.Length == 0) return null; - - var parserResult = Parser.Default.ParseArguments(args.ToArray()); - return parserResult.MapResult( - options => new CdnLsCommand(options), - options => new CdnAddCommand(options), - options => new CdnMvCommand(options), - errors => new HelpTextCommand(parserResult) - ); - } - - public class CdnLsCommand : ICommand - { - public CdnLsCommand(CdnLsOptions options) - { - } - - public ValueTask ExecuteAsync() - { - SysConsole.WriteLine($"Executing: cdn-ls"); - return ValueTask.FromResult(0); - } - } - - public class CdnAddCommand : ICommand - { - public CdnAddCommand(CdnAddOptions options) - { - } - - public ValueTask ExecuteAsync() - { - SysConsole.WriteLine($"Executing: cdn-add"); - return ValueTask.FromResult(0); - } - } - - public class CdnMvCommand : ICommand - { - public CdnMvCommand(CdnMvOptions options) - { - } - - public ValueTask ExecuteAsync() - { - SysConsole.WriteLine($"Executing: cdn-mv"); - return ValueTask.FromResult(0); - } - } - - public class HelpTextCommand : ICommand - { - public HelpTextCommand(ParserResult result) - { - this.result = result ?? throw new ArgumentNullException(nameof(result)); - Debug.Assert(result.Tag == ParserResultType.NotParsed); - } - - readonly ParserResult result; - - public ValueTask ExecuteAsync() - { - var helpText = HelpText.AutoBuild(result); - SysConsole.Error.WriteLine(helpText); - return ValueTask.FromResult(1); - } - } - } - - public interface ICommand - { - ValueTask ExecuteAsync(); - } - - public interface ICommandSet - { - ICommand? FindCommand(ReadOnlySpan args); - static IEnumerable VerbOptionTypes { get; } - } - - public class CommandTree : ICommandSet - { - IEnumerable VerbOptionTypes { get; } - public ICommand? FindCommand(ReadOnlySpan args) - { - if (args.Length == 0) return null; - - return args[0] switch { - "cdn" => new CdnOperations().FindCommand(args[1..]), - _ => null, - }; - } - } - - class Program - { - public static async Task Main(string[] args) - { - var tree = new CommandTree(); - var command = tree.FindCommand(args); - if (command is null) - { - PrintHelpText(tree); - return 1; - } - - return await command.ExecuteAsync(); - } - - static void PrintHelpText(ICommandSet tree) - { - - } - } -} +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading.Tasks; +using CommandLine; +using CommandLine.Text; +using SysConsole = System.Console; + +namespace Shamir.Console +{ + public class CdnOperations : ICommandSet + { + [Verb("ls", HelpText = "List files on the CDN")] + public class CdnLsOptions + { + } + + [Verb("add", HelpText = "Upload a file to the CDN")] + public class CdnAddOptions + { + } + + [Verb("mv", HelpText = "Move a file to the CDN")] + public class CdnMvOptions + { + } + + public static IEnumerable VerbOptionTypes + { + get + { + yield return typeof(CdnLsOptions); + yield return typeof(CdnAddOptions); + yield return typeof(CdnMvOptions); + } + } + + public ICommand? FindCommand(ReadOnlySpan args) + { + if (args.Length == 0) return null; + + var parserResult = Parser.Default.ParseArguments(args.ToArray()); + return parserResult.MapResult( + options => new CdnLsCommand(options), + options => new CdnAddCommand(options), + options => new CdnMvCommand(options), + errors => new HelpTextCommand(parserResult) + ); + } + + public class CdnLsCommand : ICommand + { + public CdnLsCommand(CdnLsOptions options) + { + } + + public ValueTask ExecuteAsync() + { + SysConsole.WriteLine($"Executing: cdn-ls"); + return ValueTask.FromResult(0); + } + } + + public class CdnAddCommand : ICommand + { + public CdnAddCommand(CdnAddOptions options) + { + } + + public ValueTask ExecuteAsync() + { + SysConsole.WriteLine($"Executing: cdn-add"); + return ValueTask.FromResult(0); + } + } + + public class CdnMvCommand : ICommand + { + public CdnMvCommand(CdnMvOptions options) + { + } + + public ValueTask ExecuteAsync() + { + SysConsole.WriteLine($"Executing: cdn-mv"); + return ValueTask.FromResult(0); + } + } + + public class HelpTextCommand : ICommand + { + public HelpTextCommand(ParserResult result) + { + this.result = result ?? throw new ArgumentNullException(nameof(result)); + Debug.Assert(result.Tag == ParserResultType.NotParsed); + } + + readonly ParserResult result; + + public ValueTask ExecuteAsync() + { + var helpText = HelpText.AutoBuild(result); + SysConsole.Error.WriteLine(helpText); + return ValueTask.FromResult(1); + } + } + } + + public interface ICommand + { + ValueTask ExecuteAsync(); + } + + public interface ICommandSet + { + ICommand? FindCommand(ReadOnlySpan args); + static IEnumerable VerbOptionTypes { get; } + } + + public class CommandTree : ICommandSet + { + IEnumerable VerbOptionTypes { get; } + public ICommand? FindCommand(ReadOnlySpan args) + { + if (args.Length == 0) return null; + + return args[0] switch { + "cdn" => new CdnOperations().FindCommand(args[1..]), + _ => null, + }; + } + } + + class Program + { + public static async Task Main(string[] args) + { + var tree = new CommandTree(); + var command = tree.FindCommand(args); + if (command is null) + { + PrintHelpText(tree); + return 1; + } + + return await command.ExecuteAsync(); + } + + static void PrintHelpText(ICommandSet tree) + { + + } + } +} diff --git a/src/console/shamir.csproj b/src/console/shamir.csproj index ab3aba7..1d465ec 100644 --- a/src/console/shamir.csproj +++ b/src/console/shamir.csproj @@ -1,15 +1,15 @@ - - - - Exe - net5.0 - Shamir.Console - ..\..\bin - enable - - - - - - - + + + + Exe + net5.0 + Shamir.Console + ..\..\bin + enable + + + + + + + diff --git a/tests/console.tests/CommandTreeTests.cs b/tests/console.tests/CommandTreeTests.cs index a781ea8..5454061 100644 --- a/tests/console.tests/CommandTreeTests.cs +++ b/tests/console.tests/CommandTreeTests.cs @@ -1,228 +1,228 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; - -namespace Shamir.Console.Tests -{ - public class CommandTreeTests - { - [SetUp] - public void Setup() - { - tree = new DefaultCommandTree( - "root", - "top level", - ImmutableArray.Create( - new DefaultCommandTree( - "bar", - "child command tree", - ImmutableArray.Empty, - ImmutableArray.Create( - new SimpleTextCommand("one", "bar/1 command"), - new SimpleTextCommand("two", "bar/2 command") - ))), - ImmutableArray.Create( - new SimpleTextCommand("foo", "foo command") - )); - } - - ICommandTree tree; - - [Test] - public void ConstructsHelpTextWithNoArgs() - { - var command = tree.FindCommand(Array.Empty()); - Assert.That(command, Is.InstanceOf()); - var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); - Assert.That(helpText, Is.EqualTo(@" -Group - root : top level - -Subgroups: - bar : child command tree - -Commands: - foo : foo command -".TrimStart())); - } - - [Test] - public void ConstructsHelpTextForSubTree() - { - var command = tree.FindCommand(new[] { "bar" }); - Assert.That(command, Is.InstanceOf()); - var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); - Assert.That(helpText, Is.EqualTo(@" -Group - root bar : child command tree - -Commands: - one : bar/1 command - two : bar/1 command -")); - } - - [TestCase((object)new string[] { "foo" }, ExpectedResult = "foo command")] - [TestCase((object)new string[] { "bar", "one" }, ExpectedResult = "bar/1 command")] - [TestCase((object)new string[] { "bar", "two" }, ExpectedResult = "bar/2 command")] - public string FindsCommand(string[] args) - { - var command = tree.FindCommand(args); - return command?.Description; - } - - - class SimpleTextCommand : ICommand - { - public SimpleTextCommand(string name, string description) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Description = description ?? throw new ArgumentNullException(nameof(description)); - } - - public string Name { get; } - - public string Description { get; } - - public ValueTask ExecuteAsync() - { - return ValueTask.FromResult(0); - } - } - } - - interface IOperationsNode - { - string Name { get; } - string Description { get; } - } - - interface ICommand : IOperationsNode - { - ValueTask ExecuteAsync(); - } - - interface ICommandTree : IOperationsNode - { - ImmutableArray SubTrees { get; } - ImmutableArray Commands { get; } - ICommand FindCommand(ReadOnlySpan args); - } - - class DefaultCommandTree : ICommandTree - { - public DefaultCommandTree(string name, string description, ImmutableArray subtrees, ImmutableArray commands) - { - this.Name = name ?? throw new ArgumentNullException(nameof(name)); - this.Description = description ?? throw new ArgumentNullException(nameof(description)); - this.SubTrees = subtrees; - this.Commands = commands; - } - - public string Name { get; } - public string Description { get; } - public ImmutableArray SubTrees { get; } - public ImmutableArray Commands { get; } - - public ICommand FindCommand(ReadOnlySpan args) - { - if (args.Length > 0) - { - foreach (var command in Commands) - { - if (command.Name == args[0]) - { - return command; - } - } - - foreach (var tree in SubTrees) - { - if (tree.Name == args[0]) - { - return tree.FindCommand(args[1..]); - } - } - } - - return new DefaultHelpTextCommand(this); - } - } - - class DefaultHelpTextCommand : ICommand - { - public DefaultHelpTextCommand(ICommandTree tree) - { - this.CommandTree = tree ?? throw new ArgumentNullException(nameof(tree)); - } - - public string Name => "help"; - public string Description => "Print help text"; - - public ICommandTree CommandTree { get; } - - public ValueTask ExecuteAsync() - { - System.Console.Error.WriteLine(GetHelpText()); - return ValueTask.FromResult(1); // TODO: const - } - - public string GetHelpText() => CommandTree.BuildHelpText(); - } - - static class ICommandTreeExtensions - { - public static string BuildHelpText(this ICommandTree tree) - { - var sb = new StringBuilder(); - sb.AppendLine("Group"); - sb.Append(" "); - sb.AppendLine(tree.Name); - sb.AppendLine(); - - if (!tree.SubTrees.IsEmpty) - { - sb.AppendLine("Subgroups:"); - - var maxSpacing = tree.SubTrees.Max(c => c.Name.Length) + 1; - - foreach (var child in tree.SubTrees) - { - sb.Append(" "); - sb.Append(child.Name); - for (var i = 0; i < maxSpacing; i++) - { - sb.Append(' '); - } - sb.Append(": "); - sb.AppendLine(child.Description); - } - } - - if (!tree.Commands.IsEmpty) - { - sb.AppendLine("Commands:"); - - var maxSpacing = tree.Commands.Max(c => c.Name.Length) + 1; - - foreach (var command in tree.Commands) - { - sb.Append(" "); - sb.Append(command.Name); - for (var i = 0; i < maxSpacing; i++) - { - sb.Append(' '); - } - sb.Append(": "); - sb.AppendLine(command.Description); - } - } - - return sb.ToString(); - } - } +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Shamir.Console.Tests +{ + public class CommandTreeTests + { + [SetUp] + public void Setup() + { + tree = new DefaultCommandTree( + "root", + "top level", + ImmutableArray.Create( + new DefaultCommandTree( + "bar", + "child command tree", + ImmutableArray.Empty, + ImmutableArray.Create( + new SimpleTextCommand("one", "bar/1 command"), + new SimpleTextCommand("two", "bar/2 command") + ))), + ImmutableArray.Create( + new SimpleTextCommand("foo", "foo command") + )); + } + + ICommandTree tree; + + [Test] + public void ConstructsHelpTextWithNoArgs() + { + var command = tree.FindCommand(Array.Empty()); + Assert.That(command, Is.InstanceOf()); + var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); + Assert.That(helpText, Is.EqualTo(@" +Group + root : top level + +Subgroups: + bar : child command tree + +Commands: + foo : foo command +".TrimStart())); + } + + [Test] + public void ConstructsHelpTextForSubTree() + { + var command = tree.FindCommand(new[] { "bar" }); + Assert.That(command, Is.InstanceOf()); + var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); + Assert.That(helpText, Is.EqualTo(@" +Group + root bar : child command tree + +Commands: + one : bar/1 command + two : bar/1 command +")); + } + + [TestCase((object)new string[] { "foo" }, ExpectedResult = "foo command")] + [TestCase((object)new string[] { "bar", "one" }, ExpectedResult = "bar/1 command")] + [TestCase((object)new string[] { "bar", "two" }, ExpectedResult = "bar/2 command")] + public string FindsCommand(string[] args) + { + var command = tree.FindCommand(args); + return command?.Description; + } + + + class SimpleTextCommand : ICommand + { + public SimpleTextCommand(string name, string description) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + Description = description ?? throw new ArgumentNullException(nameof(description)); + } + + public string Name { get; } + + public string Description { get; } + + public ValueTask ExecuteAsync() + { + return ValueTask.FromResult(0); + } + } + } + + interface IOperationsNode + { + string Name { get; } + string Description { get; } + } + + interface ICommand : IOperationsNode + { + ValueTask ExecuteAsync(); + } + + interface ICommandTree : IOperationsNode + { + ImmutableArray SubTrees { get; } + ImmutableArray Commands { get; } + ICommand FindCommand(ReadOnlySpan args); + } + + class DefaultCommandTree : ICommandTree + { + public DefaultCommandTree(string name, string description, ImmutableArray subtrees, ImmutableArray commands) + { + this.Name = name ?? throw new ArgumentNullException(nameof(name)); + this.Description = description ?? throw new ArgumentNullException(nameof(description)); + this.SubTrees = subtrees; + this.Commands = commands; + } + + public string Name { get; } + public string Description { get; } + public ImmutableArray SubTrees { get; } + public ImmutableArray Commands { get; } + + public ICommand FindCommand(ReadOnlySpan args) + { + if (args.Length > 0) + { + foreach (var command in Commands) + { + if (command.Name == args[0]) + { + return command; + } + } + + foreach (var tree in SubTrees) + { + if (tree.Name == args[0]) + { + return tree.FindCommand(args[1..]); + } + } + } + + return new DefaultHelpTextCommand(this); + } + } + + class DefaultHelpTextCommand : ICommand + { + public DefaultHelpTextCommand(ICommandTree tree) + { + this.CommandTree = tree ?? throw new ArgumentNullException(nameof(tree)); + } + + public string Name => "help"; + public string Description => "Print help text"; + + public ICommandTree CommandTree { get; } + + public ValueTask ExecuteAsync() + { + System.Console.Error.WriteLine(GetHelpText()); + return ValueTask.FromResult(1); // TODO: const + } + + public string GetHelpText() => CommandTree.BuildHelpText(); + } + + static class ICommandTreeExtensions + { + public static string BuildHelpText(this ICommandTree tree) + { + var sb = new StringBuilder(); + sb.AppendLine("Group"); + sb.Append(" "); + sb.AppendLine(tree.Name); + sb.AppendLine(); + + if (!tree.SubTrees.IsEmpty) + { + sb.AppendLine("Subgroups:"); + + var maxSpacing = tree.SubTrees.Max(c => c.Name.Length) + 1; + + foreach (var child in tree.SubTrees) + { + sb.Append(" "); + sb.Append(child.Name); + for (var i = 0; i < maxSpacing; i++) + { + sb.Append(' '); + } + sb.Append(": "); + sb.AppendLine(child.Description); + } + } + + if (!tree.Commands.IsEmpty) + { + sb.AppendLine("Commands:"); + + var maxSpacing = tree.Commands.Max(c => c.Name.Length) + 1; + + foreach (var command in tree.Commands) + { + sb.Append(" "); + sb.Append(command.Name); + for (var i = 0; i < maxSpacing; i++) + { + sb.Append(' '); + } + sb.Append(": "); + sb.AppendLine(command.Description); + } + } + + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/tests/console.tests/console.tests.csproj b/tests/console.tests/console.tests.csproj index 16748f3..434b26f 100644 --- a/tests/console.tests/console.tests.csproj +++ b/tests/console.tests/console.tests.csproj @@ -1,19 +1,19 @@ - - - - net5.0 - false - Shamir.Console.Tests - - - - - - - - - - - - - + + + + net5.0 + false + Shamir.Console.Tests + + + + + + + + + + + + +