using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Zio;
using Zio.FileSystems;
namespace Nitride
{
///
/// A class that implements a builder pattern for gathering all the
/// components of a static website. Once everything is build, then calling
/// `Build()` will generate the resulting website.
///
public class NitrideBuilder
{
private readonly string[] arguments;
private readonly List>
configureContainerCallbacks;
///
/// An event that is called after the container is built but before
/// the application runs.
///
private readonly List>
configureSiteCallbacks;
private NitrideLoggingBuilder loggingBuilder;
public NitrideBuilder(string[] arguments)
{
this.arguments = arguments;
this.loggingBuilder = new NitrideLoggingBuilder();
this.configureSiteCallbacks =
new List>();
this.configureContainerCallbacks =
new List>();
}
///
/// Gets or sets the builder used to set up logging.
///
///
public NitrideLoggingBuilder LoggingBuilder
{
get => this.loggingBuilder;
set => this.loggingBuilder =
value ?? throw new ArgumentNullException(nameof(value));
}
///
/// Gets or sets the input directory to automatically register as the
/// source of the files. If this is not set, then no Zio.IFileSystem
/// will be registered and it will have to be done manually.
///
public DirectoryInfo? RootDirectory { get; set; }
///
/// Generates or builds the resulting website based on the given
/// command.
///
/// The task once completed.
public async Task BuildAsync()
{
await Host
.CreateDefaultBuilder(this.arguments)
.UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureServices(this.ConfigureServices)
.ConfigureContainer(this.ConfigureContainer)
.RunConsoleAsync();
return 0;
}
///
/// Allows for configuration of the Autofac container to register
/// additional types, pipelines, and modules.
///
/// The callback to configure the container.
/// The builder to chain operations.
public NitrideBuilder ConfigureContainer(
Action callback)
{
this.configureContainerCallbacks.Add(callback);
return this;
}
///
/// Overrides the logging configuration for customization.
///
///
/// The builder to chain methods.
public NitrideBuilder ConfigureLogging(
Func callback)
{
this.loggingBuilder.Callback = callback;
return this;
}
///
/// Registers a callback to be called after the container is built but
/// before the application runs.
///
/// The callback to register.
/// The builder for chaining.
public NitrideBuilder ConfigureSite(
Action callback)
{
this.configureSiteCallbacks.Add(callback);
return this;
}
///
/// Sets the root directory to a common value by creating a
/// IFileSystem (from Zio) of the root directory and registering it.
/// This will be used for both input and output.
///
///
/// The path to the directory that represents "/" while
/// building.
///
/// The builder to chain calls.
public NitrideBuilder WithRootDirectory(DirectoryInfo directory)
{
this.RootDirectory = directory;
return this;
}
private void ConfigureContainer(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.
ILogger logger = this.loggingBuilder
.Setup(builder)
.ForContext();
AppDomain.CurrentDomain.ProcessExit +=
(_, _) => Log.CloseAndFlush();
// Hook up the rest of the modules.
builder.RegisterModule();
// Set up our file system.
this.RegisterRootDirectory(logger, builder);
// Finish up the registration by running our events.
foreach (var callback in this.configureSiteCallbacks)
{
builder.RegisterBuildCallback(scope => callback(this, scope));
}
foreach (var configureContainer in this.configureContainerCallbacks)
{
configureContainer.Invoke(builder);
}
}
private void ConfigureServices(IServiceCollection services)
{
services.AddAutofac();
services.AddHostedService();
}
private void RegisterRootDirectory(
ILogger logger,
ContainerBuilder builder)
{
if (this.RootDirectory == null)
{
logger.Verbose("No root directory is registered");
return;
}
logger.Debug(
"Setting root directory to {Path}",
this.RootDirectory.FullName);
var rootFileSystem = new PhysicalFileSystem();
var subFileSystem = new SubFileSystem(
rootFileSystem,
this.RootDirectory.FullName);
builder.RegisterInstance(subFileSystem)
.As()
.SingleInstance();
}
}
}