using System;
using System.IO;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace MfGames.ToolBuilder
{
///
/// A builder pattern for creating the tool. This wraps much of the hosting
/// infrastructure with some opinionated decisions and reduces the amount of
/// boilerplate needed to configure the tool.
///
public class ToolBuilder
{
private readonly string[] arguments;
private readonly ConfigToolService configService;
private readonly IHostBuilder hostBuilder;
private readonly LoggingToolService loggingService;
public ToolBuilder(
string applicationName,
string internalName,
string[] arguments)
{
// Create our various services.
this.arguments = arguments;
this.ApplicationName = applicationName;
this.InternalName = internalName;
this.configService = new ConfigToolService()
.WithInternalName(this.InternalName);
this.loggingService = new LoggingToolService();
// Set up logging first so we can report the loading process. This
// sets up the Serilog.Log.Logger which means we can use that for
// everything beyond this point.
this.loggingService.Configure(arguments);
// Start up the basic configuration.
this.hostBuilder = Host
.CreateDefaultBuilder(arguments)
.UseConsoleLifetime()
.ConfigureAppConfiguration(this.ConfigureAppConfiguration)
.UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureServices(this.ConfigureServices)
.ConfigureContainer(this.ConfigureContainer);
}
///
/// Gets the human-readable name of the application.
///
public string ApplicationName { get; }
///
/// Gets the internal name of the application.
///
public string InternalName { get; }
public static ToolBuilder Create(
string applicationName,
string internalName,
string[] arguments)
{
return new ToolBuilder(applicationName, internalName, arguments);
}
public ToolBuilder ConfigureContainer(
Action configure)
{
this.hostBuilder.ConfigureContainer(configure);
return this;
}
public ToolBuilder ConfigureServices(
Action configure)
{
this.hostBuilder.ConfigureServices(configure);
return this;
}
///
/// Finishes building the tool, parses the arguments, and runs the
/// command.
///
/// An error code, 0 for successful, otherwise false.
public async Task RunAsync()
{
try
{
await this.hostBuilder
.RunConsoleAsync()
.ConfigureAwait(false);
}
catch (Exception exception)
{
Log.Fatal(
exception,
"There was a problem running the command: {Arguments}",
this.arguments);
return Environment.ExitCode == 0 ? 1 : Environment.ExitCode;
}
// Get the exit code and return it.
return Environment.ExitCode;
}
private void ConfigureAppConfiguration(
HostBuilderContext context,
IConfigurationBuilder builder)
{
builder.SetBasePath(Directory.GetCurrentDirectory());
this.configService.Configure(builder, this.arguments);
}
private void ConfigureContainer(
HostBuilderContext context,
ContainerBuilder builder)
{
// We want to get logging up and running as soon as possible. We
// also hook up the logging to the process exit in an attempt to
// make sure the logger is properly flushed before exiting.
builder.RegisterInstance(Log.Logger).As().SingleInstance();
AppDomain.CurrentDomain.ProcessExit +=
(_, _) => Log.CloseAndFlush();
// Register the components required to make the CLI work.
builder.RegisterModule();
}
private void ConfigureServices(
HostBuilderContext context,
IServiceCollection services)
{
services.AddAutofac();
services.AddHostedService();
}
}
}