This commit is contained in:
Alexey Golub
2019-07-17 22:49:46 +03:00
parent 3611aa51e6
commit 4ba9413012
13 changed files with 88 additions and 158 deletions

View File

@@ -1,33 +1,36 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using CliFx.Attributes;
using CliFx.Models;
using CliFx.Services; using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework; using NUnit.Framework;
namespace CliFx.Tests namespace CliFx.Tests
{ {
public partial class CliApplicationTests
{
[DefaultCommand]
public class TestCommand : Command
{
public override ExitCode Execute() => new ExitCode(13);
}
}
[TestFixture] [TestFixture]
public class CliApplicationTests public partial class CliApplicationTests
{ {
[Test] [Test]
public async Task RunAsync_Test() public async Task RunAsync_Test()
{ {
// Arrange // Arrange
var command = new TestCommand(); var application = new CliApplication(
var expectedExitCode = await command.ExecuteAsync(); new CommandOptionParser(),
new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter()));
var commandOptionParser = new CommandOptionParser();
var typeProvider = new TypeProvider(typeof(TestCommand));
var commandOptionConverter = new CommandOptionConverter();
var commandResolver = new CommandResolver(typeProvider, commandOptionConverter);
var application = new CliApplication(commandOptionParser, commandResolver);
// Act // Act
var exitCodeValue = await application.RunAsync(); var exitCodeValue = await application.RunAsync();
// Assert // Assert
Assert.That(exitCodeValue, Is.EqualTo(expectedExitCode.Value), "Exit code"); Assert.That(exitCodeValue, Is.EqualTo(13), "Exit code");
} }
} }
} }

View File

@@ -3,13 +3,44 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using CliFx.Models; using CliFx.Models;
using CliFx.Services; using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework; using NUnit.Framework;
namespace CliFx.Tests namespace CliFx.Tests
{ {
public partial class CommandOptionConverterTests
{
public enum TestEnum
{
Value1,
Value2,
Value3
}
public struct TestStringConstructable
{
public string Value { get; }
public TestStringConstructable(string value)
{
Value = value;
}
}
public struct TestStringParseable
{
public string Value { get; }
private TestStringParseable(string value)
{
Value = value;
}
public static TestStringParseable Parse(string value) => new TestStringParseable(value);
}
}
[TestFixture] [TestFixture]
public class CommandOptionConverterTests public partial class CommandOptionConverterTests
{ {
private static IEnumerable<TestCaseData> GetTestCases_ConvertOption() private static IEnumerable<TestCaseData> GetTestCases_ConvertOption()
{ {

View File

@@ -1,14 +1,28 @@
using System.Collections.Generic; using System.Collections.Generic;
using CliFx.Attributes;
using CliFx.Exceptions; using CliFx.Exceptions;
using CliFx.Models; using CliFx.Models;
using CliFx.Services; using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework; using NUnit.Framework;
namespace CliFx.Tests namespace CliFx.Tests
{ {
public partial class CommandResolverTests
{
[DefaultCommand]
public class TestCommand : Command
{
[CommandOption("int", 'i', IsRequired = true)]
public int IntOption { get; set; } = 24;
[CommandOption("str", 's')] public string StringOption { get; set; } = "foo bar";
public override ExitCode Execute() => new ExitCode(IntOption, StringOption);
}
}
[TestFixture] [TestFixture]
public class CommandResolverTests public partial class CommandResolverTests
{ {
private static IEnumerable<TestCaseData> GetTestCases_ResolveCommand() private static IEnumerable<TestCaseData> GetTestCases_ResolveCommand()
{ {
@@ -36,14 +50,6 @@ namespace CliFx.Tests
}), }),
new TestCommand {IntOption = 13} new TestCommand {IntOption = 13}
); );
yield return new TestCaseData(
new CommandOptionSet("command", new[]
{
new CommandOption("int", "13")
}),
new TestCommand {IntOption = 13}
);
} }
[Test] [Test]
@@ -51,10 +57,7 @@ namespace CliFx.Tests
public void ResolveCommand_Test(CommandOptionSet commandOptionSet, TestCommand expectedCommand) public void ResolveCommand_Test(CommandOptionSet commandOptionSet, TestCommand expectedCommand)
{ {
// Arrange // Arrange
var typeProvider = new TypeProvider(typeof(TestCommand)); var resolver = new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter());
var optionConverter = new CommandOptionConverter();
var resolver = new CommandResolver(typeProvider, optionConverter);
// Act // Act
var command = resolver.ResolveCommand(commandOptionSet) as TestCommand; var command = resolver.ResolveCommand(commandOptionSet) as TestCommand;
@@ -85,10 +88,7 @@ namespace CliFx.Tests
public void ResolveCommand_IsRequired_Test(CommandOptionSet commandOptionSet) public void ResolveCommand_IsRequired_Test(CommandOptionSet commandOptionSet)
{ {
// Arrange // Arrange
var typeProvider = new TypeProvider(typeof(TestCommand)); var resolver = new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter());
var optionConverter = new CommandOptionConverter();
var resolver = new CommandResolver(typeProvider, optionConverter);
// Act & Assert // Act & Assert
Assert.Throws<CommandResolveException>(() => resolver.ResolveCommand(commandOptionSet)); Assert.Throws<CommandResolveException>(() => resolver.ResolveCommand(commandOptionSet));

View File

@@ -1,18 +0,0 @@
using CliFx.Attributes;
using CliFx.Models;
namespace CliFx.Tests.TestObjects
{
[DefaultCommand]
[Command("command")]
public class TestCommand : Command
{
[CommandOption("int", 'i', IsRequired = true)]
public int IntOption { get; set; } = 24;
[CommandOption("str", 's')]
public string StringOption { get; set; } = "foo bar";
public override ExitCode Execute() => new ExitCode(IntOption, StringOption);
}
}

View File

@@ -1,9 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public enum TestEnum
{
Value1,
Value2,
Value3
}
}

View File

@@ -1,12 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public struct TestStringConstructable
{
public string Value { get; }
public TestStringConstructable(string value)
{
Value = value;
}
}
}

View File

@@ -1,17 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public partial struct TestStringParseable
{
public string Value { get; }
private TestStringParseable(string value)
{
Value = value;
}
}
public partial struct TestStringParseable
{
public static TestStringParseable Parse(string value) => new TestStringParseable(value);
}
}

View File

@@ -1,11 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using CliFx.Services; using CliFx.Services;
namespace CliFx namespace CliFx
{ {
public partial class CliApplication : ICliApplication public class CliApplication : ICliApplication
{ {
private readonly ICommandOptionParser _commandOptionParser; private readonly ICommandOptionParser _commandOptionParser;
private readonly ICommandResolver _commandResolver; private readonly ICommandResolver _commandResolver;
@@ -17,7 +16,7 @@ namespace CliFx
} }
public CliApplication() public CliApplication()
: this(new CommandOptionParser(), GetDefaultCommandResolver(Assembly.GetCallingAssembly())) : this(new CommandOptionParser(), new CommandResolver(new CommandOptionConverter()))
{ {
} }
@@ -31,15 +30,4 @@ namespace CliFx
return exitCode.Value; return exitCode.Value;
} }
} }
public partial class CliApplication
{
private static ICommandResolver GetDefaultCommandResolver(Assembly assembly)
{
var typeProvider = TypeProvider.FromAssembly(assembly);
var commandOptionConverter = new CommandOptionConverter();
return new CommandResolver(typeProvider, commandOptionConverter);
}
}
} }

