This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-toolbuilder-cil/src/MfGames.ToolBuilder/ToolBuilder.cs
Dylan R. E. Moonfire ad0525be04 feat: initial commit
2021-09-10 12:33:42 -05:00

152 lines
4.9 KiB
C#

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
{
/// <summary>
/// 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.
/// </summary>
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<ContainerBuilder>(this.ConfigureContainer);
}
/// <summary>
/// Gets the human-readable name of the application.
/// </summary>
public string ApplicationName { get; }
/// <summary>
/// Gets the internal name of the application.
/// </summary>
public string InternalName { get; }
public static ToolBuilder Create(
string applicationName,
string internalName,
string[] arguments)
{
return new ToolBuilder(applicationName, internalName, arguments);
}
public ToolBuilder ConfigureContainer(
Action<ContainerBuilder> configure)
{
this.hostBuilder.ConfigureContainer(configure);
return this;
}
public ToolBuilder ConfigureServices(
Action<IServiceCollection> configure)
{
this.hostBuilder.ConfigureServices(configure);
return this;
}
/// <summary>
/// Finishes building the tool, parses the arguments, and runs the
/// command.
/// </summary>
/// <returns>An error code, 0 for successful, otherwise false.</returns>
public async Task<int> 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<ILogger>().SingleInstance();
AppDomain.CurrentDomain.ProcessExit +=
(_, _) => Log.CloseAndFlush();
// Register the components required to make the CLI work.
builder.RegisterModule<ToolBuilderModule>();
}
private void ConfigureServices(
HostBuilderContext context,
IServiceCollection services)
{
services.AddAutofac();
services.AddHostedService<ToolService>();
}
}
}