using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading.Tasks; using Autofac; using MfGames.ToolBuilder; using Serilog; using Zio; using Zio.FileSystems; using Module = Autofac.Module; namespace MfGames.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 readonly NitrideModule nitrideModule; public NitrideBuilder(string[] arguments) { this.arguments = arguments; this.configureSiteCallbacks = new List>(); this.configureContainerCallbacks = new List>(); this.nitrideModule = new NitrideModule(); this.nitrideModule.ApplicationName = Assembly.GetExecutingAssembly() .GetName() .Name!; } /// /// 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; } /// /// 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; } /// /// 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; } /// /// Generates or builds the resulting website based on the given /// command. /// /// The task once completed. public async Task RunAsync() { if (this.nitrideModule.ApplicationName == null) { throw new InvalidOperationException( "Application name must be set, such as with the NitrideBuilder.WithApplicationName() to properly run."); } return await ToolBoxBuilder .Create(this.nitrideModule.ApplicationName, this.arguments) .ConfigureContainer(this.ConfigureContainer) .Build() .RunAsync(); } /// /// Initialize the builder with a given Autofac module. /// /// The type of module to use. /// The builder to chain operations. public NitrideBuilder UseModule() where TModule : Module, new() { this.ConfigureContainer(x => x.RegisterModule()); return this; } /// /// Initialize the builder with a given Autofac module. /// /// The type of module to use. /// The builder to chain operations. public NitrideBuilder UseModule(Module module) { this.ConfigureContainer(x => x.RegisterModule(module)); return this; } /// /// Sets the description of the builder. /// public NitrideBuilder WithApplicationDescription(string value) { this.nitrideModule.Description = value; return this; } /// /// Sets the name of the application, which is displayed in the help screen. /// public NitrideBuilder WithApplicationName(string value) { this.nitrideModule.ApplicationName = value; 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) { // Hook up the rest of the modules. builder.RegisterModule(this.nitrideModule); // Set up our file system. // TODO Wrong logger this.RegisterRootDirectory(Log.Logger, builder); // Finish up the registration by running our events. foreach (Action? callback in this .configureSiteCallbacks) { builder.RegisterBuildCallback(scope => callback(this, scope)); } foreach (Action? configureContainer in this .configureContainerCallbacks) { configureContainer.Invoke(builder); } } 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(); } }