line endings

This commit is contained in:
Yaakov 2021-09-25 23:04:18 +10:00
parent 42fba23dd6
commit f45b840705
6 changed files with 473 additions and 472 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto

View file

@ -1,56 +1,56 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105 VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5384980-CE26-4FC0-9894-0A7AE7915FA0}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5384980-CE26-4FC0-9894-0A7AE7915FA0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shamir", "src\console\shamir.csproj", "{5264C707-99B4-43B7-AD5C-5EDD8609E697}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shamir", "src\console\shamir.csproj", "{5264C707-99B4-43B7-AD5C-5EDD8609E697}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1008129-BAE8-49BD-8807-D0A2E0082307}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1008129-BAE8-49BD-8807-D0A2E0082307}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "console.tests", "tests\console.tests\console.tests.csproj", "{9133E2DC-9E86-4DF9-A080-4122B0468FDE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "console.tests", "tests\console.tests\console.tests.csproj", "{9133E2DC-9E86-4DF9-A080-4122B0468FDE}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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|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.ActiveCfg = Debug|Any CPU
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x64.Build.0 = 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.ActiveCfg = Debug|Any CPU
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Debug|x86.Build.0 = 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.ActiveCfg = Release|Any CPU
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
{5264C707-99B4-43B7-AD5C-5EDD8609E697}.Release|x86.Build.0 = 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.ActiveCfg = Debug|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x64.Build.0 = 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.ActiveCfg = Debug|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Debug|x86.Build.0 = 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.ActiveCfg = Release|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
{9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.Build.0 = Release|Any CPU {9133E2DC-9E86-4DF9-A080-4122B0468FDE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{5264C707-99B4-43B7-AD5C-5EDD8609E697} = {A5384980-CE26-4FC0-9894-0A7AE7915FA0} {5264C707-99B4-43B7-AD5C-5EDD8609E697} = {A5384980-CE26-4FC0-9894-0A7AE7915FA0}
{9133E2DC-9E86-4DF9-A080-4122B0468FDE} = {A1008129-BAE8-49BD-8807-D0A2E0082307} {9133E2DC-9E86-4DF9-A080-4122B0468FDE} = {A1008129-BAE8-49BD-8807-D0A2E0082307}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View file

@ -1,155 +1,155 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommandLine; using CommandLine;
using CommandLine.Text; using CommandLine.Text;
using SysConsole = System.Console; using SysConsole = System.Console;
namespace Shamir.Console namespace Shamir.Console
{ {
public class CdnOperations : ICommandSet public class CdnOperations : ICommandSet
{ {
[Verb("ls", HelpText = "List files on the CDN")] [Verb("ls", HelpText = "List files on the CDN")]
public class CdnLsOptions public class CdnLsOptions
{ {
} }
[Verb("add", HelpText = "Upload a file to the CDN")] [Verb("add", HelpText = "Upload a file to the CDN")]
public class CdnAddOptions public class CdnAddOptions
{ {
} }
[Verb("mv", HelpText = "Move a file to the CDN")] [Verb("mv", HelpText = "Move a file to the CDN")]
public class CdnMvOptions public class CdnMvOptions
{ {
} }
public static IEnumerable<Type> VerbOptionTypes public static IEnumerable<Type> VerbOptionTypes
{ {
get get
{ {
yield return typeof(CdnLsOptions); yield return typeof(CdnLsOptions);
yield return typeof(CdnAddOptions); yield return typeof(CdnAddOptions);
yield return typeof(CdnMvOptions); yield return typeof(CdnMvOptions);
} }
} }
public ICommand? FindCommand(ReadOnlySpan<string> args) public ICommand? FindCommand(ReadOnlySpan<string> args)
{ {
if (args.Length == 0) return null; if (args.Length == 0) return null;
var parserResult = Parser.Default.ParseArguments<CdnLsOptions, CdnAddOptions, CdnMvOptions>(args.ToArray()); var parserResult = Parser.Default.ParseArguments<CdnLsOptions, CdnAddOptions, CdnMvOptions>(args.ToArray());
return parserResult.MapResult<CdnLsOptions, CdnAddOptions, CdnMvOptions, ICommand>( return parserResult.MapResult<CdnLsOptions, CdnAddOptions, CdnMvOptions, ICommand>(
options => new CdnLsCommand(options), options => new CdnLsCommand(options),
options => new CdnAddCommand(options), options => new CdnAddCommand(options),
options => new CdnMvCommand(options), options => new CdnMvCommand(options),
errors => new HelpTextCommand(parserResult) errors => new HelpTextCommand(parserResult)
); );
} }
public class CdnLsCommand : ICommand public class CdnLsCommand : ICommand
{ {
public CdnLsCommand(CdnLsOptions options) public CdnLsCommand(CdnLsOptions options)
{ {
} }
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
SysConsole.WriteLine($"Executing: cdn-ls"); SysConsole.WriteLine($"Executing: cdn-ls");
return ValueTask.FromResult(0); return ValueTask.FromResult(0);
} }
} }
public class CdnAddCommand : ICommand public class CdnAddCommand : ICommand
{ {
public CdnAddCommand(CdnAddOptions options) public CdnAddCommand(CdnAddOptions options)
{ {
} }
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
SysConsole.WriteLine($"Executing: cdn-add"); SysConsole.WriteLine($"Executing: cdn-add");
return ValueTask.FromResult(0); return ValueTask.FromResult(0);
} }
} }
public class CdnMvCommand : ICommand public class CdnMvCommand : ICommand
{ {
public CdnMvCommand(CdnMvOptions options) public CdnMvCommand(CdnMvOptions options)
{ {
} }
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
SysConsole.WriteLine($"Executing: cdn-mv"); SysConsole.WriteLine($"Executing: cdn-mv");
return ValueTask.FromResult(0); return ValueTask.FromResult(0);
} }
} }
public class HelpTextCommand : ICommand public class HelpTextCommand : ICommand
{ {
public HelpTextCommand(ParserResult<object> result) public HelpTextCommand(ParserResult<object> result)
{ {
this.result = result ?? throw new ArgumentNullException(nameof(result)); this.result = result ?? throw new ArgumentNullException(nameof(result));
Debug.Assert(result.Tag == ParserResultType.NotParsed); Debug.Assert(result.Tag == ParserResultType.NotParsed);
} }
readonly ParserResult<object> result; readonly ParserResult<object> result;
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
var helpText = HelpText.AutoBuild(result); var helpText = HelpText.AutoBuild(result);
SysConsole.Error.WriteLine(helpText); SysConsole.Error.WriteLine(helpText);
return ValueTask.FromResult(1); return ValueTask.FromResult(1);
} }
} }
} }
public interface ICommand public interface ICommand
{ {
ValueTask<int> ExecuteAsync(); ValueTask<int> ExecuteAsync();
} }
public interface ICommandSet public interface ICommandSet
{ {
ICommand? FindCommand(ReadOnlySpan<string> args); ICommand? FindCommand(ReadOnlySpan<string> args);
static IEnumerable<Type> VerbOptionTypes { get; } static IEnumerable<Type> VerbOptionTypes { get; }
} }
public class CommandTree : ICommandSet public class CommandTree : ICommandSet
{ {
IEnumerable<Type> VerbOptionTypes { get; } IEnumerable<Type> VerbOptionTypes { get; }
public ICommand? FindCommand(ReadOnlySpan<string> args) public ICommand? FindCommand(ReadOnlySpan<string> args)
{ {
if (args.Length == 0) return null; if (args.Length == 0) return null;
return args[0] switch { return args[0] switch {
"cdn" => new CdnOperations().FindCommand(args[1..]), "cdn" => new CdnOperations().FindCommand(args[1..]),
_ => null, _ => null,
}; };
} }
} }
class Program class Program
{ {
public static async Task<int> Main(string[] args) public static async Task<int> Main(string[] args)
{ {
var tree = new CommandTree(); var tree = new CommandTree();
var command = tree.FindCommand(args); var command = tree.FindCommand(args);
if (command is null) if (command is null)
{ {
PrintHelpText(tree); PrintHelpText(tree);
return 1; return 1;
} }
return await command.ExecuteAsync(); return await command.ExecuteAsync();
} }
static void PrintHelpText(ICommandSet tree) static void PrintHelpText(ICommandSet tree)
{ {
} }
} }
} }