View File

@@ -1,12 +1,9 @@
using System; using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;
namespace CliFx namespace CliFx
{ {
public static class Extensions public static class Extensions
{ {
public static Task<int> RunAsync(this ICliApplication application) => public static Task<int> RunAsync(this ICliApplication application) => application.RunAsync(new string[0]);
application.RunAsync(Environment.GetCommandLineArgs().Skip(1).ToArray());
} }
} }

View File

@@ -36,7 +36,7 @@ namespace CliFx.Internal
// Derives from Command // Derives from Command
type.IsDerivedFrom(typeof(Command)) && type.IsDerivedFrom(typeof(Command)) &&
// Marked with DefaultCommandAttribute or CommandAttribute // Marked with DefaultCommandAttribute or CommandAttribute
(type.IsDefined(typeof(DefaultCommandAttribute)) || type.IsDefined(typeof(CommandAttribute))); type.IsDefined(typeof(CommandAttribute));
public static CommandType Initialize(Type type) public static CommandType Initialize(Type type)
{ {

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Exceptions; using CliFx.Exceptions;
using CliFx.Internal; using CliFx.Internal;
@@ -8,18 +9,23 @@ using CliFx.Models;
namespace CliFx.Services namespace CliFx.Services
{ {
public class CommandResolver : ICommandResolver public partial class CommandResolver : ICommandResolver
{ {
private readonly ITypeProvider _typeProvider; private readonly IReadOnlyList<Type> _availableTypes;
private readonly ICommandOptionConverter _commandOptionConverter; private readonly ICommandOptionConverter _commandOptionConverter;
public CommandResolver(ITypeProvider typeProvider, ICommandOptionConverter commandOptionConverter) public CommandResolver(IReadOnlyList<Type> availableTypes, ICommandOptionConverter commandOptionConverter)
{ {
_typeProvider = typeProvider; _availableTypes = availableTypes;
_commandOptionConverter = commandOptionConverter; _commandOptionConverter = commandOptionConverter;
} }
private IEnumerable<CommandType> GetCommandTypes() => CommandType.GetCommandTypes(_typeProvider.GetTypes()); public CommandResolver(ICommandOptionConverter commandOptionConverter)
: this(GetDefaultAvailableTypes(), commandOptionConverter)
{
}
private IEnumerable<CommandType> GetCommandTypes() => CommandType.GetCommandTypes(_availableTypes);
private CommandType GetDefaultCommandType() private CommandType GetDefaultCommandType()
{ {
@@ -100,4 +106,9 @@ namespace CliFx.Services
return command; return command;
} }
} }
public partial class CommandResolver
{
private static IReadOnlyList<Type> GetDefaultAvailableTypes() => Assembly.GetEntryAssembly()?.GetExportedTypes() ?? new Type[0];
}
} }

View File

@@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
namespace CliFx.Services
{
public interface ITypeProvider
{
IReadOnlyList<Type> GetTypes();
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace CliFx.Services
{
public partial class TypeProvider : ITypeProvider
{
private readonly IReadOnlyList<Type> _types;
public TypeProvider(IReadOnlyList<Type> types)
{
_types = types;
}
public TypeProvider(params Type[] types)
: this((IReadOnlyList<Type>) types)
{
}
public IReadOnlyList<Type> GetTypes() => _types;
}
public partial class TypeProvider
{
public static TypeProvider FromAssembly(Assembly assembly) => new TypeProvider(assembly.GetExportedTypes());
public static TypeProvider FromAssemblies(IReadOnlyList<Assembly> assemblies) =>
new TypeProvider(assemblies.SelectMany(a => a.ExportedTypes).ToArray());
public static TypeProvider FromAssemblies(params Assembly[] assemblies) => FromAssemblies((IReadOnlyList<Assembly>) assemblies);
}
}