Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Microsoft.Extensions.Hosting;
using Spectre.Console.Cli;

namespace Spectre.Console.Extensions.Hosting.Infrastructure;

internal class HostEnabledCommandApp : IHostEnabledCommandApp
{
private readonly TypeRegistrar _typeRegistrar;
private readonly CommandApp _underlyingCommandApp;

public HostEnabledCommandApp(TypeRegistrar typeRegistrar)
{
_typeRegistrar = typeRegistrar;
_underlyingCommandApp = new CommandApp(typeRegistrar);
}

public void Configure(Action<IConfigurator> configuration)
{
_underlyingCommandApp.Configure(configuration);
}

public int Run(IEnumerable<string> args)
{
return _underlyingCommandApp.Run(args);
}

public async Task<int> RunAsync(IEnumerable<string> args)
{
return await _underlyingCommandApp.RunAsync(args);
}

public void SetHost(IHost host)
{
_typeRegistrar.SetHost(host);
}
}

internal class HostEnabledCommandApp<TDefaultCommand> : IHostEnabledCommandApp
where TDefaultCommand: class, ICommand
{
private readonly TypeRegistrar _typeRegistrar;
private readonly CommandApp<TDefaultCommand> _underlyingCommandApp;

public HostEnabledCommandApp(TypeRegistrar typeRegistrar)
{
_typeRegistrar = typeRegistrar;
_underlyingCommandApp = new CommandApp<TDefaultCommand>(typeRegistrar);
}

public void Configure(Action<IConfigurator> configuration)
{
_underlyingCommandApp.Configure(configuration);
}

public int Run(IEnumerable<string> args)
{
return _underlyingCommandApp.Run(args);
}

public async Task<int> RunAsync(IEnumerable<string> args)
{
return await _underlyingCommandApp.RunAsync(args);
}

public void SetHost(IHost host)
{
_typeRegistrar.SetHost(host);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.Extensions.Hosting;
using Spectre.Console.Cli;

namespace Spectre.Console.Extensions.Hosting.Infrastructure;

public interface IHostEnabledCommandApp : ICommandApp
{
void SetHost(IHost host);
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Spectre.Console.Cli;

namespace Spectre.Console.Extensions.Hosting.Infrastructure;

public sealed class TypeRegistrar : ITypeRegistrar
{
private readonly IServiceCollection _builder;
private readonly IServiceCollection _builder = new ServiceCollection();
private IServiceCollection _hostServiceCollection = null!;
private IHost? _host;

public TypeRegistrar(IServiceCollection builder)
public TypeRegistrar(IHostBuilder builder)
{
_builder = builder;
builder.ConfigureServices((_, serviceCollection) =>
{
_hostServiceCollection = serviceCollection;
});
}

public void SetHost(IHost host)
{
_host = host;
}

public ITypeResolver Build()
{
if (_host == null)
{
throw new NotSupportedException("SetHost must be called before the Resolver can be accessed.");
}

// copy all registrations from the host ServiceCollection to our internal ServiceCollection,
// so we have them all available, but do not modify the host ServiceCollection ourselves.
foreach (var serviceDescriptor in _hostServiceCollection)
{
var type = serviceDescriptor.ServiceType;
if (serviceDescriptor.ImplementationType != null)
{
_builder.AddSingleton(type, serviceDescriptor.ImplementationType);
continue;
}

_builder.AddSingleton(type, _ => _host.Services.GetService(type)!);
}

return new TypeResolver(_builder.BuildServiceProvider());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ public static class SpectreConsoleHostBuilderExtensions
public static IHostBuilder UseSpectreConsole(this IHostBuilder builder, Action<IConfigurator> configureCommandApp)
{
builder = builder ?? throw new ArgumentNullException(nameof(builder));
var command = new HostEnabledCommandApp(new TypeRegistrar(builder));
command.Configure(configureCommandApp);

builder.ConfigureServices((_, collection) =>
{
var command = new CommandApp(new TypeRegistrar(collection));
command.Configure(configureCommandApp);
collection.AddSingleton<ICommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
}
);
{
collection.AddSingleton<IHostEnabledCommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
});

return builder;
}
Expand All @@ -48,18 +47,17 @@ public static IHostBuilder UseSpectreConsole<TDefaultCommand>(this IHostBuilder
{
builder = builder ?? throw new ArgumentNullException(nameof(builder));

builder.ConfigureServices((_, collection) =>
{
var command = new CommandApp<TDefaultCommand>(new TypeRegistrar(collection));
if (configureCommandApp != null)
{
command.Configure(configureCommandApp);
}
var command = new HostEnabledCommandApp<TDefaultCommand>(new TypeRegistrar(builder));
if (configureCommandApp != null)
{
command.Configure(configureCommandApp);
}

collection.AddSingleton<ICommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
}
);
builder.ConfigureServices((_, collection) =>
{
collection.AddSingleton<IHostEnabledCommandApp>(command);
collection.AddHostedService<SpectreConsoleWorker>();
});

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Spectre.Console.Cli;
using Spectre.Console.Extensions.Hosting.Infrastructure;

namespace Spectre.Console.Extensions.Hosting.Worker;

public class SpectreConsoleWorker : IHostedService
{
private readonly ICommandApp _commandApp;
private readonly IHostEnabledCommandApp _commandApp;
private readonly IHostApplicationLifetime _hostLifetime;
private readonly IHost _host;
private readonly ILogger<SpectreConsoleWorker> _logger;
private int _exitCode;

public SpectreConsoleWorker(ILogger<SpectreConsoleWorker> logger, ICommandApp commandApp,
IHostApplicationLifetime hostLifetime)
public SpectreConsoleWorker(
ILogger<SpectreConsoleWorker> logger,
IHostEnabledCommandApp commandApp,
IHostApplicationLifetime hostLifetime,
IHost host)
{
_logger = logger;
_commandApp = commandApp;
_hostLifetime = hostLifetime;
_host = host;
}

public Task StartAsync(CancellationToken cancellationToken)
Expand All @@ -26,6 +31,7 @@ public Task StartAsync(CancellationToken cancellationToken)
try
{
var args = GetArgs();
_commandApp.SetHost(_host);
await Task.Delay(100, cancellationToken); //Just to let Microsoft.Hosting finish.
_exitCode = await _commandApp.RunAsync(args);
}
Expand Down