View file

@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<RootNamespace>Shamir.Console</RootNamespace> <RootNamespace>Shamir.Console</RootNamespace>
<OutputPath>..\..\bin</OutputPath> <OutputPath>..\..\bin</OutputPath>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,228 +1,228 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
namespace Shamir.Console.Tests namespace Shamir.Console.Tests
{ {
public class CommandTreeTests public class CommandTreeTests
{ {
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
tree = new DefaultCommandTree( tree = new DefaultCommandTree(
"root", "root",
"top level", "top level",
ImmutableArray.Create<ICommandTree>( ImmutableArray.Create<ICommandTree>(
new DefaultCommandTree( new DefaultCommandTree(
"bar", "bar",
"child command tree", "child command tree",
ImmutableArray<ICommandTree>.Empty, ImmutableArray<ICommandTree>.Empty,
ImmutableArray.Create<ICommand>( ImmutableArray.Create<ICommand>(
new SimpleTextCommand("one", "bar/1 command"), new SimpleTextCommand("one", "bar/1 command"),
new SimpleTextCommand("two", "bar/2 command") new SimpleTextCommand("two", "bar/2 command")
))), ))),
ImmutableArray.Create<ICommand>( ImmutableArray.Create<ICommand>(
new SimpleTextCommand("foo", "foo command") new SimpleTextCommand("foo", "foo command")
)); ));
} }
ICommandTree tree; ICommandTree tree;
[Test] [Test]
public void ConstructsHelpTextWithNoArgs() public void ConstructsHelpTextWithNoArgs()
{ {
var command = tree.FindCommand(Array.Empty<string>()); var command = tree.FindCommand(Array.Empty<string>());
Assert.That(command, Is.InstanceOf<DefaultHelpTextCommand>()); Assert.That(command, Is.InstanceOf<DefaultHelpTextCommand>());
var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); var helpText = ((DefaultHelpTextCommand)command).GetHelpText();
Assert.That(helpText, Is.EqualTo(@" Assert.That(helpText, Is.EqualTo(@"
Group Group
root : top level root : top level
Subgroups: Subgroups:
bar : child command tree bar : child command tree
Commands: Commands:
foo : foo command foo : foo command
".TrimStart())); ".TrimStart()));
} }
[Test] [Test]
public void ConstructsHelpTextForSubTree() public void ConstructsHelpTextForSubTree()
{ {
var command = tree.FindCommand(new[] { "bar" }); var command = tree.FindCommand(new[] { "bar" });
Assert.That(command, Is.InstanceOf<DefaultHelpTextCommand>()); Assert.That(command, Is.InstanceOf<DefaultHelpTextCommand>());
var helpText = ((DefaultHelpTextCommand)command).GetHelpText(); var helpText = ((DefaultHelpTextCommand)command).GetHelpText();
Assert.That(helpText, Is.EqualTo(@" Assert.That(helpText, Is.EqualTo(@"
Group Group
root bar : child command tree root bar : child command tree
Commands: Commands:
one : bar/1 command one : bar/1 command
two : bar/1 command two : bar/1 command
")); "));
} }
[TestCase((object)new string[] { "foo" }, ExpectedResult = "foo command")] [TestCase((object)new string[] { "foo" }, ExpectedResult = "foo command")]
[TestCase((object)new string[] { "bar", "one" }, ExpectedResult = "bar/1 command")] [TestCase((object)new string[] { "bar", "one" }, ExpectedResult = "bar/1 command")]
[TestCase((object)new string[] { "bar", "two" }, ExpectedResult = "bar/2 command")] [TestCase((object)new string[] { "bar", "two" }, ExpectedResult = "bar/2 command")]
public string FindsCommand(string[] args) public string FindsCommand(string[] args)
{ {
var command = tree.FindCommand(args); var command = tree.FindCommand(args);
return command?.Description; return command?.Description;
} }
class SimpleTextCommand : ICommand class SimpleTextCommand : ICommand
{ {
public SimpleTextCommand(string name, string description) public SimpleTextCommand(string name, string description)
{ {
Name = name ?? throw new ArgumentNullException(nameof(name)); Name = name ?? throw new ArgumentNullException(nameof(name));
Description = description ?? throw new ArgumentNullException(nameof(description)); Description = description ?? throw new ArgumentNullException(nameof(description));
} }
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
return ValueTask.FromResult(0); return ValueTask.FromResult(0);
} }
} }
} }
interface IOperationsNode interface IOperationsNode
{ {
string Name { get; } string Name { get; }
string Description { get; } string Description { get; }
} }
interface ICommand : IOperationsNode interface ICommand : IOperationsNode
{ {
ValueTask<int> ExecuteAsync(); ValueTask<int> ExecuteAsync();
} }
interface ICommandTree : IOperationsNode interface ICommandTree : IOperationsNode
{ {
ImmutableArray<ICommandTree> SubTrees { get; } ImmutableArray<ICommandTree> SubTrees { get; }
ImmutableArray<ICommand> Commands { get; } ImmutableArray<ICommand> Commands { get; }
ICommand FindCommand(ReadOnlySpan<string> args); ICommand FindCommand(ReadOnlySpan<string> args);
} }
class DefaultCommandTree : ICommandTree class DefaultCommandTree : ICommandTree
{ {
public DefaultCommandTree(string name, string description, ImmutableArray<ICommandTree> subtrees, ImmutableArray<ICommand> commands) public DefaultCommandTree(string name, string description, ImmutableArray<ICommandTree> subtrees, ImmutableArray<ICommand> commands)
{ {
this.Name = name ?? throw new ArgumentNullException(nameof(name)); this.Name = name ?? throw new ArgumentNullException(nameof(name));
this.Description = description ?? throw new ArgumentNullException(nameof(description)); this.Description = description ?? throw new ArgumentNullException(nameof(description));
this.SubTrees = subtrees; this.SubTrees = subtrees;
this.Commands = commands; this.Commands = commands;
} }
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }
public ImmutableArray<ICommandTree> SubTrees { get; } public ImmutableArray<ICommandTree> SubTrees { get; }
public ImmutableArray<ICommand> Commands { get; } public ImmutableArray<ICommand> Commands { get; }
public ICommand FindCommand(ReadOnlySpan<string> args) public ICommand FindCommand(ReadOnlySpan<string> args)
{ {
if (args.Length > 0) if (args.Length > 0)
{ {
foreach (var command in Commands) foreach (var command in Commands)
{ {
if (command.Name == args[0]) if (command.Name == args[0])
{ {
return command; return command;
} }
} }
foreach (var tree in SubTrees) foreach (var tree in SubTrees)
{ {
if (tree.Name == args[0]) if (tree.Name == args[0])
{ {
return tree.FindCommand(args[1..]); return tree.FindCommand(args[1..]);
} }
} }
} }
return new DefaultHelpTextCommand(this); return new DefaultHelpTextCommand(this);
} }
} }
class DefaultHelpTextCommand : ICommand class DefaultHelpTextCommand : ICommand
{ {
public DefaultHelpTextCommand(ICommandTree tree) public DefaultHelpTextCommand(ICommandTree tree)
{ {
this.CommandTree = tree ?? throw new ArgumentNullException(nameof(tree)); this.CommandTree = tree ?? throw new ArgumentNullException(nameof(tree));
} }
public string Name => "help"; public string Name => "help";
public string Description => "Print help text"; public string Description => "Print help text";
public ICommandTree CommandTree { get; } public ICommandTree CommandTree { get; }
public ValueTask<int> ExecuteAsync() public ValueTask<int> ExecuteAsync()
{ {
System.Console.Error.WriteLine(GetHelpText()); System.Console.Error.WriteLine(GetHelpText());
return ValueTask.FromResult(1); // TODO: const return ValueTask.FromResult(1); // TODO: const
} }
public string GetHelpText() => CommandTree.BuildHelpText(); public string GetHelpText() => CommandTree.BuildHelpText();
} }
static class ICommandTreeExtensions static class ICommandTreeExtensions
{ {
public static string BuildHelpText(this ICommandTree tree) public static string BuildHelpText(this ICommandTree tree)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("Group"); sb.AppendLine("Group");
sb.Append(" "); sb.Append(" ");
sb.AppendLine(tree.Name); sb.AppendLine(tree.Name);
sb.AppendLine(); sb.AppendLine();
if (!tree.SubTrees.IsEmpty) if (!tree.SubTrees.IsEmpty)
{ {
sb.AppendLine("Subgroups:"); sb.AppendLine("Subgroups:");
var maxSpacing = tree.SubTrees.Max(c => c.Name.Length) + 1; var maxSpacing = tree.SubTrees.Max(c => c.Name.Length) + 1;
foreach (var child in tree.SubTrees) foreach (var child in tree.SubTrees)
{ {
sb.Append(" "); sb.Append(" ");
sb.Append(child.Name); sb.Append(child.Name);
for (var i = 0; i < maxSpacing; i++) for (var i = 0; i < maxSpacing; i++)
{ {
sb.Append(' '); sb.Append(' ');
} }
sb.Append(": "); sb.Append(": ");
sb.AppendLine(child.Description); sb.AppendLine(child.Description);
} }
} }
if (!tree.Commands.IsEmpty) if (!tree.Commands.IsEmpty)
{ {
sb.AppendLine("Commands:"); sb.AppendLine("Commands:");
var maxSpacing = tree.Commands.Max(c => c.Name.Length) + 1; var maxSpacing = tree.Commands.Max(c => c.Name.Length) + 1;
foreach (var command in tree.Commands) foreach (var command in tree.Commands)
{ {
sb.Append(" "); sb.Append(" ");
sb.Append(command.Name); sb.Append(command.Name);
for (var i = 0; i < maxSpacing; i++) for (var i = 0; i < maxSpacing; i++)
{ {
sb.Append(' '); sb.Append(' ');
} }
sb.Append(": "); sb.Append(": ");
sb.AppendLine(command.Description); sb.AppendLine(command.Description);
} }
} }
return sb.ToString(); return sb.ToString();
} }
} }
} }

View file

@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<RootNamespace>Shamir.Console.Tests</RootNamespace> <RootNamespace>Shamir.Console.Tests</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\console\shamir.csproj" /> <ProjectReference Include="..\..\src\console\shamir.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>